I use the code down below to start an application and move it into a Panel on my Form. In this example I use Notepad, only as an example. Later I will use a different application.
When another application is moved in front of my Form, I can only move my Form to the foreground by clicking the title bar. If I click on the MDI child area (so the Panel where Notepad is moved into), nothing happens.
Is there a way to enable that?
Imports System.Runtime.InteropServices
Public Class Form1
Declare Auto Function SetParent Lib "user32.dll" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As Integer
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim proc As Process
proc = Process.Start("notepad.exe")
proc.WaitForInputIdle()
SetParent(proc.MainWindowHandle, Me.Panel1.Handle)
SendMessage(proc.MainWindowHandle, 274, 61488, 0)
End Sub
End Class
The problem is that a hosted (re-parented) Window, when activated, doesn't cause the hosing Form to also activate, so it's brought to the foreground.
The hosted Window is not exactly a child Window and the hosting Form doesn't receive any message from it.
A possible method to bring to the foreground the Form that hosts a foreign Window, when this Window receives the focus (you click or otherwise activate it)
It uses SetWinEventHook to install a Hook that monitors changes in state of the placement of a Window (
EVENT_SYSTEM_FOREGROUND).You specify the handle of the Window of interest, (your
proc.MainWindowHandlehere), plus itsProcessIdandThreadId. Note that these are not the same as your app's, a call to GetWindowThreadProcessId() is required to get this information.When you activate the foreign Window, the Hook calls the specified callback delegate (here,
ForegroundChangedEventDelegate), which in turn executes the method that it points to (ForegroundStateChangedCallback)When this method is called, it checks whether the object that caused the notification is
OBJID_WINDOWand that the event is actuallyEVENT_SYSTEM_FOREGROUND. If that's the case, it calls SetWindowPos to bring the hosting Form to the foreground, but without activating it, so the hosted Window doesn't lose focusNotes:
The
SetWinEventHookdelegate is created in the Constructor (Sub New()) of the parent Form, along with a GC SafeHandle that is used to prevent the delegate from being garbage-collected at the wrong time.It's released in the
OnHandleDestroyed()override, where also the hook proc is un-hookedsee the imports statement in the Form:
this means that you have to specify the namespace of your app for the import to work as intended. In VB.NET, usually the name of the app and its main
Namespacematch; if your app is named,WinFormsApp1, then it'sImports WinFormsApp1.NativeMethodsSetForegroundStateChangedHook()method. That's all.When the Form closes, the hook is released
I suggest using the code in Unhook Window into its original State to set the Parent (and, possibly, set it back to the original value before closing the hosting form). You can send
WM_CLOSEto the Window if needed.NativeMethods class
Add this class to the Project and import in your Form as described