2

Delphi 7 で記述された Windows フォーム アプリケーションと、MFC を使用して記述された C++ .dll があります。

現在、プログレスバーにユーザーの計算プロセスを表示するために、.dll からメインの実行可能ファイルへの基本的なメッセージ投稿を実装しようとしていますが、いくつかの問題に直面していました。

最初に私のアプローチを説明しましょう。次のような簡単なメッセージを Delphi アプリケーションに登録します。

WM_MSG := RegisterWindowMessage('WM_MSG');

ライブラリ部分でも同じことを行います。

UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));

これで問題ありません。デバッグ時に両側で同じ値を確認できます。

私のライブラリ関数は次のようになります(プログレスバーをテストするためのダミーの例です):

extern "C" __declspec(dllexport) int MyFunction() {  
  UINT nMsgID = RegisterWindowMessage(_T("WM_MSG"));
  HWND hWnd = FindWindow(NULL, "Form1");
  if (hWnd > 0)
    for (int i = 0; i < 100000; i++) {
      int param = ceil(100 * (double) i / (double) 100000);
      PostMessage(hWnd, nMsgID, param, NULL);
    }
  return 1;
}

実行可能なOnMessageイベント:

procedure TForm1.OnMessageEvent(var Msg: tagMSG; var Handled: Boolean);
begin
  Handled := True;
  if Msg.message = WM_MSG then
    ProgressBar1.Position := Msg.wParam
  else Handled := False;
end;

実行可能ファイルからの C++ 関数呼び出し:

procedure TMyFunctionDLL.Execute;
var
  i: Integer;
  tHWND: HWND;
begin
  tHWND := FindWindow(nil, 'mainF');
  i := Func;
end;

最初の問題は、tHWND変数とhWnd変数の値が不可解に異なることです。いくつかの調査の後、私は 3 つの状況を発見しました: 1. 負または正の巨大なhWnd 2. ゼロhWnd 3. 未定義 ('???')

すべての場合において、変数hWndは未使用としてマークされており、それが何を意味するのかわかりません。最も興味深いのは、コードを非常に単純な Delphi 形式 (1 つのユニットのみ) でテストすると、コードが機能することです。この単純な Delphi フォームは、実際のデータが計算される実際の C++ .dll コードでうまく機能します。しかし、一般的な Delphi アプリケーション (多くのユニットでありながら 1 つのフォーム) を使用すると、メイン アプリケーションの OnMessage イベントが C++ dll からのイベントをキャッチしないようです。

したがって、2 つの質問があります。1. hWnd値が常に異なるのはなぜですか。2. メイン アプリケーションをプログレスバーで正しく動作させるにはどうすればよいですか?

私はこれを解決するためにさまざまなアプローチを使用してきました。Application.HandleまたはForm1.Handleを関数パラメーターとして C++ ライブラリに渡すなど。通過中にパラメーター値が変更されたことについてさえ言わずに、それらのどれも機能しませんでした(それは別の質問になるはずです)。また、FindWindow() と PostMessage() の代わりに ::FindWindow() と ::PostMessage() を使用してみました(それらの違いですか?)、どちらも役に立ちませんでした私はすでに一日中状況を改善しようとしていますが、それを解決する方法がわかりません。どんなアイデアでも助けてください。

4

4 に答える 4

3

他の人が述べたことに加えて、より良い設計は、EXE がHWND直接 DLL に渡すようにすることであり、DLL はそれを探しに行く必要はありません。これには、EXE がHWNDDLL がメッセージを送信する先を決定できるという追加の利点があります。AllocateHWnd()そのための専用ウィンドウを作成するために使用します。

これを試して:

UINT nMsgID = RegisterWindowMessage(_T("WM_MSG")); 

extern "C" __declspec(dllexport) int __stdcall MyFunction(HWND hWnd) {   
    if ((nMsgID != 0) && (hWnd != NULL)) {
        for (int i = 0; i < 100000; i++) { 
            int param = ceil(100 * (double) i / (double) 100000); 
            PostMessage(hWnd, nMsgID, param, 0); 
        } 
    }
    return 1; 
} 

.

unit Unit1;

interface

...

var
  DllWnd: HWND = 0;

implementation

var
  WM_MSG: UINT = 0;

procedure TForm1.FormCreate(Sender: TObject); 
begin 
  DllWnd := AllocateHWnd(DllWndProc);
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  if DllWnd <> 0 then
  begin
    DeallocateHWnd(DllWnd);
    DllWnd := 0;
  end;
end; 

procedure TForm1.DllWndProc(var Message: TMessage); 
begin 
  if (Message.Msg = WM_MSG) and (WM_MSG <> 0) then 
    ProgressBar1.Position := Message.WParam
  else
    Message.Result := DefWindowProc(DllWnd, Message.Msg, Message.WParam, Message.LParam); 
end; 

...

initialization
  WM_MSG := RegisterWindowMessage('WM_MSG');     

end.

.

uses
  Unit1;

function DllFunc(Wnd: HWND): Integer; stdcall; external 'My.dll' name 'MyFunction'; 

procedure TMyFunctionDLL.Execute;   
var   
  i: Integer;   
begin   
  i := DllFunc(DllWnd);   
end;   
于 2012-06-07T19:44:54.890 に答える
1

からの結果はFindWindow、ゼロまたはゼロ以外のいずれかになります。ハンドル値は数直線上にありません。それらは単なる別個の値であるため、不等式演算子をそれらに適用することは意味がありません。言い換えると、ハンドル値は負の値に見える可能性があるため、有効なハンドルが常にゼロより大きいと想定しないでください。

ウィンドウハンドルの値が一致しない場合は、他に何も機能しないのも不思議ではありません。メッセージを適切なウィンドウに送信しているかどうかさえわからないため、メッセージの機能をデバッグする立場にはありません。最初にそれを修正することに焦点を当てます。

FindWindow最後の手段としてのみ使用してください。検索条件に一致するウィンドウが複数あるかどうかを検出する機能はありません。常に1つの結果を返します。可能であれば、検索は一切避けてください。代わりに、メッセージを送信するウィンドウを送信者に正確に伝えます。あなたはこれを試みて失敗したと言いますが、私はあなたにその道をもう少し追求することを勧めます。あなたが抱えていた問題は、おそらく呼び出し規約の不一致でした。DLLとホストアプリケーションの両方がstdcallを使用していることを確認してください。


正しいウィンドウにメッセージを送信していることを確認したらメッセージがプログレスバーを正しく操作していない理由を心配することができます。私は少なくとも2つの理由を見ることができます:

  1. DLL関数の実行中は、DLLを呼び出したメインプログラムは実行されません。DLLコードが返されるのを待っています。これは、メインプログラムがメッセージを処理していないことを意味します。DLLは大量のメッセージを投稿していますが、まだ処理されていません。プログラムがメッセージループに戻るまで、これらは処理されません。

  2. Windowsメッセージキューのデフォルトサイズは10,000です。キューに10倍の数のメッセージを投稿し、停止する前に何も処理しないため、開始する前にキューが完全に空であったとしても(キーボードまたはマウスの入力からこの機能をトリガーしている可能性があるため、これはありそうにありません) 、メッセージは10分の1しか表示されません。キューがいっぱいにPostMessageなると、メッセージを破棄するだけです。また、メッセージとともに送信する値は0から100までの整数であるため、意味のある情報を保持するのが101個だけの場合、100,000個を送信するのは無意味です。

于 2012-06-07T18:50:42.820 に答える
0

FindWindow への同一の呼び出しが異なるウィンドウを返す場合、Form1 という名前の複数のウィンドウが必要です。これらの異なるウィンドウに異なる名前を付けて、一意に識別できるようにしてください。

未使用の質問は少し不明確です。おそらく、コンパイラは、tHWND に割り当てられた値が使用されていないため、無意味であることに気付いたということです。

質問が不正確であり、これはおそらくあなたの問題の一部であるという最後のコメントをします。たとえば、すべての変数が使用されていないと言いますが、何を意味するのか明確にはわかりません。より正確で整然としていれば、デバッグはより成功するでしょう。

于 2012-06-07T16:10:17.760 に答える
0

よし、問題は解決したようだ...エクスポートされた関数を宣言するRemyの提案を試した

function MyFunction (fHWND: HWND): Integer; cdecl; external 'My.dll'

Davidがアドバイスしたように、cdecl呼び出し規約を使用します。以前の関数宣言は次のようになりました

TMyFunction = function (fHWND: HWND): Integer;

それが問題だったと思います。ご協力ありがとうございました!

PSさて、どうすれば質問を閉じることができますか?

于 2012-06-09T10:01:00.920 に答える