16

複数の異なる DLL に接続されているメイン プロジェクトを使用するアプリケーションを作成しています。ある DLL ウィンドウから別のウィンドウを開くことができる必要がありますが、DLL は相互に参照できません。

最初の DLL で sendmessage 関数を使用し、そのメッセージを適切な DLL に送信してウィンドウを開くメイン プログラムにリスナーを配置することを提案されました。

しかし、私は sendmessage 機能にまったく慣れておらず、オンラインで見つけた情報から物事をつなぎ合わせるのに多くの困難を抱えています。

誰かが sendmessage 関数を使用する正しい方法 (ある場合) と、リスナーがそのメッセージをどのようにキャプチャするかを教えてください。これまでに得たコードの一部を次に示します。正しい方向に向かっているかどうかはわかりません。

    [DllImport("user32.dll")]
    public static extern int FindWindow(string lpClassName, String lpWindowName);
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    public void button1_Click(object sender, EventArgs e)
    {
        int WindowToFind = FindWindow(null, "Form1");
    }
4

6 に答える 6

15
public static extern int FindWindow(string lpClassName, String lpWindowName);

ウィンドウを見つけるには、ウィンドウのクラス名が必要です。ここではいくつかの例を示します。

C#:

const string lpClassName = "Winamp v1.x";
IntPtr hwnd = FindWindow(lpClassName, null);

私が作成したVBで書かれたプログラムの例:

hParent = FindWindow("TfrmMain", vbNullString)

ウィンドウのクラス名を取得するには、 WinSpyと呼ばれるものが必要です。

ウィンドウのハンドルを取得したら、SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam)関数を使用してウィンドウにメッセージを送信できます。

hWnd、ここでは、FindWindow関数の結果です。上記の例では、これはとにhwndなりhParentます。SendMessageメッセージを送信するウィンドウを関数に指示します。

2番目のパラメーター、は、送信するメッセージのタイプwMsgを示す定数です。メッセージはキーストローク(たとえば、「Enterキー」または「スペースバー」をウィンドウに送信する)の場合がありますが、ウィンドウを閉じるコマンド()、ウィンドウを変更するコマンド(非表示、表示)の場合もあります。それ、最小化、タイトルの変更など)、ウィンドウ内の情報の要求(タイトルの取得、テキストボックス内のテキストの取得など)など。一般的な例には、次のものがあります。WM_CLOSE

Public Const WM_CHAR = &H102
Public Const WM_SETTEXT = &HC
Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_CLOSE = &H10
Public Const WM_COMMAND = &H111
Public Const WM_CLEAR = &H303
Public Const WM_DESTROY = &H2
Public Const WM_GETTEXT = &HD
Public Const WM_GETTEXTLENGTH = &HE
Public Const WM_LBUTTONDBLCLK = &H203

これらは、APIビューア(またはメモ帳などの単純なテキストエディタ)でを開くことで見つけることができます(Microsoft Visual Studio Directory)/Common/Tools/WINAPI/winapi32.txt

次の2つのパラメーターは、必要に応じて特定の詳細です。特定のキーを押すという点では、どの特定のキーを押すかを正確に指定します。

windowHandleC#の例、 with WM_SETTEXT:のテキストを設定する

x = SendMessage(windowHandle, WM_SETTEXT, new IntPtr(0), m_strURL);

私が作成した、VBで記述された、プログラムのアイコンを設定するプログラムのその他の例(ICON_BIG定数はで見つけることができますwinapi32.txt):

Call SendMessage(hParent, WM_SETICON, ICON_BIG, ByVal hIcon)

VBの別の例として、スペースキーを押します(VK_SPACE定数はにありますwinapi32.txt)。

Call SendMessage(button%, WM_KEYDOWN, VK_SPACE, 0)
Call SendMessage(button%, WM_KEYUP, VK_SPACE, 0)

ボタンクリックを送信するVB(左ボタンを下に、次に上に):

Call SendMessage(button%, WM_LBUTTONDOWN, 0, 0&)
Call SendMessage(button%, WM_LBUTTONUP, 0, 0&)

.DLL内でリスナーを設定する方法はわかりませんが、これらの例は、メッセージの送信方法を理解するのに役立ちます。

于 2011-02-22T21:29:10.983 に答える
7

あなたはほとんどそこにいます。(FindWindow 宣言の戻り値の変更に注意してください)。この場合、 RegisterWindowMessageを使用することをお勧めします。そのため、 WM_USERのインとアウトについて心配する必要はありません。

[DllImport("user32.dll")]    
public static extern IntPtr FindWindow(string lpClassName, String lpWindowName);    
[DllImport("user32.dll")]    
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);    
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

public void button1_Click(object sender, EventArgs e)   
{        
     // this would likely go in a constructor because you only need to call it 
     // once per process to get the id - multiple calls in the same instance 
     // of a windows session return the same value for a given string
     uint id = RegisterWindowMessage("MyUniqueMessageIdentifier");
     IntPtr WindowToFind = FindWindow(null, "Form1");    
     Debug.Assert(WindowToFind != IntPtr.Zero);
     SendMessage(WindowToFind, id, IntPtr.Zero, IntPtr.Zero);
}

そして Form1 クラスで:

class Form1 : Form
{
    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    static extern uint RegisterWindowMessage(string lpString);

    private uint _messageId = RegisterWindowMessage("MyUniqueMessageIdentifier");

    protected override void WndProc(ref Message m)
    {
       if (m.Msg == _messageId)
       {
           // do stuff

       }
       base.WndProc(ref m);
    }
}

上記のいずれもコンパイルしていないため、微調整が必​​要になる場合があることに注意してください。また、あなたを遠ざけることを警告する他の回答が的を射ていることにも注意してくださいSendMessage。現在、モジュール間通信の推奨される方法ではありません。一般的に言えば、をオーバーライドしWndProcて使用することは、 Win32 メッセージ インフラストラクチャSendMessage/PostMessageがどのように機能するかをよく理解していることを意味します。

しかし、このルートに行きたい/行く必要がある場合は、上記の方法で正しい方向に進むことができると思います。

于 2011-02-22T22:20:58.280 に答える
6

メッセージを送信する必要はありません。

一方のフォームにイベントを追加し、もう一方のフォームにイベントハンドラーを追加します。次に、他の2つを参照する3番目のプロジェクトを使用して、イベントハンドラーをイベントにアタッチできます。これが機能するために、2つのDLLが相互に参照する必要はありません。

于 2011-02-22T21:10:42.993 に答える
0

メッセージ送信を使用するのは良い考えではないようです。DLLが相互に参照できないという問題を回避する必要があると思います...

于 2011-02-22T21:09:57.803 に答える
0

Mark Byersの回答に基づいています。

3 番目のプロジェクトは、Windows サービスとしてホストされる WCF プロジェクトである可能性があります。すべてのプログラムがそのサービスをリッスンする場合、1 つのアプリケーションがサービスを呼び出すことができます。サービスはメッセージをリッスンしているすべてのクライアントに渡し、必要に応じてアクションを実行できます。

こちらの優れた WCF ビデオ - http://msdn.microsoft.com/en-us/netframework/dd728059

于 2011-02-22T21:23:06.107 に答える
0

その他のオプション:

共通アセンブリ

アセンブリによって実装できるいくつかの共通インターフェイスを持つ別のアセンブリを作成します。

反射

これにはあらゆる種類の警告と欠点がありますが、リフレクションを使用してフォームをインスタンス化/通信できます。これは遅く、実行時に動的です (コンパイル時にこのコードの静的チェックは行われません)。

于 2011-02-22T21:16:40.487 に答える