OpenDialog does not show up in Delphi MultiThreaded application

99封情书 提交于 2019-12-04 16:43:49

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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!