2014-11-24 4 views
4

Я использую API сообщений Windows для связи между двумя приложениями Windows Forms. Я вынул форму, которая была частью приложения, и превратила ее в свое приложение, так что когда она загружается, пользователь все равно может работать с другими формами в отдельном приложении из одной формы.SendMessage между WinForms Приложения - требуется фокус формы

Мне нужно иметь возможность общаться между двумя приложениями, чтобы теперь отдельная форма приложения могла сообщать главному приложению, что открыть.

Этот код используется в основной форме основного приложения, и он отлично работает ... За исключением случаев, когда основная форма не имеет фокуса.

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 

Я никогда раньше не использовал API для Windows Message, я просто схватил это от глядя, как общаться между формами, так что я новичок в этом. То, что я знаю из других исследований, заключается в том, что для обработки сообщений мне нужно окно только для сообщений. Я не понимаю, как это сделать. Я рассмотрел несколько статей и решений, таких как this one, и я думаю, что проблема, с которой я столкнулась, заключается в том, что я не знаю, как ее реализовать.

Edit 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 

End Class

Вот код на главном приложение, которое запускается, когда сообщение получает декодируется.

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 
+1

Форма не обязательно должна быть активной (или иметь фокус) для приема сообщений; как вы упомянули, все, что ему нужно, это очередь сообщений. Пожалуйста, покажите код, который вы используете во втором приложении, чтобы найти дескриптор первого, и затем отправьте свое сообщение. –

+0

Я показал остальную часть кода @JustinRyan. «Как вы уже сказали», я упомянул форму, которая не должна быть активной? Где? Очередь сообщений? Я посмотрю. – Keevan

+0

Ваш код сообщения кажется правильным, поскольку я его прочитал. Получает ли ваше принимающее окно что-либо нетипичное, например, свернуть до системного трея или скрыть его в какой-то момент (кроме как просто неактивного)? Пока существует окно, оно должно иметь возможность принимать сообщения. Если это не так, я бы попробовал альтернативный способ получить дескриптор окна (например, [FindWindow] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499 (v = vs.85) .aspx)) вместо поиска по имени процесса. –

ответ

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) 

Работает так, как мне было нужно.

Я не уверен, как еще показать правильный ответ, так как ответ был в комментариях. Пожалуйста, дайте мне знать о надлежащем этикете в этой ситуации, чтобы дать пользователю должный кредит.

+0

Не забудьте нажать галочку рядом с этим ответом, чтобы другие знали, что эта проблема решена. –

+0

Да. Придется подождать 24 часа, прежде чем я смогу это сделать. Благодаря! – Keevan

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 должны быть сделаны через ваш второй поток.

(Существует исключение для этого, но все становится более сложным, поиск как делать операции кросс-нить)

Другим решением является использование компонента фона рабочего. Об их использовании много.

1

[Чтобы добавить немного больше информации:]

Согласно Process.MainWindowHandle,

Главное окно окно открывается процесс, который в настоящее время имеет фокус (форма TopLevel). Вы должны использовать метод Refresh to обновить объект Process, чтобы получить текущий обработчик главного окна, если он изменил .

Toplevel определяется here (как связанные с MainWindowHandle), как,

Форма верхнего уровня является окно, которое не имеет родительской формы или чей родитель форма является окно рабочего стола. Окна верхнего уровня обычно используются как основная форма приложения.

Это объясняет, почему сообщения отправляются в другом месте, когда форма неактивна. Таким образом, при использовании свойств может работать для получения дескриптора окна приложений с одной формой, что делает его ненадежным в противном случае.

совпадению, FindWindow также утверждает,

Получает дескриптор окна верхнего уровня, имя класса и окна имя соответствуют заданным строкам. Эта функция не ищет дочерние окна .

Однако FindWindow (и FindWindowEx) агностик сосредотачиваться (активация) и, следовательно, будет возвращать ручку конкретного окна, не заботясь о том, является ли он на вершине.