Прослушивание другого окна изменяет размер событий в C # - PullRequest
21 голосов
/ 21 мая 2010

Я реализую небольшое приложение (наблюдатель), которое должно «прикрепиться» к нижней части другого окна (наблюдается). Последнее не является окном внутри приложения.

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

Однако это очень не элегантное решение. Я хотел бы прослушать событие изменения размера наблюдаемого окна, чтобы наблюдатель реагировал только тогда, когда это необходимо.

Я предполагаю, что должен использовать ловушку, и я нашел множество способов сделать это, но мое отсутствие знаний о WinAPI C мешает мне понять, какой ловушку мне нужно создать и как (pinvoke / parameters / etc) .

Я почти уверен, что это довольно тривиально, и некоторые из вас, знакомые с C / C ++ и WinAPI, будут иметь готовый ответ;)

Спасибо

Ответы [ 4 ]

8 голосов
/ 21 мая 2010

В продолжение ответа Криса Тейлора: вместо того, чтобы самостоятельно выполнять внутреннее взаимодействие, вы можете использовать ManagedWinApi , который содержит класс Hook.

РЕДАКТИРОВАТЬ: Для использования ManagedWinApi. Где-то в вашем коде:

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

Для обратного вызова, ссылка CallWndProc и CWPSTRUCT :

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
}
4 голосов
/ 08 октября 2013

Я предлагаю вам использовать 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);

См. Также: перехват событий

4 голосов
/ 21 мая 2010

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

Вы спрашиваете, как создать глобальный хук, используя C #, или вы счастливы создать хук в C ++, а затем взаимодействовать с ним из .NET? Второй вариант - маршрут, по которому я бы пошел.

По сути, я бы сделал следующее:

1 - Создайте глобальный хук в C и экспортируйте функции в InstallHook и UninstallHook, которые можно вызывать из приложения C # с помощью Interop. InstallHook берет окно в своем приложении на C #.

2 - Установленная функция перехвата отправляет пользовательское сообщение в окно C #, предоставляемое при вызове InstallHook, когда появляется интересующее вас сообщение, например WM_SIZE в вашем случае.

3- В приложении C # ваше окно, которое получает отправленные сообщения из ловушки, переопределит WndProc для обработки пользовательского сообщения.

Это план одного подхода.

3 голосов
/ 30 июня 2016

Я столкнулся с тем же самым в некотором коде, с которым работал, и обнаружил, что не могу внедрить управляемый .DLL в процесс.

Не желая писать неуправляемую DLL на C ++, которая использовала бы SetWindowsHook, я выбрал комбинацию SetWinEventHook, которая сигнализирует, когда окно начинается и заканчивается событием перемещения, и 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);
}
...