multithreadingdelphidelphi-2009fileopendialogtopendialog

OpenDialog does not show up in Delphi MultiThreaded application


i tried to use the openDialog in new thread but it made so strange behavior ..

if i put the if opendialog.execute then in the create constructor like this :

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 !!

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.

Solution

  • 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.