Search code examples
vb.netwinformsformsmessagewindows-messages

SendMessage between WinForms Applications - form needs focus


I'm using the Windows Messages API to communicate between two Windows Forms Apps. I took out a form that was a part of the application and made it into it's own app so that when it is loading the user can still work on the other forms now in a separate app from the one form.

I need to be able to communicate between the two apps so that the now separate app form can tell the main app what to open.

I use this code on the main form of the main app and it works great... Except when the main form doesn't have focus.

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

I've never used the Windows Message API before, I just grabbed this from looking up how to communicate between forms, so I'm new at this. What I do know from doing some more research is that I need a Message only Window to handle the messages. I don't understand how to do this. I've looked at a few articles and solutions like this one and I think the problem I'm having is that I don't know how to implement it.

Edit 1

Here is how the second app finds and sends to the main app.

                '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

Here's the class for the BuildString that is in both apps.

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

End Class

Here is the code on the main app that is run once the message gets decoded.

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

Solution

  • Thanks to @JustinRyan I figured it out using FindWindow.

    I added this to my second application that send the message.

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

    Instead of finding the application process I find the window I want and send it directly to it in the send message.

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

    It works just as I needed.

    I'm not sure of how else to show the correct answer since the answer was in the comments. Please let me know of the proper etiquette in this situation to give the user his due credit.