[Lazarus] WinINet STDCALL callback crash

Benito van der Zander benito at benibela.de
Thu Mar 14 00:09:33 CET 2013


> However, I still get frequent, random segfaults when calling Windows functions.  My current attempt, as simple as I can make it, is below.  About 50% of the time, the first command in the execute procedure works fine; the other times it causes a crash.
An actually crash or just a segfault message in the debugger?

I have been using wininet in a similar way for years, and it throws an 
error quite often, when run from within Lazarus.
But when called normally, without Lazarus everything works fine.
I think wininet and debuggers just do not play well together...


Benito

On 03/13/2013 05:51 PM, matthew at accordancebible.com wrote:
> Sven,
>
> Thank you very much for your threading example below.  I have used the model several times to build threaded implementations.  However, I still get frequent, random segfaults when calling Windows functions.  My current attempt, as simple as I can make it, is below.  About 50% of the time, the first command in the execute procedure works fine; the other times it causes a crash.  Might you or any others have suggestions on why this is?
>
> Thank you,
> Matthew
>
> <pre>
>
> TYPE
> 		DownloadThread = CLASS(TThread)
> 			PROCEDURE EXECUTE; OVERRIDE;
>
> 			PUBLIC
> 				theURL, theDest: AnsiString;
>
> 			PUBLIC
> 				CONSTRUCTOR Create(URLString: Str; dest: Str);
> 		END;
>
> VAR //global
> 		gTheURLString: Str;
> 		dest: Str;
>
> IMPLEMENTATION
>
> 	CONSTRUCTOR DownloadThread.Create(URLString: Str; dest: Str);
> 	BEGIN
> 		theURL := URLString;
> 		theDest := dest;
>
> 	  FreeOnTerminate := True;
> 	  inherited Create(False);
> 	END;
>
> 	PROCEDURE DownloadThread.execute;
> 	{ OVERRIDE }
> 	VAR
> 	  hInet: HINTERNET;
> 	  hURL: HINTERNET;
> 	  Buffer: array[0..1023] of Byte;
> 	  BufferLen, amt: cardinal;
> 	  f: file;
> 	  success: BOOLEAN;
> 	  appTitle: PChar;
>
> 	BEGIN
> 		appTitle := 'Lazarus App with Threading';
> 	  try
> 		  hInet := InternetOpen(appTitle, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
> 	    hURL := InternetOpenUrl(hInet, PChar(theURL), nil, 0, INTERNET_FLAG_RELOAD, 0);
> 	    try
> 	      FileMode := fmOpenWrite;
> 	      AssignFile(f, theDest);
> 	      try
> 	        Rewrite(f, 1);
> 	        amt := SizeOf(Buffer);
> 	        repeat
> 	          success := InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
> 	          BlockWrite(f, Buffer[0], BufferLen, amt);
> 	          if Terminated then
> 	            Exit;
> 	        until BufferLen = 0;
> 	      finally
> 	        CloseFile(f);
> 	      end;
> 	    finally
> 	      InternetCloseHandle(hURL);
> 	    end;
> 	  FINALLY
> 	    InternetCloseHandle(hInet);
> 	  END;
> 	END;
>
>
> ....main program...
>
> DownloadThread.Create(gTheURLString, dest);
>
> </pre>
>
>
> -----Original Message-----
> From: "Sven Barth"<pascaldragon at googlemail.com>
> Sent: Monday, February 25, 2013 11:51am
> To: lazarus at lists.lazarus.freepascal.org
> Subject: Re: [Lazarus] WinINet STDCALL callback crash
>
> On 25.02.2013 17:42, matthew at accordancebible.com wrote:
>> Ludo, Hans-Peter,
>>
>> Many thanks for your insights.
>>
>> Ludo, I am reviewing the msdn code example you posted; I have read quite a few but don't remember seeing that one.
>>
>> Hans-Peter, our project has so far been able to avoid the additional complexity posed by threads.  It would be with reluctance that we would turn to threading; however, if experimenting with Ludo's post content (see below) does not work, then would will use threads.  Might you know of an example implementation or code for asynchronous HTTP and/or FTP downloads?
> In principle you could do it as follows:
>
> === example begin ===
>
> type
>     TDownloader = class(TThread)
>     public type
>       TCallback = procedure(aSender: TDownloader; aData: TStream) of object;
>     private
>       fCallback: TCallback;
>       fUrl: String;
>     protected
>       procedure Execute; override;
>     public
>       constructor Create(const aUrl: String; aCallback: TCallback);
>     end;
>
> constructor TDownloader.Create(const aUrl: String; aCallback: TCallback);
> begin
>     // check that aCallback and aUrl are given and set up local variables
>     // ...
>
>     FreeOnTerminate := True;
>     inherited Create(False);
> end;
>
> procedure TDownloader.Execute;
> var
>     ms: TMemoryStream;
> begin
>     ms := TMemoryStream.Create;
>     try
>       // here you use what ever synchronous API you want and store it's
> result in ms
>
>       // when all data is received
>       fCallback(Self, ms);
>     finally
>       ms.Free;
>     end;
> end;
>
> // somewhere else
>
> TDownloader.Create('http://example.org/whatever', @MyCallback);
> // fire and forget
>
> === example end ===
>
> Note: if you want to be able to cancel the download (which could be done
> by calling the thread's "Terminate" method which sets "Terminated" to
> "True") you should not set "FreeOnTerminate" to "True", check for
> "Terminated" in the thread's "Execute" method and capture the instance
> returned by "TDownloader.Create".
>
> Regards,
> Sven
>
> --
> _______________________________________________
> Lazarus mailing list
> Lazarus at lists.lazarus.freepascal.org
> http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
>
>
>
>
> --
> _______________________________________________
> Lazarus mailing list
> Lazarus at lists.lazarus.freepascal.org
> http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus




More information about the Lazarus mailing list