簡體   English   中英

聽另一個窗口調整C#中的事件

[英]Listening to another window resize events in C#

我正在實現一個小應用程序(觀察者),需要將自己“附加”到另一個窗口的底部(觀察到)。 后者不是應用程序內部的窗口。

此時我通過獲取窗口的hWnd並在線程中定期查詢觀察窗口的位置來解決,相應地移動觀察者窗口。

然而,這是一個非常不優雅的解決方案。 我想做的是聽取觀察窗口的resize事件,以便觀察者只在必要時做出反應。

我假設我應該使用一個鈎子,並且我找到了很多方法,但是我對C WinAPI缺乏了解阻礙了我需要創建哪個鈎子以及如何(pinvoke / parameters / etc)。

我很確定這是非常簡單的,你們中的一些熟悉C / C ++和WinAPI就可以隨時得到答案;)

謝謝

擴展Chris Taylor的答案:您可以使用包含Hook類的ManagedWinApi ,而不是自己進行本地互操作。

編輯:使用ManagedWinApi。 代碼中的某個地方:

Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false);
MyHook.Callback += MyHookCallback;
MyHook StartHook();

對於回調,引用CallWndProcCWPSTRUCT

private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext)
{
    if (code >= 0)
    {
        // You will need to define the struct
        var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT));
        // Do something with the data
    }
    return 0; // Return value is ignored unless you set callNext to false
}

我建議你使用WinEvents:

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

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

另請參見: 事件掛鈎

WH_CALLWNDPROC掛鈎可能就足夠了,這將允許您監視發往目標窗口的所有消息。

您是否在詢問如何使用C#創建全局鈎子,或者您是否樂意在C ++中創建鈎子然后與.NET交互? 第二個選擇是我要去的路線。

基本上我的頭腦,我會做的是以下

1-在C中創建全局鈎子,並將函數導出到InstallHookUninstallHook ,可以使用Interop從C#應用程序調用。 InstallHook在你的C#應用​​程序中占用了一個窗口。

2-如果您感興趣的消息(如WM_SIZE ,安裝的掛鈎功能會將自定義消息發布到InstallHook調用中提供的C#窗口。

3-在C#應用程序中,從鈎子接收已發布消息的窗口將覆蓋WndProc以處理自定義消息。

這是一種方法的概述。

我在使用的一些代碼中遇到了同樣的問題,並發現我無法在進程中注入托管的.DLL。

不想寫一個使用SetWindowsHook的C ++非托管DLL,我選擇了SetWinEventHook的組合,它在窗口啟動時發出信號並結束一個move事件,一個Timer在窗口移動時進行輪詢以使其看起來跟隨窗口當它移動。

這是一個可以做到這一點的(非常簡化的)類版本(只需用代碼替換TODO以移動窗口或引發事件)。

public class DockingHelper
{
    private readonly uint m_processId, m_threadId;

    private readonly IntPtr m_target;

    // Needed to prevent the GC from sweeping up our callback
    private readonly WinEventDelegate m_winEventDelegate;
    private IntPtr m_hook;

    private Timer m_timer;

    public DockingHelper(string windowName, string className)
    {
        if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value");

        m_target = FindWindow(className, windowName);
        ThrowOnWin32Error("Failed to get target window");

        m_threadId = GetWindowThreadProcessId(m_target, out m_processId);
        ThrowOnWin32Error("Failed to get process id");

        m_winEventDelegate = WhenWindowMoveStartsOrEnds;
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool UnhookWinEvent(IntPtr hWinEventHook);

    [DllImport("user32.dll")]
    private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    private void ThrowOnWin32Error(string message)
    {
        int err = Marshal.GetLastWin32Error();
        if (err != 0)
            throw new Win32Exception(err, message);
    }

    private RECT GetWindowLocation()
    {
        RECT loc;
        GetWindowRect(m_target, out loc);
        if (Marshal.GetLastWin32Error() != 0)
        {
            // Do something useful with this to handle if the target window closes, etc.
        }
        return loc;
    }

    public void Subscribe()
    {
        // 10 = window move start, 11 = window move end, 0 = fire out of context
        m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0);
    }

    private void PollWindowLocation(object state)
    {
        var location = GetWindowLocation();
        // TODO: Reposition your window with the values from location (or fire an event with it attached)
    }

    public void Unsubscribe()
    {
        UnhookWinEvent(m_hook);
    }

    private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread.
            return;

        if (eventType == 10) // Starts
        {
            m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite);
            // This is always the original position of the window, so we don't need to do anything, yet.
        }
        else if (eventType == 11)
        {
            m_timer.Dispose();
            m_timer = null;
            var location = GetWindowLocation();
            // TODO: Reposition your window with the values from location (or fire an event with it attached)
        }
    }

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

    private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM