OpenDialog не отображается в многопоточном приложении Delphi
Я пытался использовать 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.
2 ответа
Это работает, когда вы вызываете его в конструкторе, потому что конструктор выполняется в контексте вызывающего потока (то есть основного потока), тогда как Execute() выполняется в контексте рабочего потока. VCL не является потокобезопасным, и компоненты пользовательского интерфейса, в частности, редко, если вообще работают правильно, вне основного потока. Если вы хотите отобразить открытый диалог в ветке, TThread.Execute()
метод либо:
1) звонок TThread.Synchronize()
чтобы получить доступ к TOpenDialog
в контексте основного потока.
2) вызвать Win32 API GetOpenFileName()
вместо этого При правильном использовании диалоги API можно безопасно использовать в потоках.
Я только что столкнулся с аналогичным случаем в Delphi XE2, но, полагаю, это может произойти и в 2009 году.
Delphi был взволнован, чтобы использовать новые Windows Vista диалоги открытия / сохранения, которые являются COM-компонентами вместо старого плоского API в стиле C. https://msdn.microsoft.com/library/windows/desktop/bb776913.aspx
Я добавил функцию ведения журнала отладки, и она использовалась для вызова PromptForFileName
если имя файла дампа еще не было установлено. Функция никогда ничего не делала.
Я проследил во внутренности Delphi RTL/VCL и достиг function TCustomFileSaveDialog.CreateFileDialog
в Dialogs.pas
,
Упомянутая функция вызывала Microsoft COM API, но потом - упс! - просто подавлены все ошибки, которые могли быть возвращены. Я использовал CPU Window в отладчике Delphi и увидел 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;