简体   繁体   English

OpenDialog 未出现在 Delphi 多线程应用程序中

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM