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.
Related
Our program is something like this:
C:\Main
C:\Main\Utils
C:\Main\App.exe
For C:\Main and all subfolders, we have all privileges for Everyone set to allow Full Control.
In our code, we create a .bmp file:
BmpSt := TMemoryStream.Create;
BmpSt.SetSize(Length(dta));
BmpSt.WriteBuffer(dta[1],Length(dta));
BmpSt.Seek(0,soFromBeginning);
Bmp := '\Main\Util\' + FormatDateTime('YYYYMMDDHHNNSS',Now) + '.bmp';
BmpSt.SaveToFile(Bmp);
if not DeleteFile(Bmp) then begin
ShowMessage('No');
end;
If I don't run App.exe as an administrator, it will never delete the file. However, when I do run it as an administrator, it deletes the file just fine.
Privileges are set to Full Control for Everyone.
I know this might not exactly be a Delphi question since its heavily dealing with Windows UAC, but this code worked fine when our program was in Delphi 5, now we are building it in Delphi 10.
We are also getting similiar issues using a TPrinter object to insert the above .bmp onto a print page, but I'm confident that the issues with DeleteFile will solve those.
Right after using DeleteFile() you can use GetLastError() to get what went wrong and trigger an exception for it with CheckOSError(). Once you know why it is failing you can troubleshoot further.
if not deletefile(bmp) then begin
CheckOSError(GetLastError);
end;
Well, here I am again, trying to resolve an old problem.
Briefly, I get an AV when I try to free a modal form which does not have any owner, and didnt have been freed before.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.vtipo := vtipo;
frmItensVenda.vcontrole := strtoint(edit1.Text);
frmItensVenda.Ednota.Text := Edit5.Text;
frmitensvenda.lbvend.Caption := combobox3.Text;
frmitensvenda.lbnome.Caption := combobox1x.Text;
frmItensVenda.limite := limite;
if label10.caption <> '' then
frmItensVenda.vcli := strtoint(label10.caption);
frmItensVenda.ShowModal;
Frmitensvenda.Close;
frmItensVenda.Free;
If I just activate it and then close(without doing a thing), no AV happens. Putting a break-point before the 'free' command, it shows me the variable inside the form if I put the mouse cursor on it.
But if I insert one item in the grid, using the breakpoint at the same place, when I move the cursor to the same line, it doesnt show the variables anymore, but says 'inacessible value'.
And if I proceed running the code, as the next line has the 'free' command I get an AV.
What makes me believe there is some piece of code on that procedure that is doing something unexpected to the code, but I can tell you that there is no 'free' or similar command to the form in question there.
My solution(temporary) was to just comment the '.free' command, but if I run MadException I got a memory leak when I close the application (hey, anything is better than this EAccessViolation thing for me right now..)
Any sugestions?
Ok, found the answer, finally.
The problem was a global array.
It was declared
vm1 : array[1..100] of currency;
but it was assigned a value at position 0.
To my despair, there was no error when the variable was assigned, just when I tried to free the form.
So simple when you find it.. (!!!)
Well, at least I figured it out. Thanks everyone for the support!
OP : frmItensVenda is a global variable automatic created(but not initialized).
I see you do frmItensVenda := TfrmItensVenda.Create(nil);
Look for Application.CreateForm(TfrmItensVenda, frmItensVenda); in your .dpr file.
If it is there you creating a new instance !
{$R *.RES}
begin
Application.Initialize;
Application.Title := 'AServer';
...
Application.CreateForm(TfrmItensVenda, frmItensVenda);
...
Application.Run;
end.
And yes, dynamic form management is a must really (expecially in large applications.).
At trouble with large Forms, part of my solution was to, as much as possible to create dynamic.
Only when they are needed and then free them straight after.
frmItensVenda := TfrmItensVenda.Create(nil);
frmItensVenda.ShowModal;
OP : My solution(temporary) was to just comment the '.free' command,
don't do that : use instead
frmItensVenda.Release;
The "Method Release" removes the form and frees its associated memory.
Release procedure;
description
With release, you can remove the form from memory.
Release is the form to be taken until after the execution of event handlers of the form and its child components is completed.
In all event handlers release should be used instead free to avoid access violations.
The cases where you need to use Release are times when you're in the middle of an event handler (eg, OnClick), where further processing after the event will have to access the form.
In that case calling Release instead posts a WM_RELEASE message which doesn't free the event until the event handler is done and control has returned to the message pump (ProcessMessages/Application.Run).
Reading the delphi help though, it is recommended that you use the release command.
As for the Release v.s Free method. My understanding is that "Release" is specific to forms, and allows form-related even-handlers to finish before freeing resources.
Whereas "Free" is a generic method of freeing an object from memory (so should also work with forms.)
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.
Can someone enlighten me on handling the database connection (and errors) using try finally ?
What would be the best practice ?
Seen various styles but I wonder what would be the best approach.
Should opening of the tables be put in TRY block or just the main connection
string ?
Since I usually put my database (absolute database,access..) in my exe folder
I was wondering about the best approach on this...
Or first check for file like ...
if (FileExists(sDatabasePath)) then begin
ADOConnection1.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+sDatabasePath+';Persist Security Info=False';
try
ADOConnection1.Connected:=True;
ADOTable1.Open;
except
ShowMessage ('cant access the database !');
end;
end;
???
Comments:
Never swallow exceptions, like you essentially do in your ShowMessage case. The code calling your procedure would have no way of knowing something went wrong. Only handle errors if you can fix them, or let them bubble-up the application error handler, where they'll be displayed for the user.
Depending on how your code works, you might want to protect the connection to the database with a try-finally so you're disconnected once the job is done. I don't do that, I usually keep the connection open for the life of the application.
Depending on what you do with the ADOTable1 you might want to make sure it gets closed once you're done using it with an other try-finally block. I usually do that because I don't use Db aware GUI controls and I'm a control-freak. I also handle the transaction manually (start transaction / commit / rollback)
Don't ignore errors. if your database doesn't exist (ie: FileExists() returns false), code calling your procedure doesn't know a thing, nor does the user.
Here's how I'd re-write your code:
if (FileExists(sDatabasePath)) then
begin
ADOConnection1.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+sDatabasePath+';Persist Security Info=False';
ADOConnection1.Connected:=True;
try
ADOTable1.Open;
try
// Do non-GUI database stuff here.
finally ADOTabel1.Close;
end;
finally ADOConnection1.Connected := False;
end;
end
else
raise Exception.Create('Database file not found');
If I cannot open the database I terminate the application - not much you can do without database access unless you specifically build an architecture that handles this.
Other than this, just let the default application error handler handle the error, since it would be pretty unexpected anyway.
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.