Synapse and string problems with HTTPSend in Delphi 2010 - delphi

I have been trying to get to the bottom of this problem off and on for the past 2 days and I'm really stuck. Hopefully some smart folks can help me out.
The issue is that I have a function that I call in a thread that downloads a file (using Synapse libraries) from a website that is passed to it. However, I've found that every once in a while there are sites where it will not pull down a file, but wget or Firefox/IE will download it without issue.
Digging into it, I've found some curious things. Here is the relevant code:
uses
//[..]
HTTPSend,
blcksock;
//[..]
type
TMyThread = class(TThread)
protected
procedure Execute; override;
private
{ Private declarations }
fTheUrl: string;
procedure GetFile(const TheUrl: string);
public
property thrd_TheUrl: string read fTheUrl write fTheUrl;
end;
implementation
[..]
procedure TMyThread.GetFile(const TheUrl: string);
var
HTTP: THTTPSend;
success: boolean;
sLocalUrl: string;
IsSame : boolean;
begin
HTTP := THTTPSend.Create;
try
HTTP.UserAgent :=
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)';
HTTP.ProxyHost := 'MYPROXY.COM';
HTTP.ProxyPort := '80';
sLocalUrl :=
'http://web.archive.org/web/20071212205017/energizer.com/usbcharger/download/UsbCharger_setup_V1_1_1.exe';
IsSame := SameText(sLocalUrl, sTheUrl); //this equals True when I debug
///
///
/// THIS IS WHERE THE ISSUE BEGINS
/// I will comment out 1 of the following when debugging
///
HTTP.HTTPMethod('GET', sLocalUrl); // ----this works and WILL download the file
HTTP.HTTPMethod('GET', sTheUrl); // --- this always fails, and HTTP.ResultString contains "Not Found"
success := SysUtils.UpperCase(HTTP.ResultString) = 'OK';
if HTTP.ResultCode > 0 then
success := True; //this is here just to keep the value around while debugging
finally
HTTP.Free;
end;
end;
procedure TMyThread.Execute
begin
//fTheURL contains this value: http://web.archive.org/web/20071212205017/energizer.com/usbcharger/download/UsbCharger_setup_V1_1_1.exe
GetFile(fTheUrl);
end;
The problem is that when I assign a local variable to the function and give it the URL directly, everything works. However, when passing the variable into the function, it fails. Anyone have any ideas?
HTTP.HTTPMethod('GET', sLocalUrl); // ----this works and WILL download the file
HTTP.HTTPMethod('GET', sTheUrl); // --- this always fails, and HTTP.ResultString contains "Not Found"
I'm using the latest version of Synapse from their SVN repository (version from 2 days ago).
NOTE: The file I am attempting to download is known to have a virus, the program I am writing is meant to download malicious files for analysis. So, don't execute the file once you download it.
However, I'm using this URL b/c this is the one I can reproduce the issue with.

Your code is missing the crucial detail how you use TMyThread class. However, you write
every once in a while there are sites where it will not pull down a file, but wget or Firefox/IE will download it without issue.
which sounds like a timing issue.
Using the local variable works every time. Using the function parameter works only some of the time. That may be caused by the function parameter not containing the correct URL some of the time.
You need to be aware that creating a non-suspended thread may result in it starting to execute immediately (and possibly even to complete), before the next line after the construction call has even started to execute. Setting any property of the thread object after the thread has been created may therefore not work, as the thread execution may be past the point where the property is read. The fTheUrl field of the thread object will be an empty string initially, so whether the thread downloads the file will depend on it being set before.
Your fTheUrl field isn't even protected by a synchronization primitive. Both the thread proc and the code in the main thread can access it concurrently. Sharing data in this way between threads is an unsafe thing to do and may result in any thing from wrong behaviour to actual crashes.
If your thread is really used to download a single file you should remove the write access to the property, and write a custom constructor with a parameter for the URL. That will properly initialize the field before the thread starts.
If you are downloading several files in your program you should really not create a thread for each. Use a pool of threads (may be only one even) that will be assigned the files to download. For that a thread property is the right solution, but then it will need to be implemented with synchronization, and the thread should block when no file is to be downloaded, and unblock when the property is set. The download thread (or threads) would be consumer(s) in a producer-consumer implementation. Stack Overflow has questions and answers regarding this in the Delphi tag, in particular in the questions where alternatives to Suspend() and Resume() are discussed.
One last thing: Don't let unhandled exceptions escape the Execute() method. I'm not sure about whether Delphi 2010 handles these exceptions in the VCL, but unhandled exceptions in a thread may lead to problems like app crashes or freezes.

Well, I'm almost embarrassed to report it, but I owe it to those that took the time to respond.
The issue had nothing to do with Synapse, or TThread, but instead had everything to do with the fact that the URL is case-sensitive!
In my full application, I had a helper function that lowercased the URL (for some reason). I removed that and everything started working again...

Please update last Synapse Revision 127.

Related

Delphi 7 Occasional deadlock changing TLabel.Font.Style from IdHTTPListener event

Wondered if anyone could help with a really tricky sporadic (not consistently reproducible) issue - possibly to do with threads. I'm in Delphi 7 (it's old code...), running an IdHttpListener (Indy). Code below is copied from a large cumbersome app - but hopefully enough to explain. An incoming HTTP request runs the following event - where web_lock is a TCriticalSection I define at the top. I do it in a critical section as the web requests causes changes that I need to be atomic.
procedure TFWebServer.WebServerCommandGet(Thread: TIdPeerThread;
RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
var S,PageString : string;
begin
web_lock.Acquire;
PageString:=' ';
if (copy(RequestInfo.Document,1,15)='/_Request_Part_') then begin
s:=copy(RequestInfo.Document,16,length(RequestInfo.Document));
FMainGui.doFunction(s);
PageString:='OK';
end; // Lots more else cases here...
ResponseInfo.ContentType:='text/plain';
ResponseInfo.ResponseNo:=200;
ResponseInfo.ContentStream:=TMemoryStream.Create;
ResponseInfo.ContentStream.Write(PageString[1],length(PageString)*
sizeof(PageString[1]));
ResponseInfo.ContentLength:=length(PageString)*sizeof(PageString[1]);
web_lock.Release;
end;
and then, my FMainGui.doFunction(s) does something like this:-
procedure doFunction(s : String);
var i,j : integer;
begin
i:=strtoint(s);
for j:=1 to Pages do begin // Pages is dynamic - but correctly set
if (j=i) then // Pagelabs[j] is always a visible TLabel
Pagelabs[j].Font.Style:=[fsBold]
else Pagelabs[j].Font.Style:=[];
end;
end;
Bit simplified - Pagelabs is a set of dynamically created TLabels that I display on a page, and the one you've selected using the web request gets made bold.
THE PROBLEM: Occasionally, and unpredictably, I get some kind of deadlock dealing with the web request - it just freezes, with circular swirly mouse pointer, and doesn't recover. If I'm debugging it in Delphi, the call stack is empty, and I can only step through the assembly code - afraid I'm not up to working what that means! I tracked it down to the Pagelabs[j].Font.Style:=[fsBold] line above, by writing one line text files between every single line of code... so while the error was sporadic, when it did occur, it was always that line it locked up on.
I appreciate this is a snippet of a large app, but is there anything obvious I'm doing wrong? Eg - should it be safe to change GUI properties from a thread triggered by the HTTP Listener? Or is there something different I should be doing?
Any ideas much appreciated,
Thanks,
Wes
You cannot manipulate UI controls from worker threads, only from the main GUI thread!
You have two options:
You may block your HTTP threads and temporary switch to main thread by TThread.Synchronize() procedure. Like in this example:
http://docwiki.embarcadero.com/CodeExamples/Seattle/en/Synchronize_(Delphi)
You may prefer delayed out-of-sync execution via
Windows messages ( use PostMessage and avoid SendMessage )http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
AsyncCall library http://andy.jgknet.de/blog/bugfix-units/asynccalls-29-asynchronous-function-calls/
thread-queues approach with something like OmniThreadsLibrary
First option might slow you down, for any long processing in the GUI thread would also freeze your HTTP handlers.
Second options would require making a "snapshot" copy of any required data and passing it along with the deferred call request ( as the VCL update code might be executed at any random time AFTER (or during) the HTTP handler (or several HTTP handlers). It would be normal when several HTTP handlers would request GUI updates and you would have to check which of them passed the most recent data and skip other requests for example.

How to use Lookupqueue?

I use Delphi 2007 and I try to find out how to ask Windows (XP, Server 2003 or 2008) if a named MSMQ queue is installed. I have found this but it is in C++ so it is not easy to use from Delphi. Example, I have an installed queue named '.\private$\nctsinqueue'. It works fine to use it by:
var
QueueInfo : IMSMQQueueInfo2;
begin
QueueInfo := CoMSMQQueueInfo.Create;
The problem is that in some installations of Windows where my application is installed this queue does not exists. It depend of the preferences if a queue is needed. So I want to ask Windows if a named queue is installed and in that case I can go on with the code above.
EDIT:
Tried this code
function Test: Boolean;
var
QueueInfo : IMSMQQueueInfo2;
begin
Result := True;
QueueInfo := CoMSMQQueueInfo.Create;
QueueInfo.PathName := '.\private$\nonexistingqueue';
FQueue := QueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
end;
And it raise an exception on the last line. I can of course have a try/except here and return False in that case but I don't like to have exceptionhandling for this. I want to ask WinApi or something if the queue exists. Queue.IsOpen that kobik suggest only says if an existing queue is opened. It must of course exist before it can be opened.
Edit2:
I take a more practical approach to this, so I solved it with ini-files for my application.
It tries to open only if the queue is present in the ini-file.
Disadvantage is of course that the ini-file must be in sync with the queues in the system, but that part is rather static.

Application freezes on call to SetLength

This question might or might not solve my problem - but I hope to learn how Delphi/Windows can behave in a way which can cause this.
I have an application which uses a 3rd party component to load an Outlook .msg file.
In some cases (specific mails) the application freezes when calling SetLength (inside of the component, I have the source code).
This happens sometimes when setLength is called inside of a procedure which loads the properties from the file (stream). It happens the exact same place on the same mail - and can be reproduced every time.
Obviously the component does a lot of stuff and it is probably a sideeffect of some of this. However, the mails contains confidential data which I cannot send to the developer of the 3rd party component, so I cannot send it to him to debug it.
The program is running under windows XP on a domain.
The curious thing is that it only happens when the user running the program is not set to be administrator on the local machine.
ms := TMemoryStream.Create;
try
WriteStorageToStream(SubStorage, ms);
ApplyValue(ms, ms.Size)
finally
ms.Free;
end;
procedure ApplyValue(Stream: TStream; brLen: Integer);
var
s: AnsiString;
begin
SetLength(s, brLen); // this freezes it all. brLen=3512
FillChar(s[1], brLen, #0);
Stream.Read(s[1], brLen);
Value := s;
end;
What WriteStorageToStream does exactly is unknown to me, but since we are not manipulating the stream and brLen has an integer value, I assume it's irrelevant.
I'd say it was simple memory overwrite, causing a failure of the memory manager when the SetLength is called which then tries to use the memory management structures. The problem is in WriteStorageToStream(SubStorage, ms);
To find it, use the FastMM debug version with the memory overwrite detection options turned on.
There's absolutely no reason SetLength would freeze on a AnsiString that's only 3512 characters long. How are you sure that it's freezing there and not somewhere earlier (like in WriteStorageToSteam)? Presumably you're stepping through this in the debugger. Does the CPU spike to 100% on that process thread when it's frozen? The fact that it freezes only on certain emails indicates to me that something in the contents of those emails is causing the freeze. The call to SetLength has nothing to do with the contents; it only cares about the length.

ADO error 'Operation cancelled' in Delphi

I have the following code for executing a sql stored procedure that returns multiple resultsets, then reads this result from stream. For background info: it returns xml blocks as strings and then transforms it into complete xml.
It has worked well over a year but now i have a case that results in error message: Operation cancelled. Debugger shows: Project x raised exception class EOleException with message Operation cancelled.
I have no idea whats causing it. Any help or suggestions would be great.
const
adExecuteStream = $00000400; //Indicates that the results of a command execution should be returned as a stream.
var
objCmd, InputStream, XML, XSLT, Template, Processor, objConn, strmResults : Variant;
ATStreamClass : TMemoryStream;
Adapt : TStreamAdapter;
OutputStream: IStream;
objCmd := CreateOLEObject('ADODB.Command');
objCmd.ActiveConnection := dmABaasMock.dbRaAndm.ConnectionObject;
objCmd.CommandType := adCmdText;
objCmd.CommandText := <sp proc name with params>;
strmResults := CreateOLEObject('ADODB.Stream');
strmResults.Open;
objCmd.Properties['Output Stream'] := strmResults;
objCmd.Execute(EmptyParam, EmptyParam, adExecuteStream); // HERE COMES THE EXCEPTION
strmResults.Position := 0;
xmlMemo.text := strmResults.ReadText;
Difficult to guess what is going wrong without seeing more.
Three things you can do to get more debugging information:
What has changed between when it was working and now? OS version (or ADO), DB, stored proc,...
Is the stored proc working when launched directly in the SQL environment?
Could you rewrite your code to use the regular ADO components instead of doing late binding and get the results in DataSets instead of an ADODB.Stream OleObject. Only having Variant objects doesn't help much if you need to debug and drill down in the code? You can't debug a black box...
I recently had some strange error message when connecting to ADO, and not having the connection string right.
I'm not 100% sure it was the same error message (sorry, I forgot to take a screenshot back then), but if it is, then this might help:
In .NET, when you connect to ADO and use integrated security, you can specify Integrated Security="True", but using the native providers (not only in Delphi, but from any native environment), you will have to specify Integrated Security="SSPI".
I got into the situation because I was fiddling with connection strings (to connect from Delphi native win32 to a server that I previously connected to from .NET) and forgot to copy just the relevant parts.
--jeroen

Too many open files

I get an EInOutError with message 'Too many open files' when executing this code block repeatedly for some time from a number of client threads:
var InputFile : Text;
...
Assign (InputFile, FileName);
Reset (InputFile)
try
// do some stuff
finally
CloseFile (InputFile);
end;
The number of client threads is approximately 10, so only 10 files can be open at any time. Is there any possibility that Delphi refuses to close files right away? Can I ensure that it does? Or am I making a mistake here? This is the only place where I open files and the try..finally block should guarantee that opened files get closed, shouldn't it?
REEDIT: forget the edit
I can only advise you to use the more "modern" facilities for dealing with files. I don't know whether there is a limit of open files using the Windows API, but I just tested and could easily open 1000 streams in parallel:
procedure TForm1.Button1Click(Sender: TObject);
var
Strs: TList;
i: integer;
begin
Strs := TList.Create;
try
for i := 1 to 1000 do begin
Strs.Add(TFileStream.Create('D:\foo.txt', fmOpenRead or fmShareDenyWrite));
end;
finally
FreeObjectList(Strs);
end;
end;
I have never understood why people still use untyped files instead of TStream and its descendants in new code.
Edit: In your comment you write that you only want to read plain text files - if so just create a TStringList and use its LoadFromFile() method.
You aren't running this on an older Windows 9x based computer, are you? If so, you might be running into a DOS filehandle problem.
Delphi closes immidiately in the CloseFile. Your example code seems to be correct.
Try again without anything between try and finally.
There IS a thread safety issue here although I can't see how it could cause the problem.
The problem is Reset uses the global FileMode variable.
As for client threads--are you sure they aren't leaking away on broken connections or something?
Might be useful to put some debug output alongside the Reset and the Close so you can see how long each thread has the file open for.
Do you really need threads? It sounds like they are causing you problems. Your code would be easier to debug without them.
This code should work just fine. There are no known problems related to using files from threaded code (as far as I know). We use such idioms fairly regularly and everything works fine.
I would suggest adding some logging code (before Assign and CloseFile) to see if a) close is executed and b) you really have only 10 threads running. Maybe your thread terminating logic is faulty and CloseFile never executes.

Resources