外部应用程序窗口移动时移动窗口

Per*_*nce 5 c# pinvoke winapi user32 winforms

我有一个总是在顶部的应用程序(基本上是一个状态显示),我想跟随另一个程序,并始终坐在最小化按钮的左侧.

我可以Rect使用以下代码来表示"目标"过程,然后我可以将其与偏移量配对以生成叠加层的初始位置.

获取HWnd IntPtr:

private IntPtr HWnd = Process.GetProcessesByName("targetapplication")[0].MainWindowHandle; 
Run Code Online (Sandbox Code Playgroud)

user32.dll以下函数声明函数:

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
Run Code Online (Sandbox Code Playgroud)

适当的struct:

[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}
Run Code Online (Sandbox Code Playgroud)

然后按需调用它.

但是,我想避免不断轮询这个值,所以我想挂钩到目标应用程序并在目标窗口移动时做出响应.

浏览user32.dll文档,我能看到的唯一方法是使用SetWindowsHookEx().但是,我并不完全确定如何从这里拦截一个事件.

我相信目标应用程序是基于WinForms但我无法确定.因此,让我响应目标Move事件或直接响应某些Windows消息的解决方案都很有用.

关于如何进行的任何想法?

Jim*_*imi 11

这个展示了如何将Winform挂钩到另一个进程(在本例中为记事本)并遵循进程主窗口的移动,以创建可以与进程交互的工具栏.

使用的主要API函数是SetWinEventHook()

结果的可视化表示:

SetWinEventHook示例图像

Form类初始化过程:

public partial class Form1 : Form
{
    private IntPtr NotepadhWnd;
    private IntPtr hWinEventHook;
    private Process Target;
    private RECT rect = new RECT();
    protected Hook.WinEventDelegate WinEventDelegate;
    static GCHandle GCSafetyHandle;

    public Form1()
    {
        InitializeComponent();
        WinEventDelegate = new Hook.WinEventDelegate(WinEventCallback);
        GCSafetyHandle = GCHandle.Alloc(WinEventDelegate);

        try
        {
            Target = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null);
            if (Target != null)
            {
                NotepadhWnd = Target.MainWindowHandle;
                uint TargetThreadId = Hook.GetWindowThread(NotepadhWnd);

                if (NotepadhWnd != IntPtr.Zero)
                {
                    hWinEventHook = Hook.WinEventHookOne(Hook.SWEH_Events.EVENT_OBJECT_LOCATIONCHANGE, 
                                                         WinEventDelegate, 
                                                         (uint)Target.Id, 
                                                         TargetThreadId);
                    rect = Hook.GetWindowRect(NotepadhWnd);
                    this.Location = new Point(rect.Right, rect.Top);
                }
            }
        }
        catch (Exception ex)
        {
            //ErrorManager.Logger(this, this.InitializeComponent(), ex.HResult, ex.Data, DateTime.Now);
            throw ex;
        }
    }

    protected void WinEventCallback(IntPtr hWinEventHook, 
                                    Hook.SWEH_Events eventType, 
                                    IntPtr hWnd, 
                                    Hook.SWEH_ObjectId idObject, 
                                    long idChild, 
                                    uint dwEventThread, 
                                    uint dwmsEventTime)
    {
        if (hWnd == NotepadhWnd && 
            eventType == Hook.SWEH_Events.EVENT_OBJECT_LOCATIONCHANGE && 
            idObject == (Hook.SWEH_ObjectId)Hook.SWEH_CHILDID_SELF)
        {
            rect = Hook.GetWindowRect(hWnd);
            this.Location = new Point(rect.Right, rect.Top);
        }
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        GCSafetyHandle.Free();
        Hook.WinEventUnhook(hWinEventHook);
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        if (Target == null)
        {
            this.Hide();
            MessageBox.Show("Notepad not found!", "Target Missing", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            this.Close();
        }
        else
        {
            this.Size = new Size(50, 140);
        }
    }
Run Code Online (Sandbox Code Playgroud)

用于引用Windows API方法的支持(部分)类:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

public class Hook
{
    public static long SWEH_CHILDID_SELF = 0;

    //SetWinEventHook() flags
    public enum SWEH_dwFlags : uint
    {
        WINEVENT_OUTOFCONTEXT = 0x0000,     // Events are ASYNC
        WINEVENT_SKIPOWNTHREAD = 0x0001,    // Don't call back for events on installer's thread
        WINEVENT_SKIPOWNPROCESS = 0x0002,   // Don't call back for events on installer's process
        WINEVENT_INCONTEXT = 0x0004         // Events are SYNC, this causes your dll to be injected into every process
    }

    //SetWinEventHook() events
    public enum SWEH_Events : uint
    {
        EVENT_MIN = 0x00000001,
        EVENT_MAX = 0x7FFFFFFF,
        EVENT_SYSTEM_SOUND = 0x0001,
        EVENT_SYSTEM_ALERT = 0x0002,
        EVENT_SYSTEM_FOREGROUND = 0x0003,
        EVENT_SYSTEM_MENUSTART = 0x0004,
        EVENT_SYSTEM_MENUEND = 0x0005,
        EVENT_SYSTEM_MENUPOPUPSTART = 0x0006,
        EVENT_SYSTEM_MENUPOPUPEND = 0x0007,
        EVENT_SYSTEM_CAPTURESTART = 0x0008,
        EVENT_SYSTEM_CAPTUREEND = 0x0009,
        EVENT_SYSTEM_MOVESIZESTART = 0x000A,
        EVENT_SYSTEM_MOVESIZEEND = 0x000B,
        EVENT_SYSTEM_CONTEXTHELPSTART = 0x000C,
        EVENT_SYSTEM_CONTEXTHELPEND = 0x000D,
        EVENT_SYSTEM_DRAGDROPSTART = 0x000E,
        EVENT_SYSTEM_DRAGDROPEND = 0x000F,
        EVENT_SYSTEM_DIALOGSTART = 0x0010,
        EVENT_SYSTEM_DIALOGEND = 0x0011,
        EVENT_SYSTEM_SCROLLINGSTART = 0x0012,
        EVENT_SYSTEM_SCROLLINGEND = 0x0013,
        EVENT_SYSTEM_SWITCHSTART = 0x0014,
        EVENT_SYSTEM_SWITCHEND = 0x0015,
        EVENT_SYSTEM_MINIMIZESTART = 0x0016,
        EVENT_SYSTEM_MINIMIZEEND = 0x0017,
        EVENT_SYSTEM_DESKTOPSWITCH = 0x0020,
        EVENT_SYSTEM_END = 0x00FF,
        EVENT_OEM_DEFINED_START = 0x0101,
        EVENT_OEM_DEFINED_END = 0x01FF,
        EVENT_UIA_EVENTID_START = 0x4E00,
        EVENT_UIA_EVENTID_END = 0x4EFF,
        EVENT_UIA_PROPID_START = 0x7500,
        EVENT_UIA_PROPID_END = 0x75FF,
        EVENT_CONSOLE_CARET = 0x4001,
        EVENT_CONSOLE_UPDATE_REGION = 0x4002,
        EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003,
        EVENT_CONSOLE_UPDATE_SCROLL = 0x4004,
        EVENT_CONSOLE_LAYOUT = 0x4005,
        EVENT_CONSOLE_START_APPLICATION = 0x4006,
        EVENT_CONSOLE_END_APPLICATION = 0x4007,
        EVENT_CONSOLE_END = 0x40FF,
        EVENT_OBJECT_CREATE = 0x8000,               // hwnd ID idChild is created item
        EVENT_OBJECT_DESTROY = 0x8001,              // hwnd ID idChild is destroyed item
        EVENT_OBJECT_SHOW = 0x8002,                 // hwnd ID idChild is shown item
        EVENT_OBJECT_HIDE = 0x8003,                 // hwnd ID idChild is hidden item
        EVENT_OBJECT_REORDER = 0x8004,              // hwnd ID idChild is parent of zordering children
        EVENT_OBJECT_FOCUS = 0x8005,                // hwnd ID idChild is focused item
        EVENT_OBJECT_SELECTION = 0x8006,            // hwnd ID idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex
        EVENT_OBJECT_SELECTIONADD = 0x8007,         // hwnd ID idChild is item added
        EVENT_OBJECT_SELECTIONREMOVE = 0x8008,      // hwnd ID idChild is item removed
        EVENT_OBJECT_SELECTIONWITHIN = 0x8009,      // hwnd ID idChild is parent of changed selected items
        EVENT_OBJECT_STATECHANGE = 0x800A,          // hwnd ID idChild is item w/ state change
        EVENT_OBJECT_LOCATIONCHANGE = 0x800B,       // hwnd ID idChild is moved/sized item
        EVENT_OBJECT_NAMECHANGE = 0x800C,           // hwnd ID idChild is item w/ name change
        EVENT_OBJECT_DESCRIPTIONCHANGE = 0x800D,    // hwnd ID idChild is item w/ desc change
        EVENT_OBJECT_VALUECHANGE = 0x800E,          // hwnd ID idChild is item w/ value change
        EVENT_OBJECT_PARENTCHANGE = 0x800F,         // hwnd ID idChild is item w/ new parent
        EVENT_OBJECT_HELPCHANGE = 0x8010,           // hwnd ID idChild is item w/ help change
        EVENT_OBJECT_DEFACTIONCHANGE = 0x8011,      // hwnd ID idChild is item w/ def action change
        EVENT_OBJECT_ACCELERATORCHANGE = 0x8012,    // hwnd ID idChild is item w/ keybd accel change
        EVENT_OBJECT_INVOKED = 0x8013,              // hwnd ID idChild is item invoked
        EVENT_OBJECT_TEXTSELECTIONCHANGED = 0x8014, // hwnd ID idChild is item w? test selection change
        EVENT_OBJECT_CONTENTSCROLLED = 0x8015,
        EVENT_SYSTEM_ARRANGMENTPREVIEW = 0x8016,
        EVENT_OBJECT_END = 0x80FF,
        EVENT_AIA_START = 0xA000,
        EVENT_AIA_END = 0xAFFF
    }

    //SetWinEventHook() Object Ids
    public enum SWEH_ObjectId : long
    {
         OBJID_WINDOW =             0x00000000,
         OBJID_SYSMENU =            0xFFFFFFFF,
         OBJID_TITLEBAR =           0xFFFFFFFE,
         OBJID_MENU =               0xFFFFFFFD,
         OBJID_CLIENT =             0xFFFFFFFC,
         OBJID_VSCROLL =            0xFFFFFFFB,
         OBJID_HSCROLL =            0xFFFFFFFA,
         OBJID_SIZEGRIP =           0xFFFFFFF9,
         OBJID_CARET =              0xFFFFFFF8,
         OBJID_CURSOR =             0xFFFFFFF7,
         OBJID_ALERT =              0xFFFFFFF6,
         OBJID_SOUND =              0xFFFFFFF5,
         OBJID_QUERYCLASSNAMEIDX =  0xFFFFFFF4,
         OBJID_NATIVEOM =           0xFFFFFFF0
    }

    private static SWEH_dwFlags WinEventHookInternalFlags = SWEH_dwFlags.WINEVENT_OUTOFCONTEXT | 
                                                            SWEH_dwFlags.WINEVENT_SKIPOWNPROCESS | 
                                                            SWEH_dwFlags.WINEVENT_SKIPOWNTHREAD;

    public delegate void WinEventDelegate(IntPtr hWinEventHook, 
                                          SWEH_Events eventType, 
                                          IntPtr hwnd, 
                                          SWEH_ObjectId idObject, 
                                          long idChild, 
                                          uint dwEventThread, 
                                          uint dwmsEventTime);

    public static IntPtr WinEventHookRange(SWEH_Events _eventFrom, 
                                           SWEH_Events _eventTo, 
                                           WinEventDelegate _delegate, 
                                           uint idProcess, uint idThread)
    {
        new UIPermission(UIPermissionWindow.AllWindows).Demand();
        return UnsafeNativeMethods.SetWinEventHook(_eventFrom, _eventTo, 
                                                   IntPtr.Zero, _delegate, 
                                                   idProcess, idThread, 
                                                   WinEventHookInternalFlags);
    }

    public static IntPtr WinEventHookOne(SWEH_Events _event, WinEventDelegate _delegate, uint idProcess, uint idThread)
    {
        new UIPermission(UIPermissionWindow.AllWindows).Demand();
        return UnsafeNativeMethods.SetWinEventHook(_event, _event, 
                                                   IntPtr.Zero, _delegate, 
                                                   idProcess, idThread, 
                                                   WinEventHookInternalFlags);
    }

    public static bool WinEventUnhook(IntPtr hWinEventHook)
    {
        return UnsafeNativeMethods.UnhookWinEvent(hWinEventHook);
    }

    public static uint GetWindowThread(IntPtr hWnd)
    {
        new UIPermission(UIPermissionWindow.AllWindows).Demand();
        return UnsafeNativeMethods.GetWindowThreadProcessId(hWnd, IntPtr.Zero);
    }

    public static RECT GetWindowRect(IntPtr hWnd)
    {
        RECT rect = new RECT();
        bool _result = SafeNativeMethods.GetWindowRect(hWnd, ref rect);
        return rect;
    }
}

[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
}

[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId);

    [DllImport("user32.dll", SetLastError = false)]
    public static extern IntPtr SetWinEventHook(Hook.SWEH_Events eventMin, Hook.SWEH_Events eventMax, 
                                                IntPtr hmodWinEventProc, Hook.WinEventDelegate lpfnWinEventProc,
                                                uint idProcess, uint idThread, Hook.SWEH_dwFlags dwFlags);

    [DllImport("user32.dll", SetLastError = false)]
    public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
}
Run Code Online (Sandbox Code Playgroud)

  • 对于后代来说,这是一个与我的渲染同步间隔完全无关的问题。 (2认同)