[英]OpenDialog does not show up in Delphi MultiThreaded application
i tried to use the openDialog in new thread but it made so strange behavior..我试图在新线程中使用 openDialog 但它做出了如此奇怪的行为..
if i put the if opendialog.execute then in the create constructor like this:如果我将 if opendialog.execute then 放在 create 构造函数中,如下所示:
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 !! opendialog 正常打开,但是当我把它放在线程的执行生产者中时,它根本没有打开!
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
Hash:= 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.当您在构造函数中调用它时它会起作用,因为构造函数在调用线程(即主线程)的上下文中运行,而 Execute() 在工作线程的上下文中运行。 The VCL is not thread-safe, and UI components in particular rarely if ever work correctly outside of the main thread.
VCL 不是线程安全的,尤其是 UI 组件很少在主线程之外正常工作。 If you want to display an open dialog in a thread, then have your
TThread.Execute()
method either:如果要在线程中显示打开的对话框,则使用
TThread.Execute()
方法:
1) call TThread.Synchronize()
to access the TOpenDialog
within the context of the main thread. 1) 调用
TThread.Synchronize()
以在主线程的上下文中访问TOpenDialog
。
2) call the Win32 API GetOpenFileName()
function directly instead. 2)直接调用Win32 API
GetOpenFileName()
function 代替。 API dialogs can be safely used in threads when used properly.如果使用得当,API 对话框可以在线程中安全使用。
I just hit a similar case in Delphi XE2 but I suppose it can happen in 2009 too.我刚刚在 Delphi XE2 中遇到了类似的情况,但我想它也可能在 2009 年发生。
Delphi was uplifted to use new Windows Vista open/save dialogs, which are COM-based components instead of old flat C-style API. Delphi 被提升为使用新的 Windows Vista 打开/保存对话框,这是基于 COM 的组件,而不是旧的平面 C 样式 API。 https://msdn.microsoft.com/library/windows/desktop/bb776913.aspx
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.我正在添加一个调试日志记录 function,如果尚未设置转储文件名,它会调用
PromptForFileName
。 The function never did a thing. function 从未做过任何事情。
I traced into Delphi RTL/VCL internals and reached function TCustomFileSaveDialog.CreateFileDialog
in Dialogs.pas
.我追踪到 Delphi RTL/VCL 内部并在 Dialogs.pas 中到达
function TCustomFileSaveDialog.CreateFileDialog
Dialogs.pas
。
The said function was calling into Microsoft COM API, but then - oops.上述 function 正在调用 Microsoft COM API,但随后 - 哎呀。 - 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.我在 Delphi 调试器中使用 CPU Window 并看到
EAX
寄存器出现$800401f0
错误,这代表“COM 尚未初始化”的情况。 https://msdn.microsoft.com/en-us/library/cc704587.aspx 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.我确信上述 function 在程序的其他地方工作得很好,所以我认为它 - 对我来说出乎意料 - 在单独的线程中执行。 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
在您的情况下,您已经知道您有多线程问题,我认为您可以尝试直接解决方案,而不是使用
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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.