我试图在新线程中使用openDialog,但它做了如此奇怪的行为..
如果我将if opendialog.execute放入创建构造函数中,如下所示:
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;
opendialog正常打开但是当我把它放在线程的执行生成器中时它根本没有打开!!
我是初学者,所以任何人都可以向我解释发生了什么?
提前致谢。
[编辑]
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.
答案 0 :(得分:7)
当你在构造函数中调用它时它起作用,因为构造函数在调用线程(即主线程)的上下文中运行,而Execute()在工作线程的上下文中运行。 VCL不是线程安全的,特别是UI组件,如果在主线程之外正常工作,则很少。如果要在线程中显示打开的对话框,请使用TThread.Execute()
方法:
1)调用TThread.Synchronize()
访问主线程上下文中的TOpenDialog
。
2)直接调用Win32 API GetOpenFileName()
函数。正确使用时,可以在线程中安全地使用API对话框。
答案 1 :(得分:0)
我刚刚在Delphi XE2中遇到类似的情况,但我想它也可能在2009年发生。
Delphi被提升为使用新的Windows Vista打开/保存对话框,这些对话框是基于COM的组件,而不是旧的扁平C风格API。 https://msdn.microsoft.com/library/windows/desktop/bb776913.aspx
我正在添加调试日志记录功能,如果尚未设置转储文件名,则用于调用PromptForFileName
。这个功能从来没有做过。
我追溯到Delphi RTL / VCL内部,并在function TCustomFileSaveDialog.CreateFileDialog
中到达Dialogs.pas
。
上述函数正在调用Microsoft COM API,但随后 - 哎呀! - 只是抑制了可能返回的所有错误。我在Delphi调试器中使用了CPU窗口,看到EAX
寄存器出现$800401f0
错误,代表“COM尚未初始化”的情况。
https://msdn.microsoft.com/en-us/library/cc704587.aspx
我确信所说的功能在程序的其他地方工作得很好,所以我认为 - 对我来说意外 - 在一个单独的线程中执行。情况就是这样。在你的情况下,你知道你已经有多线程问题了,我认为你可以尝试直接解决方案,而不是使用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;