用 C# 发送消息

发布于 2024-10-18 13:16:49 字数 710 浏览 1 评论 0原文

我正在创建一个应用程序,该应用程序使用连接到多个不同 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");
    }

I'm creating an application that uses a main project that is connected to several different DLLs. From one DLL window I need to be able to open a window in another but the DLL's can't reference each other.

It was suggested to me to use the sendmessage function in the first DLL and have a listener in the main program that directs that message to the appropriate DLL to open it's window.

However I'm not familiar at all with the sendmessage function and am having a lot of diffculty piecing things together from information I'm finding online.

If someone could please show me the correct way (if there is any) to use the sendmessage function and maybe how a listener captures that message that would be amazing. Here is some of the code I've got so far I'm not sure if I'm heading in the right direction.

    [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");
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

难理解 2024-10-25 13:16:49
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) 向其发送消息) 函数。

这里的hWndFindWindow函数的结果。在上面的示例中,这将是 hwndhParent。它告诉 SendMessage 函数将消息发送到哪个窗口。

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

接下来的两个参数是某些细节(如果需要的话)。在按下某些键时,它们会准确指定要按下哪个特定键。

C# 示例,使用 WM_SETTEXT 设置 windowHandle 的文本:

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 中设置侦听器,但这些示例应该有助于理解如何发送消息。

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

In order to find the window, you need the class name of the window. Here are some examples:

C#:

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

Example from a program that I made, written in VB:

hParent = FindWindow("TfrmMain", vbNullString)

In order to get the class name of a window, you'll need something called Win Spy

Once you have the handle of the window, you can send messages to it using the SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam) function.

hWnd, here, is the result of the FindWindow function. In the above examples, this will be hwnd and hParent. It tells the SendMessage function which window to send the message to.

The second parameter, wMsg, is a constant that signifies the TYPE of message that you are sending. The message might be a keystroke (e.g. send "the enter key" or "the space bar" to a window), but it might also be a command to close the window (WM_CLOSE), a command to alter the window (hide it, show it, minimize it, alter its title, etc.), a request for information within the window (getting the title, getting text within a text box, etc.), and so on. Some common examples include the following:

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

These can be found with an API viewer (or a simple text editor, such as notepad) by opening (Microsoft Visual Studio Directory)/Common/Tools/WINAPI/winapi32.txt.

The next two parameters are certain details, if they are necessary. In terms of pressing certain keys, they will specify exactly which specific key is to be pressed.

C# example, setting the text of windowHandle with WM_SETTEXT:

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

More examples from a program that I made, written in VB, setting a program's icon (ICON_BIG is a constant which can be found in winapi32.txt):

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

Another example from VB, pressing the space key (VK_SPACE is a constant which can be found in winapi32.txt):

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

VB sending a button click (a left button down, and then up):

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

No idea how to set up the listener within a .DLL, but these examples should help in understanding how to send the message.

隔纱相望 2024-10-25 13:16:49

你快到了。 (注意 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 消息基础结构 有效。

但如果你想/需要走这条路,我认为上述内容会让你朝着正确的方向前进。

You are almost there. (note change in the return value of FindWindow declaration). I'd recommend using RegisterWindowMessage in this case so you don't have to worry about the ins and outs of 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);
}

And then in your Form1 class:

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);
    }
}

Bear in mind I haven't compiled any of the above so some tweaking may be necessary.
Also bear in mind that other answers warning you away from SendMessage are spot on. It's not the preferred way of inter module communication nowadays and genrally speaking overriding the WndProc and using SendMessage/PostMessage implies a good understanding of how the Win32 message infrastructure works.

But if you want/need to go this route I think the above will get you going in the right direction.

半窗疏影 2024-10-25 13:16:49

您不需要发送消息。

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

You don't need to send messages.

Add an event to the one form and an event handler to the other. Then you can use a third project which references the other two to attach the event handler to the event. The two DLLs don't need to reference each other for this to work.

回首观望 2024-10-25 13:16:49

其他一些选项:

通用程序集

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

反射

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

Some other options:

Common Assembly

Create another assembly that has some common interfaces that can be implemented by the assemblies.

Reflection

This has all sorts of warnings and drawbacks, but you could use reflection to instantiate / communicate with the forms. This is both slow and runtime dynamic (no static checking of this code at compile time).

清旖 2024-10-25 13:16:49

马克·拜尔斯的回答为基础。

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

这里有精彩的 WCF 视频 - http://msdn.microsoft.com/en-us/netframework /dd728059

Building on Mark Byers's answer.

The 3rd project could be a WCF project, hosted as a Windows Service. If all programs listened to that service, one application could call the service. The service passes the message on to all listening clients and they can perform an action if suitable.

Good WCF videos here - http://msdn.microsoft.com/en-us/netframework/dd728059

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文