OpenDialog не отображается в многопоточном приложении Delphi - PullRequest
2 голосов
/ 16 июля 2011

Я пытался использовать 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;

openialog открывается нормально, но когда я помещаю его в исполняющий файл потока, он вообще не открывается !!

Я новичок в темах, так что кто-нибудь может объяснить мне, что случилось?

Заранее спасибо.

[Редактировать]

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 ]

7 голосов
/ 17 июля 2011

Это работает, когда вы вызываете его в конструкторе, потому что конструктор выполняется в контексте вызывающего потока (то есть основного потока), тогда как Execute () запускается в контексте рабочего потока.VCL не является потокобезопасным, и компоненты пользовательского интерфейса, в частности, редко, если вообще работают правильно, вне основного потока.Если вы хотите отобразить открытое диалоговое окно в потоке, то используйте метод TThread.Execute():

1) вызов TThread.Synchronize() для доступа к TOpenDialog в контексте основного потока.

2) вместо этого вызовите функцию Win32 API GetOpenFileName() напрямую.При правильном использовании диалоги API можно безопасно использовать в потоках.

0 голосов
/ 29 января 2018

Я только что столкнулся с аналогичным случаем в 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;
...