WinForms应用程序之间的SendMessage - 表单需要关注

时间:2014-11-24 23:50:57

标签: vb.net winforms forms messages windows-messages

我正在使用Windows Messages API在两个Windows窗体应用程序之间进行通信。我拿出了一个表单,该表单是应用程序的一部分,并将其放入自己的应用程序中,以便在加载时,用户仍然可以在一个单独的应用程序中处理其他表单。

我需要能够在两个应用程序之间进行通信,以便现在单独的应用程序表单可以告诉主应用程序打开什么。

我在主应用程序的主窗体上使用此代码并且效果很好...除非主窗体没有焦点。

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    Try
        Select Case m.Msg
            Case &H400
                BS.BuildString(m.LParam)
            Case Else
                MyBase.WndProc(m)
        End Select
    Catch ex As Exception

    End Try
End Sub

我之前从未使用过Windows Message API,我只是通过查找表单之间的通信方式来抓住这个,所以我是新手。通过做更多研究我知道的是我需要一个仅消息窗口来处理消息。我不明白该怎么做。 我看了几篇文章和解决方案,比如this one,我认为我遇到的问题是我不知道如何实现它。

编辑1

以下是第二个应用找到并发送到主应用的方式。

                'used to send a message using SendMessage API to the 
                'main app to open the ID
                Private WithEvents BS As New BuildString 
                'get this running process
                Dim proc As Process = Process.GetCurrentProcess()
                'get all other (possible) running instances
                Dim processes As Process() = Process.GetProcessesByName("ProcessName")

                If processes.Length > 0 Then
                    'iterate through all running target applications
                    For Each p As Process In processes
                        'now send the ID to the running instance of the main app
                        BS.PostString(p.MainWindowHandle, &H400, 0, "ID:" & ID)
                    Next
                Else
                    MessageBox.Show("Main application not running")
                End If

这是两个应用程序中BuildString的类。

Imports System.Text

Public Class BuildString

Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Public Event StringOK(ByVal Result As String)
Private hwnd As Integer = 0
Private wMsg As Integer = 0
Private wParam As Integer = 0
Private lParam As String = ""
Private tempA(-1) As Byte
Private enc As Encoding = Encoding.UTF8

Public Property Encode() As Encoding
    Get
        Return enc
    End Get
    Set(ByVal value As Encoding)
        enc = value
    End Set
End Property

Public Sub BuildString(ByVal b As IntPtr)
    If b <> 0 Then
        'build temp array
        Dim tempB(tempA.Length) As Byte
        tempA.CopyTo(tempB, 0)
        tempB(tempA.Length) = b
        ReDim tempA(tempB.Length - 1)
        tempB.CopyTo(tempA, 0)
    Else
        'decode byte array to string
        Dim s As String
        If enc Is Encoding.UTF8 Then
            s = Encoding.UTF8.GetString(tempA)
        ElseIf enc Is Encoding.Unicode Then
            s = Encoding.Unicode.GetString(tempA)
        ElseIf enc Is Encoding.ASCII Then
            s = Encoding.ASCII.GetString(tempA)
        Else
            s = Encoding.Default.GetString(tempA)
        End If
        'send out result string via event
        RaiseEvent StringOK(s)
        ReDim tempA(-1)
    End If
End Sub

Public Sub PostString(ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String)
    Me.hwnd = hwnd
    Me.wMsg = wMsg
    Me.wParam = wParam
    Me.lParam = lParam
    'create a new thread to post window message
    Dim t As Threading.Thread
    t = New Threading.Thread(AddressOf SendString)
    t.Start()
End Sub

Private Sub SendString()
    'create byte array
    Dim ba() As Byte
    'encode string to byte array
    If enc Is Encoding.UTF8 Then
        ba = Encoding.UTF8.GetBytes(lParam)
    ElseIf enc Is Encoding.Unicode Then
        ba = Encoding.Unicode.GetBytes(lParam)
    ElseIf enc Is Encoding.ASCII Then
        ba = Encoding.ASCII.GetBytes(lParam)
    Else
        ba = Encoding.Default.GetBytes(lParam)
    End If
    Dim i As Integer
    For i = 0 To ba.Length - 1
        'start post message
        PostMessage(hwnd, wMsg, wParam, ba(i))
    Next
    'post a terminator message to destination window
    PostMessage(hwnd, wMsg, wParam, 0)
End Sub

结束班

以下是消息解码后运行的主应用程序上的代码。

Private Sub SB_StringOK(ByVal Result As String) Handles BS.StringOK
    Dim sArray() As String = Result.Split(";")
    'rest of the code to open the ID
End Sub

3 个答案:

答案 0 :(得分:3)

感谢@JustinRyan,我使用FindWindow想出来了。

我将此添加到发送邮件的第二个应用程序中。

Private Declare Function FindWindow1 Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer

我没有找到应用程序进程,而是找到了我想要的窗口,并在发送消息中将其直接发送给它。

Dim parenthwnd As Integer = FindWindow1(Nothing, "Form Name")
BS.PostString(parenthwnd, &H400, 0, "ID:" & ID)

它就像我需要的那样工作。

由于回答是在评论中,我不确定如何显示正确的答案。 请让我知道在这种情况下的正确礼仪,以便给予用户应有的信誉。

答案 1 :(得分:1)

您可以尝试使用新的线程并在第二个线程上创建第二个表单,而不是使用此API,因此两个表单都在不同的线程上运行,并且仍然可以是同一个项目的一部分

Dim Form2Thread As New System.Threading.Thread(Address of MethodName)


Sub Form1Load() Handles Form1.Shown
 Form2Thread.Start()
End Sub
Sub MethodName()
 'All your form creation code here
 'Form2.Show()
End Sub

这很简单,但缺点是你不能从原始线程上运行的方法直接编辑Form2的控件或属性。 Form2的所有更改都必须通过您的第二个帖子进行。

(有一个例外,但事情开始变得更复杂,搜索如何进行跨线程操作)

另一种解决方案是使用Background Worker组件。大量关于使用它们的教程。

答案 2 :(得分:1)

[添加更多信息:]

根据Process.MainWindowHandle

  

主窗口是当前进程打开的窗口   焦点(TopLevel表单)。您必须使用Refresh方法   刷新Process对象以获取当前主窗口句柄(如果它)   已经改变了。

TopLevel被定义为here(从MainWindowHandle链接),

  

顶级表单是没有父表单或其父表单的窗口   form是桌面窗口。顶级窗口通常用作   应用程序中的主要表单。

这可以解释为什么在表单处于非活动状态时将消息发送到其他位置。因此,虽然使用Process属性可能有助于获取单个表单应用程序的窗口句柄,但这使得它不可靠。

巧合的是,FindWindow也表示,

  

检索顶级窗口的句柄,该窗口的类名和窗口   name匹配指定的字符串。此功能不搜索孩子   窗户。

但是,FindWindow(和FindWindowEx)无关紧要(激活),因此将返回特定窗口的句柄而不关心它是否在顶部。

相关问题