Access violation in Thread with TIdHTTP - multithreading

An access violation occurs after the stream is terminated, but idHTTP continues to fulfill the request.
Here the constructor and destructor of the thread:
constructor TTelegramListener.Create(Asyspended: Boolean);
begin
FFlag := False;
FreeOnTerminate := True;
inherited Create(Asyspended);
end;
destructor TTelegramListener.Destroy;
begin
FCallback := nil;
inherited;
end;
Here is the call and creation of the thread object:
procedure TTeleBot.StartListenMessages(CallProc: TCallbackProc);
begin
if Assigned(FMessageListener) then
FMessageListener.DoTerminate;
FMessageListener := TTelegramListener.Create(False);
FMessageListener.Priority := tpLowest;
FMessageListener.FreeOnTerminate := True;
FMessageListener.Callback := CallProc;
FMessageListener.TelegramToken := FTelegramToken;
end;
This is where the thread is killed:
if Assigned(FMessageListener) then
FMessageListener.Terminate;
The code for the thread itself:
procedure TTelegramListener.Execute;
var
LidHTTP: TIdHTTP;
LSSLSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
Offset, PrevOffset: Integer;
LJSONParser: TJSONObject;
LResronseList: TStringList;
LArrJSON: TJSONArray;
begin
Offset := 0;
PrevOffset := 0;
//create a local indy http component
try
LidHTTP := TIdHTTP.Create;
LidHTTP.HTTPOptions := LidHTTP.HTTPOptions + [hoNoProtocolErrorException];
LidHTTP.Request.BasicAuthentication := False;
LidHTTP.Request.CharSet := 'utf-8';
LidHTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
LSSLSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(LidHTTP);
LSSLSocketHandler.SSLOptions.Method := sslvTLSv1_2;
LSSLSocketHandler.SSLOptions.SSLVersions := [sslvTLSv1_2];
LSSLSocketHandler.SSLOptions.Mode := sslmUnassigned;
LSSLSocketHandler.SSLOptions.VerifyMode := [];
LSSLSocketHandler.SSLOptions.VerifyDepth := 0;
LidHTTP.IOHandler := LSSLSocketHandler;
LJSONParser := TJSONObject.Create;
LResronseList := TStringList.Create;
except
on E: Exception do
begin
FLastError := 'Error of create objects';
FreeAndNil(LidHTTP);
FreeAndNil(LJSONParser);
FreeAndNil(LResronseList);
end;
end;
try
while not Terminated do
begin
LJSONParser := TJSONObject.Create;
if Assigned(LidHTTP) then
begin
FResponse := LidHTTP.Get(cBaseUrl + FTelegramToken + '/getUpdates?offset=' + IntToStr(Offset) + '&timeout=30');
if FResponse.Trim = '' then
Continue;
LArrJSON := ((TJSONObject.ParseJSONValue(FResponse) as TJSONObject).GetValue('result') as TJSONArray);
if lArrJSON.Count <= 0 then Continue;
LResronseList.Clear;
for var I := 0 to LArrJSON.Count - 1 do
LResronseList.Add(LArrJSON.Items[I].ToJSON);
Offset := LResronseList.Count;
if Offset > PrevOffset then
begin
LJSONParser := TJSONObject.ParseJSONValue(LResronseList[LResronseList.Count - 1], False, True) as TJSONObject;
if (LJSONParser.FindValue('message.text') <> nil) and (LJSONParser.FindValue('message.text').Value.Trim <> '') then
begin
if LJSONParser.FindValue('message.from.id') <> nil then
FUserID := LJSONParser.FindValue('message.from.id').Value; //Его ИД по которому можем ему написать
if LJSONParser.FindValue('message.from.first_name') <> nil then
FUserName := LJSONParser.FindValue('message.from.first_name').Value;
if (LJSONParser.FindValue('message.from.first_name') <> nil) and (LJSONParser.FindValue('message.from.last_name') <> nil) then
FUserName := LJSONParser.FindValue('message.from.first_name').Value + ' ' + LJSONParser.FindValue('message.from.last_name').Value; //Это имя написавшего боту
if LJSONParser.FindValue('message.text') <> nil then
FUserMessage := LJSONParser.FindValue('message.text').Value; //Текст сообщения
Synchronize(Status); // Сообщим что есть ответ
end;
if LJSONParser <> nil then
LJSONParser.Free;
PrevOffset := LResronseList.Count;
end;
end;
end;
finally
FreeAndNil(LidHTTP);
FreeAndNil(LJSONParser);
FreeAndNil(LResronseList);
end;
end;
In the Status procedure, the Callback function is called:
procedure TTelegramListener.Status;
begin
if Assigned(FCallback) then
FCallback(FUserID, FUserName, FUserMessage);
end;
How to fix this code so that everything is thread-safe and solve the problem with the exception?
Tried exiting the while loop on a flag that is passed before destroying the thread. This didn't solve the problem. Tried Disconnecting the
LidHTTP
component, but that didn't work either.

Having dealt with the problem, the code works like this:
procedure TTelegramListener.Execute;
var
LidHTTP: TIdHTTP;
LSSLSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
Offset, PrevOffset: Integer;
LJSONParser: TJSONObject;
LResronseList: TStringList;
LArrJSON: TJSONArray;
begin
Offset := 0;
PrevOffset := 0;
//create a local indy http component
LidHTTP := TIdHTTP.Create;
LidHTTP.HTTPOptions := LidHTTP.HTTPOptions + [hoNoProtocolErrorException];
LidHTTP.Request.BasicAuthentication := False;
LidHTTP.Request.CharSet := 'utf-8';
LidHTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
LSSLSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(LidHTTP);
LSSLSocketHandler.SSLOptions.Method := sslvTLSv1_2;
LSSLSocketHandler.SSLOptions.SSLVersions := [sslvTLSv1_2];
LSSLSocketHandler.SSLOptions.Mode := sslmUnassigned;
LSSLSocketHandler.SSLOptions.VerifyMode := [];
LSSLSocketHandler.SSLOptions.VerifyDepth := 0;
LidHTTP.IOHandler := LSSLSocketHandler;
LJSONParser := TJSONObject.Create;
LResronseList := TStringList.Create;
try
while not Terminated do
begin
if Assigned(LidHTTP) then
begin
FResponse := LidHTTP.Get(cBaseUrl + FTelegramToken + '/getUpdates?offset=' + IntToStr(Offset) + '&timeout=30');
if FResponse.Trim = '' then
Continue;
LArrJSON := ((TJSONObject.ParseJSONValue(FResponse) as TJSONObject).GetValue('result') as TJSONArray);
if lArrJSON.Count <= 0 then Continue;
LResronseList.Clear;
for var I := 0 to LArrJSON.Count - 1 do
LResronseList.Add(LArrJSON.Items[I].ToJSON);
Offset := LResronseList.Count;
if Offset > PrevOffset then
begin
LJSONParser := TJSONObject.ParseJSONValue(LResronseList[LResronseList.Count - 1], False, True) as TJSONObject;
if (LJSONParser.FindValue('message.text') <> nil) and (LJSONParser.FindValue('message.text').Value.Trim <> '') then
begin
if LJSONParser.FindValue('message.from.id') <> nil then
FUserID := LJSONParser.FindValue('message.from.id').Value; //Его ИД по которому можем ему написать
if LJSONParser.FindValue('message.from.first_name') <> nil then
FUserName := LJSONParser.FindValue('message.from.first_name').Value;
if (LJSONParser.FindValue('message.from.first_name') <> nil) and (LJSONParser.FindValue('message.from.last_name') <> nil) then
FUserName := LJSONParser.FindValue('message.from.first_name').Value + ' ' + LJSONParser.FindValue('message.from.last_name').Value; //Это имя написавшего боту
if LJSONParser.FindValue('message.text') <> nil then
FUserMessage := LJSONParser.FindValue('message.text').Value; //Текст сообщения
Synchronize(Status); // Сообщим что есть ответ
end;
PrevOffset := LResronseList.Count;
end;
end;
end;
finally
FreeAndNil(LidHTTP);
FreeAndNil(LJSONParser);
FreeAndNil(LResronseList);
end;
end;
Thanks everyone for the replies. A library for working with the Telegram API has been created, the library supports sending and receiving messages, sending files and geolocation. Link to the GitHub project: https://github.com/yaroslav-arkhipov/Telebot_pascal_lib/

Related

Error to receive file on socket inside a thread

I'm having trouble to receive a byte array containg a PNG file.
When the code is executed in OnClientRead event it works fine, already when transfered for a thread, happens an error of MemoryStream that says:
Out of memory while expanding memory stream.
At this point:
if SD.State = ReadingSize then
I want to know how to solve this specific trouble and also how can I check if I'm receiving a data that contains a file or a simple String?
The code:
type
TSock_Thread = class(TThread)
private
Socket: TCustomWinSocket;
public
constructor Create(aSocket: TCustomWinSocket);
procedure Execute; override;
end;
type
TInt32Bytes = record
case Integer of
0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
1: (Value: Int32);
end;
TSocketState = (ReadingSize, ReadingStream);
TSocketData = class
public
Stream: TMemoryStream;
Png: TPngImage;
State: TSocketState;
Size: TInt32Bytes;
Offset: Integer;
constructor Create;
destructor Destroy; override;
end;
{ ... }
constructor TSock_Thread.Create(aSocket: TCustomWinSocket);
begin
inherited Create(true);
Socket := aSocket;
FreeOnTerminate := true;
end;
procedure TSock_Thread.Execute;
var
s: String;
BytesReceived: Integer;
BufferPtr: PByte;
SD: TSocketData;
Item: TListItem;
begin
inherited;
while Socket.Connected do
begin
if Socket.ReceiveLength > 0 then
begin
s := Socket.ReceiveText;
{ SD := TSocketData(Socket.Data);
if SD.State = ReadingSize then
begin
while SD.Offset < SizeOf(Int32) do
begin
BytesReceived := Socket.ReceiveBuf(SD.Size.Bytes[SD.Offset],
SizeOf(Int32) - SD.Offset);
if BytesReceived <= 0 then
Exit;
Inc(SD.Offset, BytesReceived);
end;
SD.Size.Value := ntohl(SD.Size.Value);
SD.State := ReadingStream;
SD.Offset := 0;
SD.Stream.Size := SD.Size.Value;
end;
if SD.State = ReadingStream then
begin
if SD.Offset < SD.Size.Value then
begin
BufferPtr := PByte(SD.Stream.Memory);
Inc(BufferPtr, SD.Offset);
repeat
BytesReceived := Socket.ReceiveBuf(BufferPtr^,
SD.Size.Value - SD.Offset);
if BytesReceived <= 0 then
Exit;
Inc(BufferPtr, BytesReceived);
Inc(SD.Offset, BytesReceived);
until SD.Offset = SD.Size.Value;
end;
try
SD.Stream.Position := 0;
SD.Png.LoadFromStream(SD.Stream);
SD.Stream.Clear;
except
SD.Png.Assign(nil);
end;
Item := Form1.ListView1.Selected;
if (Item <> nil) and (Item.Data = Socket) then
Form1.img1.Picture.Graphic := SD.Png;
SD.State := ReadingSize;
SD.Offset := 0;
end; }
end;
Sleep(100);
end;
end;
procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
var
TST: TSock_Thread;
begin
TST := TSock_Thread.Create(Socket);
TST.Resume;
end;
UPDATE:
The code in the answer is not working for me because ServerType=stThreadBlocking blocks all clients connections with the server. And because of this, I'm searching for something like this (ServerType=stNonBlocking, TThread and OnAccept event):
type
TSock_Thread = class(TThread)
private
Png: TPngImage;
Socket: TCustomWinSocket;
public
constructor Create(aSocket: TCustomWinSocket);
procedure Execute; override;
procedure PngReceived;
end;
// ...
// ===============================================================================
constructor TSock_Thread.Create(aSocket: TCustomWinSocket);
begin
inherited Create(true);
Socket := aSocket;
FreeOnTerminate := true;
end;
// ===============================================================================
procedure TSock_Thread.PngReceived;
var
Item: TListItem;
begin
Item := Form1.ListView1.Selected;
if (Item <> nil) and (Item.Data = Socket) then
Form1.img1.Picture.Graphic := Png;
end;
procedure TSock_Thread.Execute;
var
Reciving: Boolean;
DataSize: Integer;
Data: TMemoryStream;
s, sl: String;
begin
inherited;
while Socket.Connected do
begin
if Socket.ReceiveLength > 0 then
begin
s := Socket.ReceiveText;
if not Reciving then
begin
SetLength(sl, StrLen(PChar(s)) + 1);
StrLCopy(#sl[1], PChar(s), Length(sl) - 1);
DataSize := StrToInt(sl);
Data := TMemoryStream.Create;
Png := TPngImage.Create;
Delete(s, 1, Length(sl));
Reciving := true;
end;
try
Data.Write(s[1], Length(s));
if Data.Size = DataSize then
begin
Data.Position := 0;
Png.LoadFromStream(Data);
Synchronize(PngReceived);
Data.Free;
Reciving := false;
end;
except
Png.Assign(nil);
Png.Free;
Data.Free;
end;
end;
Sleep(100);
end;
end;
procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
var
TST: TSock_Thread;
begin
TST := TSock_Thread.Create(Socket);
TST.Resume;
end;
This code has an error of conversion of data at this line:
DataSize := StrToInt(sl);
How can I fix this?
how solve this specific trouble
You are not using TServerSocket threading the way it is meant to be used.
If you want to use TServerSocket in stThreadBlocking mode (see my other answer for how to use TServerSocket in stNonBlocking mode), the correct way is to:
derive a thread class from TServerClientThread
override its virtual ClientExecute() method to do your I/O work (via TWinSocketStream)
use the TServerSocket.OnGetThread event to instantiate the thread.
If you don't do this, TServerSocket will create its own default threads (to fire the OnClient(Read|Write) events in the main thread), which will interfere with your manual threads.
Also, you don't need the state machine that I showed you in my answer to your other question. That was for event-driven code. Threaded I/O code can be written linearly instead.
Try something more like this:
type
TSock_Thread = class(TServerClientThread)
private
Png: TPngImage;
procedure PngReceived;
protected
procedure ClientExecute; override;
end;
type
TInt32Bytes = record
case Integer of
0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
1: (Value: Int32);
end;
procedure TSock_Thread.ClientExecute;
var
SocketStrm: TWinSocketStream;
Buffer: TMemoryStream;
Size: TInt32Bytes;
Offset: Integer;
BytesReceived: Integer;
BufferPtr: PByte;
begin
SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
try
Buffer := TMemoryStream.Create;
try
Png := TPngImage.Create;
try
while ClientSocket.Connected do
begin
if not SocketStrm.WaitForData(100) then Continue;
Offset := 0;
while Offset < SizeOf(Int32) do
begin
BytesReceived := SocketStrm.Read(Size.Bytes[Offset], SizeOf(Int32) - Offset);
if BytesReceived <= 0 then Exit;
Inc(Offset, BytesReceived);
end;
Size.Value := ntohl(Size.Value);
Buffer.Size := Size.Value;
BufferPtr := PByte(Buffer.Memory);
Offset := 0;
while Offset < Size.Value do
begin
BytesReceived := SocketStrm.Read(BufferPtr^, Size.Value - Offset);
if BytesReceived <= 0 then Exit;
Inc(BufferPtr, BytesReceived);
Inc(Offset, BytesReceived);
end;
Buffer.Position := 0;
try
Png.LoadFromStream(Buffer);
except
Png.Assign(nil);
end;
Synchronize(PngReceived);
end;
finally
Png.Free;
end;
finally
Buffer.Free;
end;
finally
SocketStrm.Free;
end;
end;
procedure TSock_Thread.PngReceived;
var
Item: TListItem;
begin
Item := Form1.ListView1.Selected;
if (Item <> nil) and (Item.Data = ClientSocket) then
Form1.img1.Picture.Graphic := Png;
end;
procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
SocketThread := TSock_Thread.Create(False, ClientSocket);
end;
how i can check if i'm receiving a data that contains a file or a simple String?
The client needs to send that information to your server. You are already sending a value to specify the data size before sending the actual data. You should also preceed the data with a value to specify the data's type. Then you can handle the data according to its type as needed.

Inno Setup - Define music button and error with language selector?

This is the code with the error:
#include "Music\botva2.iss"
#include "Music\BASS_Module.iss"
[Code]
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
external 'ShellExecuteW#shell32.dll stdcall';
var
LanguageForm: TSetupForm;
SelectLabel: TNewStaticText;
CancelButton: TNewButton;
procedure LangChange(Sender : TObject);
begin
case TNewComboBox(Sender).ItemIndex of
0: { English }
begin
SelectLabel.Caption := 'Select the language to the installation:';
CancelButton.Caption := 'Cancel';
LanguageForm.Caption := 'PH';
end;
1: { Español }
begin
SelectLabel.Caption := 'Selecciona el idioma de la instalación:';
CancelButton.Caption := 'Cancelar';
LanguageForm.Caption := 'PH';
end;
end;
end;
procedure SelectLanguage();
var
OKButton: TNewButton;
LangCombo: TNewComboBox;
Languages: TStrings;
Params: string;
Instance: THandle;
P, I: Integer;
S, L: string;
begin
Languages := TStringList.Create();
Languages.Add('eng=English');
Languages.Add('spa=Español');
LanguageForm := CreateCustomForm;
LanguageForm.Caption := SetupMessage(msgSelectLanguageTitle);
LanguageForm.ClientWidth := ScaleX(240);
LanguageForm.ClientHeight := ScaleY(125);
LanguageForm.BorderStyle := bsDialog;
LanguageForm.Center;
CancelButton := TNewButton.Create(LanguageForm);
CancelButton.Parent := LanguageForm;
CancelButton.Left := ScaleX(140);
CancelButton.Top := ScaleY(93);
CancelButton.Width := ScaleY(90);
CancelButton.Height := ScaleY(23);
CancelButton.TabOrder := 3;
CancelButton.ModalResult := mrCancel;
CancelButton.Caption := SetupMessage(msgButtonCancel);
OKButton := TNewButton.Create(LanguageForm);
OKButton.Parent := LanguageForm;
OKButton.Left := ScaleX(10);
OKButton.Top := ScaleY(93);
OKButton.Width := ScaleX(90);
OKButton.Height := ScaleY(23);
OKButton.Caption := SetupMessage(msgButtonOK);
OKButton.Default := True
OKButton.ModalResult := mrOK;
OKButton.TabOrder := 2;
LangCombo := TNewComboBox.Create(LanguageForm);
LangCombo.Parent := LanguageForm;
LangCombo.Left := ScaleX(16);
LangCombo.Top := ScaleY(56);
LangCombo.Width := ScaleX(206);
LangCombo.Height := ScaleY(21);
LangCombo.Style := csDropDownList;
LangCombo.DropDownCount := 16;
LangCombo.TabOrder := 1;
SelectLabel := TNewStaticText.Create(LanguageForm);
SelectLabel.Parent := LanguageForm;
SelectLabel.Left := ScaleX(16);
SelectLabel.Top := ScaleY(15);
SelectLabel.Width := ScaleX(273);
SelectLabel.Height := ScaleY(39);
SelectLabel.AutoSize := False
SelectLabel.Caption := SetupMessage(msgSelectLanguageLabel);
SelectLabel.TabOrder := 0;
SelectLabel.WordWrap := True;
for I := 0 to Languages.Count - 1 do
begin
P := Pos('=', Languages.Strings[I]);
L := Copy(Languages.Strings[I], 0, P - 1);
S := Copy(Languages.Strings[I], P + 1, Length(Languages.Strings[I]) - P);
LangCombo.Items.Add(S);
if L = ActiveLanguage then
LangCombo.ItemIndex := I;
LangCombo.OnChange := #LangChange;
end;
if LanguageForm.ShowModal = mrOK then
begin
// Collect current instance parameters
for I := 1 to ParamCount do
begin
S := ParamStr(I);
// Unique log file name for the elevated instance
if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
begin
S := S + '-localized';
end;
// Do not pass our /SL5 switch
if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
begin
Params := Params + AddQuotes(S) + ' ';
end;
end;
L := Languages.Strings[LangCombo.ItemIndex];
P := Pos('=', L);
L := Copy(L, 0, P-1);
// ... and add selected language
Params := Params + '/LANG=' + L;
Instance := ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
if Instance <= 32 then
begin
MsgBox(
Format('Running installer with selected language failed. Code: %d', [Instance]),
mbError, MB_OK);
end;
end;
end;
function InitializeSetup(): Boolean;
var
Language: string;
begin
Result := True;
Language := ExpandConstant('{param:LANG}');
if Language = '' then
begin
Log('No language specified, showing language dialog');
SelectLanguage();
Result := False;
Exit;
end
else
begin
Log('Language specified, proceeding with installation');
end;
end;
procedure RedesignWizardForm;
begin
with WizardForm do
begin
BorderIcons:=[];
Bevel1.Hide;
AutoScroll := False;
ClientHeight := ScaleY(349);
end;
with WizardForm.CancelButton do
begin
Top := ScaleY(319);
end;
with WizardForm.NextButton do
begin
Top := ScaleY(319);
end;
with WizardForm.BackButton do
begin
Top := ScaleY(319);
end;
with WizardForm.WizardBitmapImage do
begin
Width := ScaleX(500);
end;
with WizardForm.WelcomeLabel2 do
begin
Visible := False;
end;
with WizardForm.WelcomeLabel1 do
begin
Visible := False;
end;
with WizardForm.WizardSmallBitmapImage do
begin
Left := ScaleX(0);
Width := ScaleX(500);
Height := ScaleY(60);
end;
with WizardForm.PageDescriptionLabel do
begin
Visible := False;
end;
with WizardForm.PageNameLabel do
begin
Visible := False;
end;
with WizardForm.WizardBitmapImage2 do
begin
Width := ScaleX(500);
ExtractTemporaryFile('WizardForm.WizardBitmapImage2.bmp');
Bitmap.LoadFromFile(ExpandConstant('{tmp}\WizardForm.WizardBitmapImage2.bmp'));
end;
with WizardForm.FinishedLabel do
begin
Visible := False;
end;
with WizardForm.FinishedHeadingLabel do
begin
Visible := False;
end;
end;
procedure InitializeWizard1();
begin
RedesignWizardForm;
WizardForm.DiskSpaceLabel.Visible := False;
end;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4)
end;
procedure InitializeWizard();
begin
InitializeWizard1();
InitializeWizard2();
end;
procedure DeinitializeSetup();
begin
BASS_DeInit; //Îñâîáîæäàåì ïðîöåññ
gdipShutdown
end;
This code includes the language selector Inno Setup - How to change a label caption [or other controls in general], when selected value in combox box changes and a code that define music and music button. If i delete all about language selector, the code works fine. What is the problem?
The code of music and button includes: botva2.iss, BASS_Module.iss, botva2.dll, CallbackCtrl.dll.
This error appears when you select accept or cancel on the language selector.
The DeinitializeSetup is called, even when the setup is aborted by returning False from the InitializeSetup.
So I guess the BASS_DeInit (or the gdipShutdown) fails, because an equivalent BASS_Init was never called.
You have to avoid calling the code in the DeinitializeSetup, when the BASS_Init was never called.
var
BASS_Initialized: Boolean;
procedure InitializeWizard2();
begin
ExtractTemporaryFile('BASS.dll');
ExtractTemporaryFile('CallbackCtrl.dll');
ExtractTemporaryFile('botva2.dll');
ExtractTemporaryFile('MusicButton.png');
ExtractTemporaryFile('Music.mp3');
BASS_Init('{tmp}\Music.mp3')
BASS_CreateOnOffButton(WizardForm, '{tmp}\MusicButton.png', 20, 320, 36, 36, 4);
BASS_Initialized := True;
end;
procedure DeinitializeSetup();
begin
if BASS_Initialized then
begin
BASS_DeInit;
gdipShutdown;
end;
end;

Read strings from .exe files(Like Strings.exe) in Delphi

I want to write a program for read/extract all of the valid strings in a .exe files (For example: "This program must be run under Win" or "MZ"), Exactly like Strings.exe of sysinternals.
Actually i want to scan a .exe file and if that contain special string value such as "ekrn.exe" or "Filrefox.exe" then detect that file as a suspicious file (Killing ekrn.exe or inject malcode to firefox.exe).
I wrote the following code in Delphi :
const
TargetName = 'E:\AntiDebugg.exe';
var
hFile: THandle;
tmp: AnsiString;
dwFileSize, lChar, lSearch: Integer;
dwNumRead: Cardinal;
dwBuffer: array of AnsiChar;
begin
mmo1.Clear;
hFile := CreateFileA(TargetName, GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
dwFileSize := GetFileSize(hFile, nil);
SetLength(dwBuffer, dwFileSize);
lChar := 0;
lSearch := 0;
while lChar <= dwFileSize do
begin
ReadFile(hFile, dwBuffer[lChar], SizeOf(dwBuffer), dwNumRead, nil);
while dwBuffer[lChar] <> '' do
begin
tmp := tmp + dwBuffer[lChar];
Inc(lChar, 1);
end;
lSearch := 0;
Inc(lChar, 1);
end;
mmo1.Text := (tmp);
CloseHandle(hFile);
The result of running my code is (A small piece):
MZPےے¸#؛´ ح!¸Lح!گگThis program must be run under Win32
$7PEL
%0فQà´أ\
¤"0Bگب.textd­ .itext| .data`#.bssطN.idata\
#.didataب#.tls.rdata#.reloc¤"#.rsrc###Boolean#alseTrueSystem4#AnsiCharP# Charےh#Integerے€#Byteک#Wordے°#Pointerؤ#Cardinalےےےà# NativeIntےےےü#
NativeUIntے#ShortStringے,# PAnsiChar0#D#stringT#TClassŒ#h#HRESULTے€#TGUID
But this isn't my desired result and my desired result is :
MZP
This program must be run under Win32
.text
`.itext
`.data
.bss
.idata
.didata
.tls
.rdata
#.reloc
B.rsrc
Boolean
False
True
System
AnsiChar
Char
Integer
Byte
Word
Pointer
Cardinal
NativeInt
NativeUInt
ShortString
PAnsiChar0
string
TClass
HRESULT
TGUID
In this example the AntiDebugg.exe compiled by Delphi .
The result of Strings.exe for strings of "AntiDebugg.exe"
Any idea ?
What should i to do ?
Try something like this:
const
TargetName = 'E:\AntiDebugg.exe';
MinStringLength = 2;
var
hFile: THandle;
hMapping: THandle;
pView: Pointer;
dwFileSize: DWORD;
pCurrent, pEOF, pStart: PAnsiChar;
iLen: Integer;
begin
mmo1.Clear;
hFile := CreateFile(TargetName, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
dwFileSize := GetFileSize(hFile, nil);
if dwFileSize = $FFFFFFFF then RaiseLastOSError;
hMapping := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, dwFileSize, nil);
if hMapping = 0 then RaiseLastOSError;
try
pView := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, dwFileSize);
if pView = nil then RaiseLastOSError;
try
pCurrent := PAnsiChar(pView);
pEOF := pCurrent + dwFileSize;
pStart := nil;
while pCurrent < pEOF do
begin
if pCurrent^ in [#9, #10, #13, #32..#128] then
begin
if pStart = nil then
pStart := pCurrent;
end
else if pStart <> nil then
begin
iLen := Integer(pCurrent - pStart);
if iLen >= MinStringLength then
begin
SetString(tmp, pStart, iLen);
mmo1.Lines.Add(tmp);
end;
pStart := nil;
end;
Inc(pCurrent);
end;
finally
UnmapViewOfFile(pView);
end;
finally
CloseHandle(hMapping);
end;
finally
CloseHandle(hFile);
end;
end;
AsciiDump coded by {steve10120#ic0de.org}
function FileToPtr(szFilePath: string; var pFile: Pointer;
var dwFileSize: DWORD): Boolean;
var
hFile: DWORD;
dwRead: DWORD;
begin
Result := FALSE;
hFile := CreateFile(PChar(szFilePath), GENERIC_READ, 0, nil,
OPEN_EXISTING, 0, 0);
if (hFile <> INVALID_HANDLE_VALUE) then
begin
dwFileSize := GetFileSize(hFile, nil);
if (dwFileSize > 0) then
begin
pFile := VirtualAlloc(nil, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (Assigned(pFile)) then
begin
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
ReadFile(hFile, pFile^, dwFileSize, dwRead, nil);
if (dwRead = dwFileSize) then
Result := TRUE;
end;
end;
CloseHandle(hFile);
end;
end;
function FindASCIIStringsA(szFilePath: string; dwMinLength: DWORD;
szDumpPath: string): Boolean;
var
pFile: Pointer;
dwFileSize: DWORD;
i: DWORD;
szDump: string;
dwLength: DWORD;
hFile: TextFile;
begin
Result := FALSE;
if (FileToPtr(szFilePath, pFile, dwFileSize)) then
begin
dwLength := 0;
AssignFile(hFile, szDumpPath);
// yeah I don't like it but its easiest for writing lines..
Rewrite(hFile);
for i := 0 to (dwFileSize - 1) do
begin
if (PByte(DWORD(pFile) + i)^ in [$20 .. $7E]) then
begin
szDump := szDump + Char(PByte(DWORD(pFile) + i)^);
// WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
Inc(dwLength);
end
else
begin
if (dwLength >= dwMinLength) then
WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
dwLength := 0;
szDump := '';
end;
end;
if (FileSize(hFile) > 0) then
Result := TRUE;
CloseFile(hFile);
VirtualFree(pFile, 0, MEM_RELEASE);
end;
end;
function FindASCIIStrings(szFilePath:string; dwMinLength:DWORD; szDumpPath:string):Boolean;
var
pFile: Pointer;
dwFileSize: DWORD;
IDH: PImageDosHeader;
INH: PImageNtHeaders;
i: DWORD;
szDump: string;
dwLength: DWORD;
hFile: TextFile;
begin
Result := FALSE;
if (FileToPtr(szFilePath, pFile, dwFileSize)) then
begin
IDH := pFile;
if (IDH^.e_magic = IMAGE_DOS_SIGNATURE) then
begin
INH := Pointer(DWORD(pFile) + IDH^._lfanew);
if (INH^.Signature = IMAGE_NT_SIGNATURE) then
begin
dwLength := 0;
AssignFile(hFile, szDumpPath); // yeah I don't like it but its easiest for writing lines..
Rewrite(hFile);
for i := INH^.OptionalHeader.SizeOfHeaders to (dwFileSize - 1) do
begin
if (PByte(DWORD(pFile) + i)^ in [$20..$7E]) then
begin
szDump := szDump + Char(PByte(DWORD(pFile) + i)^);
Inc(dwLength);
end
else
begin
if (dwLength >= dwMinLength) then
WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
dwLength := 0;
szDump := '';
end;
end;
if (FileSize(hFile) > 0) then
Result := TRUE;
CloseFile(hFile);
end;
end;
VirtualFree(pFile, 0, MEM_RELEASE);
end;
end;
procedure TForm2.btn1Click(Sender: TObject);
begin
FindASCIIStrings('e:\AntiDebugg.exe', 2,
IncludeTrailingPathDelimiter(ExtractFilePath(param str(0))) +
ExtractFileName(paramstr(1)) + '.dmp')
end;

Delphi XE4 - get your local IP in statusbar

Seen most of the examples posted here but none seem to work on XE4. I am trying to display my IP in the status bar.
AdvOfficeStatusBar1.Panels[0].Text := GetIP;
Functions I have seen here mostly fail on xe4 as they were written in older delphi versions.I would like to get just the local IP,not the one behind the router. Well, for the sake of learning, it would be usefull to know that one too...:)
I tried this :
function getIP: string;
type
TaPInAddr = array [0..10] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
phe: PHostEnt;
pptr: PaPInAddr;
Buffer: array [0..63] of char;
i: Integer;
GInitData: TWSADATA;
begin
WSAStartup($101, GInitData);
Result := '';
GetHostName(Buffer, SizeOf(Buffer));
phe :=GetHostByName(buffer);
if phe = nil then Exit;
pptr := PaPInAddr(Phe^.h_addr_list);
i := 0;
while pptr^[i] <> nil do
begin
result:=StrPas(inet_ntoa(pptr^[i]^));
Inc(i);
end;
WSACleanup;
end;
but it would not work .... many others too ... Seems i am missing something,but what?
Tried this too :
function getIPs: Tstrings;
type
TaPInAddr = array[0..10] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
phe: PHostEnt;
pptr: PaPInAddr;
Buffer: array[0..63] of Char;
I: Integer;
GInitData: TWSAData;
begin
WSAStartup($101, GInitData);
Result := TstringList.Create;
Result.Clear;
GetHostName(Buffer, SizeOf(Buffer));
phe := GetHostByName(buffer);
if phe = nil then Exit;
pPtr := PaPInAddr(phe^.h_addr_list);
I := 0;
while pPtr^[I] <> nil do
begin
Result.Add(inet_ntoa(pptr^[I]^));
Inc(I);
end;
WSACleanup;
end;
wont work stops on : (GetHostName(Buffer, SizeOf(Buffer));
This however WORKS ! :
Function GetIPAddress: String;
type pu_long = ^u_long;
var varTWSAData : TWSAData;
varPHostEnt : PHostEnt;
varTInAddr : TInAddr;
namebuf : Array[0..255] of ansichar;
begin
try
try
If WSAStartup($101,varTWSAData) <> 0 Then
Result := ''
Else Begin
gethostname(namebuf,sizeof(namebuf));
varPHostEnt := gethostbyname(namebuf);
varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
Result := inet_ntoa(varTInAddr);
End;
except
Result := '';
end;
finally
WSACleanup;
end;
end;
Function GetIPAddress: String;
type pu_long = ^u_long;
var varTWSAData : TWSAData;
varPHostEnt : PHostEnt;
varTInAddr : TInAddr;
namebuf : Array[0..255] of ansichar;
begin
try
try
If WSAStartup($101,varTWSAData) <> 0 Then
Result := ''
Else Begin
gethostname(namebuf,sizeof(namebuf));
varPHostEnt := gethostbyname(namebuf);
varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
Result := inet_ntoa(varTInAddr);
End;
except
Result := '';
end;
finally
WSACleanup;
end;
end;

Delphi: Multithreading, Thread safe not working

When data is sending to "tunnel" socket, it's sometimes merged, implemented the Critical Section but it's not working..
What I'm doing wrong ?
type
my_ff_thread = class;
my_ss_thread = class;
Tmy_tunnel_from_MappedPortTCP = class;
Tmy_thread_list = class
ff_id : string;
ff_connection : TIdTCPConnection;
constructor Create(local_ff_id: string; local_ss_c: TIdTCPConnection);
end;
Tmy_tunnel_from_MappedPortTCP = class(TIdBaseComponent)
protected
procedure InitComponent; override;
public
function my_connect:boolean;
end;
my_ff_thread = class(TThread)
protected
procedure Execute; override;
public
constructor Create;
end;
my_ss_thread = class(TThread)
protected
Fff_id : string;
Fff_cmd : string;
Fff_data : TIdBytes;
procedure Execute; override;
public
constructor Create(ff_id:string; ff_cmd:string; ff_data:TIdBytes);
function prepare_cmd(cmd:string; id:string; data:string):string;
function set_nulls_at_begin(s:string):string;
end;
var my_list : TThreadList;
CS: TRTLCriticalSection;
tunnel: TIdTCPConnection;
Implementation
constructor my_ff_thread.Create;
begin
inherited Create(True);
end;
constructor my_ss_thread.Create(ff_id:string; ff_cmd:string; ff_data:TIdBytes);
begin
inherited Create(True);
Fff_id := ff_id;
Fff_cmd := ff_cmd;
Fff_data := ff_data;
end;
constructor Tmy_thread_list.Create(local_ff_id: string; local_ss_c: TIdTCPConnection);
begin
ff_id := local_ff_id;
ff_connection := local_ss_c;
end;
function my_ss_thread.set_nulls_at_begin(s:string):string;
var len, i : integer;
res : string;
begin
if s='' then
begin
Result := '';
Exit;
end;
res := '';
len := Length(s);
if len < 10 then
for i:=1 to (10 - len) do
begin
res := res + '0';
end;
Result := res + s;
end;
function my_ss_thread.prepare_cmd(cmd:string; id:string; data:string):string;
var
packet : string;
begin
packet := set_nulls_at_begin(IntToStr(Length(cmd))) + cmd;
packet := packet + set_nulls_at_begin(IntToStr(Length(id))) + id;
packet := packet + set_nulls_at_begin(IntToStr(Length(data))) + data;
Result := packet;
end;
function del_ff_from_list(firefox_id:string):boolean;
var i : integer;
begin
Result := True;
try
with my_list.LockList do
begin
for i:=0 to Count-1 do
begin
if Tmy_thread_list(Items[i]).ff_id = firefox_id then
begin
Delete(i);
break;
end;
end;
end;
finally
my_list.UnlockList;
end;
end;
procedure my_ss_thread.Execute;
var ss : TIdTCPClient;
unix_time : integer;
data : TIdBytes;
packet : string;
packet_stream: TStringStream;
begin
ss := TIdTCPClient.Create(nil);
try
with TIdTcpClient(ss) do
begin
Host := '127.0.0.1';
Port := 6666;
ReadTimeout := 1000 * 5;
Connect;
end;
except
on E:Exception do
begin
ss.Disconnect;
exit;
end;
end;
try
my_list.LockList.Add(Tmy_thread_list.Create(Fff_id, ss));
finally
my_list.UnlockList;
end;
try
ss.Socket.Write(Fff_data);
except
on E:Exception do begin {Fmy_memo.Lines.Add('First data not sent!');} end;
end;
unix_time := DateTimeToUnix(NOW);
while True do
begin
ss.Socket.CheckForDataOnSource(5);
if not ss.Socket.InputBufferIsEmpty then
begin
SetLength(data, 0);
ss.Socket.InputBuffer.ExtractToBytes(data);
packet := prepare_cmd('data_from_ss', Fff_id, TIdEncoderMIME.EncodeBytes(data));
packet_stream := TStringStream.Create(packet);
packet_stream.Position := 0;
ss.Socket.InputBuffer.Clear;
unix_time := DateTimeToUnix(NOW);
try
EnterCriticalSection(CS);
tunnel.Socket.Write(packet_stream, -1, True);
LeaveCriticalSection(CS);
except
on E:Exception do
begin
end;
end;
end;
if (DateTimeToUnix(NOW) - unix_time) > 120 then
begin
ss.Disconnect;
break;
end;
if not ss.Connected then
begin
break;
end;
if not tunnel.Connected then
begin
ss.Disconnect;
break;
end;
end;
try
if tunnel.Connected then
begin
EnterCriticalSection(CS);
packet := prepare_cmd('disconnect', Fff_id, 'x');
packet_stream := TStringStream.Create(packet);
packet_stream.Position := 0;
tunnel.Socket.Write(packet_stream, -1, True);
LeaveCriticalSection(CS);
end;
except
on E:Exception do begin end;
end;
Terminate;
end;
procedure my_ff_thread.Execute;
var
t : my_ss_thread;
cmd, id : string;
i : integer;
found_ss : TIdTCPConnection;
list : TList;
packet : string;
cmd_len, id_len, data_len : integer;
data : TIdBytes;
orig_data : string;
packet_stream: TStringStream;
cmd_len_str, id_len_str, data_len_str : string;
begin
packet_stream := TStringStream.Create;
while not Terminated do
begin
packet_stream.Position := 0;
try
tunnel.Socket.ReadStream(packet_stream);
except
on E:Exception do begin end;
end;
packet := packet_stream.DataString;
if packet = '0000' then
continue;
try
cmd_len_str := Copy(packet, 1, 10);
cmd_len := StrToInt(cmd_len_str);
except
on E:Exception do begin end;
end;
Delete(packet, 1, 10);
cmd := Copy(packet, 1, cmd_len);
Delete(packet, 1, cmd_len);
try
id_len_str := Copy(packet, 1, 10);
id_len := StrToInt(id_len_str);
except
on E:Exception do begin end;
end;
Delete(packet, 1, 10);
id := Copy(packet, 1, id_len);
Delete(packet, 1, id_len);
SetLength(data, 0);
try
data_len_str := Copy(packet, 1, 10);
data_len := StrToInt(data_len_str);
except
on E:Exception do begin end;
end;
Delete(packet, 1, 10);
data := TIdDecoderMIME.DecodeBytes(Copy(packet, 1, data_len));
orig_data := Copy(packet, 1, data_len);
Delete(packet, 1, data_len);
found_ss := nil;
try
list := my_list.LockList;
for i:=0 to list.Count-1 do
begin
if Tmy_thread_list(list[i]).ff_id = id then
begin
found_ss := Tmy_thread_list(list[i]).ff_connection;
break;
end;
end;
finally
my_list.UnlockList;
end;
if cmd = 'disconnect' then
begin
if found_ss <> nil then
if found_ss.Connected then
begin
found_ss.Disconnect;
del_ff_from_list(id);
continue;
end;
end;
if found_ss = nil then
begin
t := my_ss_thread.Create(id, cmd, data);
t.Start;
end
else
begin
if found_ss <> nil then
try
if found_ss.Connected then
begin
found_ss.Socket.Write(data);
end;
except
on E:Exception do begin end;
end;
end;
if not tunnel.Connected then
begin
Terminate;
break;
end;
end;
end;
function Tmy_tunnel_from_MappedPortTCP.my_connect:boolean;
var t : my_ff_thread;
begin
Result := True;
try
with TIdTcpClient(tunnel) do
begin
Host := '192.168.0.157';
Port := 8099;
Connect;
end;
except
on E:Exception do
begin
tunnel.Disconnect;
exit;
end;
end;
t := my_ff_thread.Create;
t.Start;
end;
initialization
InitializeCriticalSection(CS);
my_list := TThreadList.Create;
tunnel := TIdTCPClient.Create(nil);
finalization
DeleteCriticalSection(CS);
end.
Try something like this:
type
my_ff_thread = class;
my_ss_thread = class;
Tmy_tunnel_from_MappedPortTCP = class;
Tmy_thread_list = class
public
ff_id : string;
ff_connection : TIdTCPConnection;
constructor Create(const local_ff_id: string; local_ss_c: TIdTCPConnection);
end;
Tmy_tunnel_from_MappedPortTCP = class(TIdBaseComponent)
protected
procedure InitComponent; override;
public
function my_connect: boolean;
function my_disconnect: boolean;
end;
my_ff_thread = class(TThread)
protected
procedure Execute; override;
public
constructor Create;
end;
my_ss_thread = class(TThread)
protected
Fff_id : string;
Fff_cmd : string;
Fff_data : TIdBytes;
procedure Execute; override;
public
constructor Create(const ff_id, ff_cmd: string; const ff_data: TIdBytes);
end;
var
my_list : TThreadList = nil;
CS: TCriticalSection = nil;
tunnel: TIdTCPClient = nil;
tunnel_thread: my_ff_thread = nil;
implementation
constructor Tmy_thread_list.Create(const local_ff_id: string; local_ss_c: TIdTCPConnection);
begin
ff_id := local_ff_id;
ff_connection := local_ss_c;
end;
constructor my_ss_thread.Create(const ff_id, ff_cmd: string; const ff_data: TIdBytes);
begin
inherited Create(False);
Fff_id := ff_id;
Fff_cmd := ff_cmd;
Fff_data := Copy(ff_data, 0, Length(ff_data));
end;
procedure my_ss_thread.Execute;
var
ss : TIdTCPClient;
data : TIdBytes;
packet : string;
procedure WriteStrToStream(strm: TStream; const s: String);
var
buf: TIdBytes;
len: Integer;
begin
buf := ToBytes(s, IndyUTF8Encoding);
len := Length(buf);
strm.WriteBuffer(len, SizeOf(Integer));
if bytes <> nil then
strm.WriteBuffer(buf[0], len);
end;
procedure WritePacketToTunnel(const cmd: string; const bytes: TIdBytes = nil);
var
strm: TMemoryStream;
begin
strm := TMemoryStream.Create;
try
WriteStrToStream(strm, cmd);
WriteStrToStream(strm, Fff_id);
WriteStrToStream(strm, TIdEncoderMIME.EncodeBytes(bytes));
CS.Enter;
try
tunnel.IOHandler.Write(strm, 0, True);
finally
CS.Leave;
end;
finally
strm.Free;
end;
end;
begin
ss := TIdTCPClient.Create(nil);
try
ss.Host := '127.0.0.1';
ss.Port := 6666;
ss.ReadTimeout := 1000 * 120;
ss.Connect;
try
my_list.Add(Tmy_thread_list.Create(Fff_id, ss));
try
ss.IOHandler.Write(Fff_data);
except
{Fmy_memo.Lines.Add('First data not sent!');}
raise;
end;
while not Terminated do
begin
SetLength(data, 0);
ss.IOHandler.ReadBytes(data, -1);
if Length(data) = 0 then
break;
WritePacketToTunnel('data_from_ss', data);
end;
WritePacketToTunnel('disconnect');
finally
ss.Disconnect;
end;
finally
ss.Free;
end;
end;
constructor my_ff_thread.Create;
begin
inherited Create(False);
end;
procedure my_ff_thread.Execute;
var
cmd, id : string;
data : TIdBytes;
i : integer;
found_ss : TIdTCPConnection;
list : TList;
function ReadStrFromStream(strm: TStream): string;
var
len: Integer;
begin
strm.ReadBuffer(len, SizeOf(Integer));
if len > 0 then
Result := IdGlobal.ReadStringFromStream(strm, len, IndyUTF8Encoding)
else
Result := '';
end;
procedure ReadPacketFromTunnel(var v_cmd, v_id: string; var v_data: TIdBytes);
var
strm: TMemoryStream;
begin
strm := TMemoryStream.Create;
try
tunnel.IOHandler.ReadStream(strm, -1, False);
strm.Position := 0;
v_cmd := ReadStrFromStream(strm);
v_id := ReadStrFromStream(strm);
v_data := TIdDecoderMIME.DecodeBytes(ReadStrFromStream(strm));
finally
strm.Free;
end;
end;
begin
while not Terminated do
begin
ReadPacketFromTunnel(cmd, id, data);
found_ss := nil;
list := my_list.LockList;
try
for i := 0 to list.Count-1 do
begin
if Tmy_thread_list(list[i]).ff_id = id then
begin
found_ss := Tmy_thread_list(list[i]).ff_connection;
break;
end;
end;
finally
my_list.UnlockList;
end;
if cmd = 'disconnect' then
begin
if found_ss <> nil then
found_ss.Disconnect;
del_ff_from_list(id);
continue;
end;
if found_ss <> nil then
begin
try
found_ss.IOHandler.Write(data);
except
end;
Continue;
end;
my_ss_thread.Create(id, cmd, data);
end;
end;
function Tmy_tunnel_from_MappedPortTCP.my_connect: boolean;
begin
Result := True;
try
tunnel.Host := '192.168.0.157';
tunnel.Port := 8099;
tunnel.Connect;
tunnel_thread := my_ff_thread.Create(tunnel);
except
tunnel.Disconnect;
Result := False;
end;
end;
function Tmy_tunnel_from_MappedPortTCP.my_disconnect: boolean;
begin
Result := True;
try
if tunnel_thread <> nil then tunnel_thread.Terminate;
try
tunnel.Disconnect;
finally
if tunnel_thread <> nil then
begin
tunnel_thread.WaitFor;
FreeAnNil(tunnel_thread);
end;
end;
except
Result := False;
end;
end;
initialization
CS := TCriticalSection.Create;
my_list := TThreadList.Create;
tunnel := TIdTCPClient.Create(nil);
finalization
tunnel.Free;
my_list.Free;
CS.Free;
end.

Resources