web-dev-qa-db-ja.com

C#でメッセージを送信する

複数の異なるDLLに接続されているメインプロジェクトを使用するアプリケーションを作成しています。 1つのDLL windowから別のウィンドウを開くことができる必要があるが、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");
    }
14
Nicole

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

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

6
Mark Byers
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)

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

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

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

2番目のパラメーターwMsgは、送信するメッセージの[〜#〜] type [〜#〜]を表す定数です。メッセージはキーストローク(たとえば、「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

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

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

C#の例、WM_SETTEXTを使用して「windowHandle」のテキストを設定します。

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

VBで書かれたプログラムのアイコンを設定する、私が作成したプログラムのその他の例(ICONBIGは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内でリスナーを設定する方法はわかりませんが、これらの例はメッセージの送信方法を理解するのに役立ちます。

11
TimFoolery

あなたはほとんどそこにいます。 (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をオーバーライドしてSendMessage/PostMessageは、 Win32メッセージインフラストラクチャ の仕組みをよく理解していることを意味します。

しかし、もしあなたがこのルートに行きたい/必要なら、上記はあなたを正しい方向に導いてくれると思います。

3
dkackman

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

0
Daniel Hilgarth

その他のオプション:

共通アセンブリ

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

反射

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

0
RQDQ

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

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

良いWCFビデオはこちら- http://msdn.Microsoft.com/en-us/netframework/dd728059

0
JonWillis