OpenDialog does not show up in Delphi MultiThreaded application
i tried to use the openDialog in new thread but it made so strange behavior ..
if i put the if opendialog.execute then in the create constructor like this :
constructor TChatMemberThread.Create(Name: string);
begin
inherited Create(True);
FName := Name;
FreeOnTerminate := True;
Opendialog := TOpenDialog.create(nil);
if opendialog.execute then
for 0 to opendialog.filescount do
somecodeishere
end;
end;
the opendialog open normally but when i put it in the execute producer of the thread it didn't open at all !!
i'm so beginner in threads so can any one explain for me what happened ?
Thanks in advance .
[Edit]
unit Unit1;
interface
uses
Classes,Dialogs,ComCtrls,SysUtils,DCPcrypt2, DCPmd5;
type
TOpenThread = class(TThread)
private
{ Private declarations }
OpenDlG : TOpenDialog;
LI : TListItem;
Procedure Openit;
Function MD5it(Const filename : string ):String;
protected
procedure Execute; override;
Public
Constructor Create;
Destructor Destroy;Override;
end;
implementation
uses Main;
{ TOpenThread }
Constructor TOpenThread.Create;
begin
inherited Create(True);
opendlg := Topendialog.Create(nil);
opendlg.Filter := 'All Files | *.*';
openDlg.Options := [OfAllowMultiSelect];
openDlg.InitialDir := GetCurrentDir;
end;
Destructor TOpenThread.Destroy;
begin
OpenDlg.Free;
inherited;
end;
Function TOpenThread.MD5it(Const filename : string ):String;
var
hash : TDCP_MD5 ;
Digest: array[0..15] of byte;
Source: TFileStream;
i: integer;
s: string;
begin
Source:= nil;
try
Source:= TFileStream.Create(filename,fmOpenRead); // open the file specified by Edit1
except
MessageDlg('Unable to open file',mtError,[mbOK],0);
end;
if Source <> nil then
begin
H开发者_StackOverflowash:= TDCP_MD5.Create(nil); // create the hash
Hash.Init; // initialize it
Hash.UpdateStream(Source,Source.Size); // hash the stream contents
Hash.Final(Digest); // produce the digest
Source.Free;
s:= '';
for i:= 0 to 15 do
s:= s + IntToHex(Digest[i],2);
Result := s;
end;
Hash.Free;
end;
Procedure TOpenThread.Openit;
var
I: Integer;
begin
if opendlg.Execute then
begin
for I := 0 to openDlg.Files.Count - 1 do begin
LI := Form1.LV1.Items.Add;
LI.Caption := ExtractFileName(openDlg.Files[i]);
LI.SubItems.Add(MD5it(openDlg.Files[i]));
LI.SubItems.add(openDlg.Files[i]);
end;
//SB.Panels[0].Text := ' '+IntToStr(LV1.Items.Count)+' File(s)';
OpenDlg.Free;
end;end;
procedure TOpenThread.Execute;
begin
{ Place thread code here }
Synchronize(OpenIt);
end;
end.
It works when you call it in the constructor because the constructor runs in the context of the calling thread (ie the main thread), whereas Execute() runs in the context of the worker thread. The VCL is not thread-safe, and UI components in particular rarely if ever work correctly outside of the main thread. If you want to display an open dialog in a thread, then have your TThread.Execute()
method either:
1) call TThread.Synchronize()
to access the TOpenDialog
within the context of the main thread.
2) call the Win32 API GetOpenFileName()
function directly instead. API dialogs can be safely used in threads when used properly.
I just hit a similar case in Delphi XE2 but I suppose it can happen in 2009 too.
Delphi was uplifted to use new Windows Vista open/save dialogs, which are COM-based components instead of old flat C-style API. https://msdn.microsoft.com/library/windows/desktop/bb776913.aspx
I was adding a debug logging function, and it used to call PromptForFileName
if the dump file name was not set yet. The function never did a thing.
I traced into Delphi RTL/VCL internals and reached function TCustomFileSaveDialog.CreateFileDialog
in Dialogs.pas
.
The said function was calling into Microsoft COM API, but then - oops! - just suppressed all the errors that could be returned. I used CPU Window in the Delphi Debugger and saw EAX
register having $800401f0
error, which stands for 'COM was not initialized yet' situation.
https://msdn.microsoft.com/en-us/library/cc704587.aspx
I knew for sure that the said function worked perfectly in other places of the program, so I supposed it was - unexpectedly for me - executing in a separate thread. That was the case. In your case you DO KNOW already you have multithreading issues, and I think you may try the direct solution, instead of the workaround with Synchronize
uses ActiveX, Windows;
constructor TChatMemberThread.Create(Name: string);
var COM_Init_Here: Boolean;
begin
inherited Create(True);
FName := Name;
FreeOnTerminate := True;
COM_Init_Here := S_OK = CoInitialize(nil); // ***
try // ***
Opendialog := TOpenDialog.create(nil);
if opendialog.execute then
for 0 to opendialog.filescount do
somecodeishere
end;
finally // ***
if COM_Init_Here then CoUnInitialize(); // ***
end; // ***
end;
精彩评论