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)

为了得到一个窗口的类名,你需要一个叫做Win Spy的东西

获得窗口句柄后,您可以使用该SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam)函数向其发送消息。

hWnd,这里,是FindWindow函数的结果。在上面的例子中,这将是hwndand hParent。它告诉SendMessage函数将消息发送到哪个窗口。

第二个参数wMsg是一个常量,表示您要发送的消息的类型。该消息可能是击键(例如,向窗口发送“回车键”或“空格键”),但也可能是关闭窗口的命令(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.

如果需要,接下来的两个参数是某些细节。在按下某些键方面,他们将准确指定要按下哪个特定键。

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

您无需发送消息。

将事件添加到一个表单,将事件处理程序添加到另一个表单。然后,您可以使用引用其他两个的第三个项目将事件处理程序附加到事件。这两个 DLL 不需要相互引用就可以工作。

于 2011-02-22T21:10:42.993 回答
0

使用发送消息听起来不是一个好主意。我认为您应该尝试解决 DLL 无法相互引用的问题...

于 2011-02-22T21:09:57.803 回答
0

其他一些选项:

普通组装

创建另一个具有一些可由程序集实现的通用接口的程序集。

反射

这有各种警告和缺点,但您可以使用反射来实例化/与表单通信。这既慢又运行时动态(在编译时没有对此代码的静态检查)。

于 2011-02-22T21:16:40.487 回答
0

基于Mark Byers 的回答

第三个项目可能是一个 WCF 项目,作为 Windows 服务托管。如果所有程序都侦听该服务,则一个应用程序可以调用该服务。该服务将消息传递给所有侦听客户端,如果合适,它们可以执行操作。

好的 WCF 视频在这里 - http://msdn.microsoft.com/en-us/netframework/dd728059

于 2011-02-22T21:23:06.107 回答