65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Visual Basic 的注册消息进行进程间通信

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.39/5 (15投票s)

2003 年 3 月 19 日

CPOL

4分钟阅读

viewsIcon

213428

本文介绍了如何注册自定义 Windows 消息并创建专门用于处理这些消息的窗口,并使用它们在您的应用程序之间进行通信。

引言

在 Visual Basic 中实现多任务处理的最简单方法之一是创建一个单独的可执行程序来执行每个任务,并简单地使用 Shell 命令根据需要运行它们。 这样做唯一的问题是,一旦程序运行,您需要与其通信才能控制其操作。 实现此目的的一种方法是使用 RegisterWindowMessageSendMessage API 调用来创建您自己的特定窗口消息,并在窗口之间发送它们,从而允许您创建两个或多个相互 通信 的程序。 在此示例中,服务器的任务是监视打印队列,并在发生任何事件(作业添加、驱动程序更改、作业打印等)时向每个感兴趣的客户端发送消息。

指定您自己的唯一消息

Windows 通过相互发送标准 Windows 消息(例如 WM_CLOSE 用于关闭和终止窗口)来进行通信。 存在大量标准消息,涵盖了可以由不同窗口执行和对不同窗口执行的大多数标准操作。 但是,如果您想实现自己的自定义通信,则需要创建自己的自定义消息。 这是通过 RegisterWindowMessage API 调用完成的

'\\ Declaration to register custom messages
Private Declare Function RegisterWindowMessage Lib "user32" Alias _
  "RegisterWindowMessageA" (ByVal  lpString As String) As Long

此 API 调用接受一个唯一的字符串并将其注册为已定义的 Windows 消息,从而返回该消息的系统范围内的唯一标识符作为结果。 此后,任何应用程序中指定相同字符串的 RegisterWindowMessage 调用都将返回相同的唯一消息 ID。 由于此值在每个会话期间是恒定的,因此将其存储在全局变量中以加快执行速度是安全的

     Public Const MSG_CHANGENOTIFY = "MCL_PRINT_NOTIFY" 
     
      Public Function WM_MCL_CHANGENOTIFY() As Long 
      Static msg As Long 
      
      If msg = 0 Then 
            msg = RegisterWindowMessage(MSG_CHANGENOTIFY)
      End If
     
     WM_MCL_CHANGENOTIFY = msg
     
     End Function

由于此消息需要为每个使用它进行通信的应用程序所知,因此最好将其放入所有项目共用的共享代码模块中。

创建窗口以侦听这些消息

要在 Visual Basic 中创建窗口,您通常使用表单设计器并将新表单添加到您的项目中。 但是,由于我们的通信窗口没有可见的组件,也没有与用户的交互,这有点多余。 相反,我们可以使用 CreateWindowEx API 调用来创建一个专门用于我们通信的窗口

Private Declare Function CreateWindowEx  _
    Lib "user32"  Alias "CreateWindowExA"  
    (ByVal dwExStyle  As Long, _
    '\\ The window class, e.g. "STATIC","BUTTON" etc.
    ByVal lpClassName As String, _
    '\\ The window's name (and caption if it has one) 
    ByVal lpWindowName As String, _
    ByVal dwStyle As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal nWidth As Long, _
    ByVal nHeight As Long, _
    ByVal hWndParent As Long, _
    ByVal hMenu As Long, _
    ByVal hInstance As Long, _
    lpParam As Any) As Long 

如果此调用成功,它将返回一个唯一的窗口句柄,该句柄可用于引用该窗口。 可以在 SendMessage 调用中使用它来向其发送消息。

在典型的客户端/服务器通信中,您需要为客户端和服务器创建一个窗口。 同样,这可以使用每个应用程序通用的少量代码来完成

Public Const WINDOWTITLE_CLIENT = "Merrion Computing IPC - Client" 
Public Const WINDOWTITLE_SERVER = "Merrion Computing IPC - Server"
     
Public Function CreateCommunicationWindow(ByVal client As Boolean) As Long 
    
    Dim hwndThis As Long 
    Dim sWindowTitle As String 
    
    If client Then 
        sWindowTitle = WINDOWTITLE_CLIENT 
    Else
        sWindowTitle = WINDOWTITLE_SERVER 
    End If 
    
    hwndThis = CreateWindowEx(0, "STATIC", sWindowTitle,_
        0, 0, 0, 0, 0, 0, 0, App.hInstance, ByVal 0&)
    
    CreateCommunicationWindow = hwndThis
    
End Function

显然,对于您自己的应用程序,您应该对 WINDOWTITLE_CLIENTWINDOWTITLE_SERVER 使用与以上不同的文本,以确保您的窗口名称是唯一的。

处理自定义消息

就这样,您有了一个自定义消息,并创建了一个可以向其发送该消息的窗口。 但是,由于此消息对窗口来说是全新的,因此当它收到它时,它不会做任何事情。 要实际处理该消息,您需要对窗口进行子类化以截获消息并对其做出反应。 要对窗口进行子类化,您可以创建一个处理 Windows 消息的过程,并将其替换为该窗口的默认消息处理过程。 您的过程必须具有与默认窗口过程相同的参数和返回类型

Private Declare Function CallWindowProc Lib 
      "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc 
      As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal
       wParam As Long, ByVal lParam As Long) As Long

   
   
    '\\ --[VB_WindowProc]--------------------------------- 
    '\\ 'typedef LRESULT (CALLBACK* WNDPROC)(HWND, 
    '\\                     UINT, WPARAM, LPARAM); 
    '\\ Parameters: 
    '\\   hwnd - window handle receiving message 
    '\\   wMsg - The window message (WM_..etc.) 
    '\\   wParam - First message parameter 
    '\\   lParam - Second message parameter 
Public Function VB_WindowProc(ByVal hwnd As Long, _
     ByVal wMsg As Long, ByVal wParam As Long,_
     ByVal lParam As Long) As Long 
     
    If wMsg = WM_MCL_CHANGENOTIFY Then 
      '\\Respond to the custom message here
     
    Else
        '\\Pass the message to the previous 
        '\\window procedure to handle it
        VB_WindowProc = CallWindowProc(hOldProc, hwnd, _
                                    wMsg, wParam, lParam)
    End If
     
End Function

然后,您需要通知 Windows 将此过程替换为现有的窗口过程。 为此,您调用 SetWindowLong 以更改存储在 GWL_WINDPROC 索引中的过程的地址。

Public Const GWL_WNDPROC = (-4) 
Public Declare Function SetWindowLongApi  Lib "user32" _
     Alias "SetWindowLongA" _
    (ByVal hwnd  As Long, ByVal nIndex  As Long, _
     ByVal dwNewLong  As Long) As Long 
   
   '\\ Use (after creating the window...) 
   hOldProc = SetWindowLongApi(hwndThis, _
      GWL_WNDPROC, AddressOf VB_WindowProc)

您将先前窗口过程的地址保留在 hOldProc 中,以便传递您未处理以进行默认处理的所有消息。 最好在关闭窗口之前将窗口过程设置回此地址。

发送自定义消息

将自定义消息发送到服务器窗口有两个步骤:首先,您需要使用 FindWindowEx API 调用找到该窗口的窗口句柄。 然后,您需要使用 SendMessage API 调用发送消息。

'\\ Declarations 
Public Declare Function SendMessageLong Lib "user32" Alias "SendMessageA" _
   (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam _
   As Long, ByVal lParam As Long) As Long _
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
   (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 
  
  '\\ use.... 
  Dim hwndTarget As Long
  
  hwndTarget = FindWindow(vbNullString, WINDOWTITLE_SERVER)
  
  If hwndTarget <> 0 Then 
      Call SendMessageLong(hwnd_Server, _
           WM_MCL_CHANGENOTIFY, 0,0) 
  End If

这将向服务器窗口发送 WM_MCL_CHANGENOTIFY 消息,并在处理完毕后返回。

© . All rights reserved.