2

に表示されません。..openDialogのは、私は新しいスレッドでは、openDialogを使用しようとしたが、それはとても奇妙な振る舞いをしたのDelphiマルチスレッドのアプリケーション

私はこのようなコンストラクタを作成して、その後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. 
+1

に代わり、回避策の直接的な解決策を試すかもしれないと思う私はあなたが実際にあるダイアログからファイルの一覧を取得したいと思って、すでに知っていますかメインスレッドを開始し、ハッシュを行うワーカースレッドを開始します。スレッドからステータスを更新する必要がある場合は、スレッドを同期させるか、メインスレッドにキューイングします。 –

答えて

7

それはコンストラクタが呼び出し側スレッドのコンテキストで実行されますので、あなたは、コンストラクタでそれを呼び出したとき(つまりメインスレッド)、一方、実行()は、のコンテキストで実行されます動作しますワーカースレッド。 VCLはスレッドセーフではなく、UIコンポーネントは特にメインスレッド外で正しく動作することはめったにありません。スレッドに開いたダイアログを表示する場合は、TThread.Execute()メソッドを

のいずれかの方法で呼び出すことができます。を呼び出して、メインスレッドのコンテキスト内でTOpenDialogにアクセスします。

2)代わりにWin32 API GetOpenFileName()関数を直接呼び出します。 APIダイアログは、正しく使用するとスレッドで安全に使用できます。

+1

私はオプション2が良いアイデアだとは確信していません。ファイルダイアログは通常モーダルです。メインスレッドに存在する残りのUIをどのように無効にしますか? –

+0

(私は理解してなかったので)私はあなたが言ったことをやった、それは動作しますが、それはまだ私はこの遅れそうTOpenDialog で開かれたファイルのハッシュスレッドが、私はそれがしばらくぶら下がっていると思いながら、私は通常、GUIを使用することができるのを防ぐことができますどのようにGUIがハングアップしますMD5it関数 を処理していただきありがとうございます。 – Rain

+2

@Tattah - 編集では、「実行」の「同期」のみですべてのコードがメインスレッドで実行されます。おそらくメインスレッドにダイアログを表示し、スレッドにファイルをMD5して、リストビューを同期させます。 –

0

私はちょうどデルファイXE2で同様のケースをヒットしましたが、私はそれも2009年に起こることができると思います。

Delphiは、古いフラットCスタイルのAPIではなくCOMベースのコンポーネントである新しいWindows Vistaのオープン/セーブダイアログを使用するように改良されました。 https://msdn.microsoft.com/library/windows/desktop/bb776913.aspx

デバッグログ機能を追加していましたが、ダンプファイル名がまだ設定されていない場合はPromptForFileNameを呼び出していました。この機能は決して何もしませんでした。

デルファイのRTL/VCL内部をトレースし、Dialogs.pasfunction TCustomFileSaveDialog.CreateFileDialogに達しました。

上記の関数はMicrosoft COM APIを呼び出していましたが、その後は - oops! - 返される可能性があるすべてのエラーを抑制しました。私はDelphiデバッガでCPUウィンドウを使用し、$800401f0エラーを持つEAXレジスタを見ました。これは '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; 
+0

メインのGUIスレッドはすでに 'Co(Un)Initialize'を呼び出しています。ワーカースレッドのコンテキストでダイアログを呼び出す場合は、手動で呼び出す必要があります。ワーカースレッドでCOMオブジェクトを使用することは、COMオブジェクトのルールに従う限り、安全です。 –

+0

私は、曖昧な「VCLは設計されていません」ではなく、作成者のコードが失敗した具体的な理由を示しています。また、TOpenDialogにCOMが必要なことも直感的ではありません。ところで、COMアパートメントは、VCLアプリケーションのために常に初期化されていることをDelphi docs明示保証のどこかに示しています(単純なHello Worldを含む)?私は疑問に思う。 'Synchronize'回避策を使用することは明らかに利点もありますが、より多くのオプションが優れています。 –

関連する問題