单击作为面板父级的另一个应用程序的窗口时如何将表单移动到前台

问题描述 投票:0回答:2

我使用下面的代码启动一个应用程序并将其移动到我的表单上的面板中。在这个例子中我使用记事本,仅作为示例。稍后我将使用不同的应用程序。

当另一个应用程序移动到我的表单前面时,我只能通过单击标题栏将我的表单移动到前台。如果我单击 MDI 子区域(记事本移入的面板),则不会发生任何事情。
有办法实现吗?

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
vb.net winforms winapi parent-child mdi
2个回答
2
投票

问题在于,托管(重新设置父级)窗口在激活时不会导致托管窗体也激活,因此它会被带到前台。
托管窗口并不完全是子窗口,托管表单不会从它接收任何消息。

当此窗口获得焦点(单击或以其他方式激活它)时,将托管foreign窗口的表单带到前台的可能方法
它使用 SetWinEventHook 安装一个 Hook,用于监视窗口放置状态的变化 (

EVENT_SYSTEM_FOREGROUND
)。
您指定感兴趣的窗口的句柄(此处为您的
proc.MainWindowHandle
),以及它的
ProcessId
ThreadId
。请注意,这些与您的应用程序不同,需要调用 GetWindowThreadProcessId() 才能获取此信息。

当您激活 foreign 窗口时,Hook 会调用指定的回调委托(此处为

ForegroundChangedEventDelegate
),后者又执行它指向的方法 (
ForegroundStateChangedCallback
)
调用此方法时,它会检查引起通知的对象是否是
OBJID_WINDOW
以及事件实际上是
EVENT_SYSTEM_FOREGROUND
。如果是这种情况,它会调用 SetWindowPos 将托管窗体带到前台,但不激活它,因此托管窗口不会失去焦点

备注:

  • SetWinEventHook
    委托是在父Form的构造函数(
    Sub New()
    )中创建的,还有一个GC SafeHandle,用于防止委托在错误的时间被垃圾收集。
    它在
    OnHandleDestroyed()
    覆盖中释放,其中挂钩过程也未挂钩

  • 请参阅表格中的进口声明:

      Imports [Your App Namespace].NativeMethods  
    

这意味着您必须指定应用程序的命名空间才能使导入按预期工作。在 VB.NET 中,通常应用程序的名称与其主要的

Namespace
匹配;如果您的应用程序名为
WinFormsApp1
,那么它就是
Imports WinFormsApp1.NativeMethods

  • 要激活 Hook,一旦更改了该窗口的父级,请使用其句柄调用
    SetForegroundStateChangedHook()
    方法。仅此而已。
    当窗体关闭时,钩子被释放

我建议使用将窗口取消挂钩到其原始状态中的代码来设置父项(并且可能在关闭托管表单之前将其设置回原始值)。如果需要,您可以将

WM_CLOSE
发送到窗口。


Imports [Your App Namespace].NativeMethods

Public Class SomeForm

    Private hForegrundChangedEventHook As IntPtr
    Private ReadOnly ForegrundChangedEventDelegate As WinEventDelegate
    Private Shared GCForegroundStateSafetyHandle As GCHandle

    Public Sub New()
        InitializeComponent()
        ForegrundChangedEventDelegate = New WinEventDelegate(AddressOf ForegroundStateChangedCallback)
        GCForegroundStateSafetyHandle = GCHandle.Alloc(ForegrundChangedEventDelegate)
    End Sub

    Protected Overrides Sub OnHandleDestroyed(e As EventArgs)
        GCForegroundStateSafetyHandle.Free()
        UnhookWinEvent(hForegrundChangedEventHook)
        MyBase.OnHandleDestroyed(e)
    End Sub

    Private Sub SetForegroundStateChangedHook(hWnd As IntPtr)
        Dim processId As UInteger
        Dim targetThreadId = GetWindowThread(hWnd, processId)
        hForegrundChangedEventHook = WinEventHookOne(SWEH_Events.EVENT_SYSTEM_FOREGROUND, ForegrundChangedEventDelegate, processId, targetThreadId)
    End Sub

    Friend Sub ForegroundStateChangedCallback(hWinEventHook As IntPtr, eventType As SWEH_Events, hWnd As IntPtr, idObject As SWEH_ObjectId, idChild As Long, dwEventThread As UInteger, dwmsEventTime As UInteger)
        If idObject = SWEH_ObjectId.OBJID_WINDOW AndAlso eventType = SWEH_Events.EVENT_SYSTEM_FOREGROUND Then
            Dim flags = SWP_Flags.SWP_ASYNCWINDOWPOS Or SWP_Flags.SWP_NOACTIVATE Or SWP_Flags.SWP_NOSIZE Or SWP_Flags.SWP_NOMOVE
            SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, flags)
        End If
    End Sub
End Class

NativeMethods 类

将此类添加到项目中并按照描述导入到您的表单中

Imports System.Runtime.InteropServices

Public Class NativeMethods

    <DllImport("user32.dll", SetLastError:=True)>
    Friend Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As UInteger) As UInteger
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Friend Shared Function SetWindowPos(hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, uFlags As SWP_Flags) As Boolean
    End Function

    Friend Delegate Sub WinEventDelegate(
        hWinEventHook As IntPtr,
        eventType As SWEH_Events,
        hwnd As IntPtr, idObject As SWEH_ObjectId,
        idChild As Long,
        dwEventThread As UInteger,
        dwmsEventTime As UInteger)

    <DllImport("user32.dll", SetLastError:=False)>
    Friend Shared Function SetWinEventHook(
        eventMin As SWEH_Events,
        eventMax As SWEH_Events,
        hmodWinEventProc As IntPtr,
        lpfnWinEventProc As WinEventDelegate,
        idProcess As UInteger,
        idThread As UInteger,
        dwFlags As SWEH_dwFlags) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=False)>
    Friend Shared Function UnhookWinEvent(hWinEventHook As IntPtr) As Boolean
    End Function

    Friend Shared WinEventHookInternalFlags As SWEH_dwFlags =
        SWEH_dwFlags.WINEVENT_OUTOFCONTEXT Or SWEH_dwFlags.WINEVENT_SKIPOWNPROCESS


    Friend Shared Function WinEventHookOne(evt As SWEH_Events, weDelegate As WinEventDelegate, idProcess As UInteger, idThread As UInteger) As IntPtr
        Return SetWinEventHook(evt, evt, IntPtr.Zero, weDelegate, idProcess, idThread, WinEventHookInternalFlags)
    End Function


    Friend Shared Function GetWindowThread(hWnd As IntPtr, ByRef processID As UInteger) As UInteger
        processID = 0
        Return GetWindowThreadProcessId(hWnd, processID)
    End Function


    ' SetWinEventHook Events
    Friend Enum SWEH_Events As UInteger
        EVENT_MIN = &H1
        EVENT_MAX = &H7FFFFFFF
        EVENT_SYSTEM_SOUND = &H1
        EVENT_SYSTEM_ALERT = &H2
        EVENT_SYSTEM_FOREGROUND = &H3
        EVENT_SYSTEM_MENUSTART = &H4
        EVENT_SYSTEM_MENUEND = &H5
        EVENT_SYSTEM_MENUPOPUPSTART = &H6
        EVENT_SYSTEM_MENUPOPUPEND = &H7
        EVENT_SYSTEM_CAPTURESTART = &H8
        EVENT_SYSTEM_CAPTUREEND = &H9
        EVENT_SYSTEM_MOVESIZESTART = &HA
        EVENT_SYSTEM_MOVESIZEEND = &HB
        EVENT_SYSTEM_CONTEXTHELPSTART = &HC
        EVENT_SYSTEM_CONTEXTHELPEND = &HD
        EVENT_SYSTEM_DRAGDROPSTART = &HE
        EVENT_SYSTEM_DRAGDROPEND = &HF
        EVENT_SYSTEM_DIALOGSTART = &H10
        EVENT_SYSTEM_DIALOGEND = &H11
        EVENT_SYSTEM_SCROLLINGSTART = &H12
        EVENT_SYSTEM_SCROLLINGEND = &H13
        EVENT_SYSTEM_SWITCHSTART = &H14
        EVENT_SYSTEM_SWITCHEND = &H15
        EVENT_SYSTEM_MINIMIZESTART = &H16
        EVENT_SYSTEM_MINIMIZEEND = &H17
        EVENT_SYSTEM_DESKTOPSWITCH = &H20
        EVENT_SYSTEM_END = &HFF
        EVENT_OEM_DEFINED_START = &H101
        EVENT_OEM_DEFINED_END = &H1FF
        EVENT_UIA_EVENTID_START = &H4E00
        EVENT_UIA_EVENTID_END = &H4EFF
        EVENT_UIA_PROPID_START = &H7500
        EVENT_UIA_PROPID_END = &H75FF
        EVENT_CONSOLE_CARET = &H4001
        EVENT_CONSOLE_UPDATE_REGION = &H4002
        EVENT_CONSOLE_UPDATE_SIMPLE = &H4003
        EVENT_CONSOLE_UPDATE_SCROLL = &H4004
        EVENT_CONSOLE_LAYOUT = &H4005
        EVENT_CONSOLE_START_APPLICATION = &H4006
        EVENT_CONSOLE_END_APPLICATION = &H4007
        EVENT_CONSOLE_END = &H40FF
        EVENT_OBJECT_CREATE = &H8000
        EVENT_OBJECT_DESTROY = &H8001
        EVENT_OBJECT_SHOW = &H8002
        EVENT_OBJECT_HIDE = &H8003
        EVENT_OBJECT_REORDER = &H8004
        EVENT_OBJECT_FOCUS = &H8005
        EVENT_OBJECT_SELECTION = &H8006
        EVENT_OBJECT_SELECTIONADD = &H8007
        EVENT_OBJECT_SELECTIONREMOVE = &H8008
        EVENT_OBJECT_SELECTIONWITHIN = &H8009
        EVENT_OBJECT_STATECHANGE = &H800A
        EVENT_OBJECT_LOCATIONCHANGE = &H800B
        EVENT_OBJECT_NAMECHANGE = &H800C
        EVENT_OBJECT_DESCRIPTIONCHANGE = &H800D
        EVENT_OBJECT_VALUECHANGE = &H800E
        EVENT_OBJECT_PARENTCHANGE = &H800F
        EVENT_OBJECT_HELPCHANGE = &H8010
        EVENT_OBJECT_DEFACTIONCHANGE = &H8011
        EVENT_OBJECT_ACCELERATORCHANGE = &H8012
        EVENT_OBJECT_INVOKED = &H8013
        EVENT_OBJECT_TEXTSELECTIONCHANGED = &H8014
        EVENT_OBJECT_CONTENTSCROLLED = &H8015
        EVENT_SYSTEM_ARRANGMENTPREVIEW = &H8016
        EVENT_OBJECT_END = &H80FF
        EVENT_AIA_START = &HA000
        EVENT_AIA_END = &HAFFF
    End Enum

    ' SetWinEventHook Window Objects
    Friend Enum SWEH_ObjectId As Long
        OBJID_WINDOW = &H0
        OBJID_SYSMENU = &HFFFFFFFFUI
        OBJID_TITLEBAR = &HFFFFFFFEUI
        OBJID_MENU = &HFFFFFFFDUI
        OBJID_CLIENT = &HFFFFFFFCUI
        OBJID_VSCROLL = &HFFFFFFFBUI
        OBJID_HSCROLL = &HFFFFFFFAUI
        OBJID_SIZEGRIP = &HFFFFFFF9UI
        OBJID_CARET = &HFFFFFFF8UI
        OBJID_CURSOR = &HFFFFFFF7UI
        OBJID_ALERT = &HFFFFFFF6UI
        OBJID_SOUND = &HFFFFFFF5UI
        OBJID_QUERYCLASSNAMEIDX = &HFFFFFFF4UI
        OBJID_NATIVEOM = &HFFFFFFF0UI
    End Enum

    ' WinEventDelegate flags
    Friend Enum SWEH_dwFlags As UInteger
        WINEVENT_OUTOFCONTEXT = &H0     ' Events are ASYNC - No dll needed
        WINEVENT_SKIPOWNTHREAD = &H1    ' Don't call back for events on installer's thread
        WINEVENT_SKIPOWNPROCESS = &H2   ' Don't call back for events on installer's process
        WINEVENT_INCONTEXT = &H4        ' Events are SYNC, this causes your dll to be injected into every process
    End Enum

    ' SetWindowPos flags
    <Flags>
    Public Enum SWP_Flags As UInteger
        SWP_NOSIZE = &H1
        SWP_NOMOVE = &H2
        SWP_NOZORDER = &H4
        SWP_NOREDRAW = &H8
        SWP_NOACTIVATE = &H10
        SWP_DRAWFRAME = &H20
        SWP_FRAMECHANGED = &H20
        SWP_SHOWWINDOW = &H40
        SWP_HIDEWINDOW = &H80
        SWP_NOCOPYBITS = &H100
        SWP_NOOWNERZORDER = &H200
        SWP_NOREPOSITION = &H200
        SWP_NOSENDCHANGING = &H400
        SWP_NOCLIENTSIZE = &H800
        SWP_NOCLIENTMOVE = &H1000
        SWP_DEFERERASE = &H2000
        SWP_ASYNCWINDOWPOS = &H4000
    End Enum
End Class

0
投票

感谢Jimi的大力帮助,我才得以成功:

NativeMethods
类添加到项目中,使用上面 Jimi 的回答中的代码

在表单中使用以下代码:

Imports System.Runtime.InteropServices
Imports WindowsApp1.NativeMethods
Public Class Form1
    Private ReadOnly ForegrundChangedEventDelegate As WinEventDelegate
    Private Shared GCForegroundStateSafetyHandle As GCHandle
    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
    Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim proc As Process
        Dim ProcId As Integer = 0
        proc = Process.Start("notepad.exe")
        proc.WaitForInputIdle()
        SetParent(proc.MainWindowHandle, Me.Panel1.Handle)
        SendMessage(proc.MainWindowHandle, 274, 61488, 0)
        GetWindowThreadProcessId(proc.MainWindowHandle, ProcId)
        SetForegroundStateChangedHook(proc.MainWindowHandle)
    End Sub
    Public Sub New()
        InitializeComponent()
        ForegrundChangedEventDelegate = New WinEventDelegate(AddressOf ForegroundStateChangedCallback)
        GCForegroundStateSafetyHandle = GCHandle.Alloc(ForegrundChangedEventDelegate)
    End Sub
    Protected Overrides Sub OnHandleDestroyed(e As EventArgs)
        GCForegroundStateSafetyHandle.Free()
        UnhookWinEvent(hForegrundChangedEventHook)
        MyBase.OnHandleDestroyed(e)
    End Sub
    Private Sub SetForegroundStateChangedHook(hWnd As IntPtr)
        Dim processId As UInteger
        Dim targetThreadId = GetWindowThread(hWnd, processId)
        hForegrundChangedEventHook = WinEventHookOne(SWEH_Events.EVENT_SYSTEM_FOREGROUND, ForegrundChangedEventDelegate, processId, targetThreadId)
    End Sub
    Friend Sub ForegroundStateChangedCallback(hWinEventHook As IntPtr, eventType As SWEH_Events, hWnd As IntPtr, idObject As SWEH_ObjectId, idChild As Long, dwEventThread As UInteger, dwmsEventTime As UInteger)
        If idObject = SWEH_ObjectId.OBJID_WINDOW AndAlso eventType = SWEH_Events.EVENT_SYSTEM_FOREGROUND Then
            Dim flags = SWP_Flags.SWP_ASYNCWINDOWPOS Or SWP_Flags.SWP_NOACTIVATE Or SWP_Flags.SWP_NOSIZE Or SWP_Flags.SWP_NOMOVE
            SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, flags)
        End If
    End Sub
    End Class
© www.soinside.com 2019 - 2024. All rights reserved.