2010-05-21 139 views
16

我正在实现一个小型应用程序(观察者),它需要将自己“附加”到另一个窗口(观察)的底部。后者不是应用程序内的窗口。聆听另一个窗口调整C#中的事件大小

在这一刻,我通过获取窗口的hWnd和定期在一个线程中查询观察窗口的位置,相应地移动观察窗口来解决问题。

但是,这是一个非常不雅的解决方案。我想要做的是听取观察窗口的大小调整事件,以便观察者仅在必要时才会作出反应。我认为我应该使用一个钩子,并且我发现了很多方法,但是我对C WinAPI的知识缺乏阻碍了我理解我需要创建哪个钩子以及如何(pinvoke/parameters/etc )。

我敢肯定,这是很显然的,一些你熟悉C/C++和WinAPI的将有答案准备在手边;)

感谢

+0

只在标题和标签中提及C#。你使用C#还是C? – Yuvi 2010-05-21 07:08:05

+0

@Yuvi,我的猜测是他想听C#中的一个“事件”,这个事件是在另一个不在他的“域”中的应用程序中触发的。 – 2010-05-21 07:09:36

+0

@Yuvi我提到了C#,因为我的基本程序是用C#编写的。使用C++ for Business应用程序不太舒服;) – 2010-05-21 09:53:26

回答

7

扩展Chris Taylor的答案:您可以使用ManagedWinApi,其中包含Hook类,而不是自己执行本机互操作。

编辑:使用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 
} 
+1

事实上,我偶然发现了ManagedWinAPi,但文档并不像我喜欢的那样自我解释,在WinAPI中看到了我的无能。我正在研究这些例子。 据我所知,我并不需要一个全局系统,因为直接挂钩窗口就足够了。我只需要收到有关调整大小/移动事件的通知。理想情况下,这应该发生在C#事件中。 我需要弄清楚的是如何连接点,因此任何建议都非常值得欢迎,特别是熟悉ManagedWinApi的人员。 – 2010-05-21 09:57:27

+0

谢谢!这非常清楚。我会尽快测试它。作为参考,CWPSTRUCT在这里http://www.pinvoke.net/default.aspx/Structures/CWPSTRUCT.html – 2010-05-21 10:29:52

+0

你的建议的代码只钩住C#项目中的窗口。我需要挂钩到外部窗口。谷歌搜索我明白,这似乎不可能在C#中,除非Magadewinapi有办法做到这一点。我试图将Hook构造函数中的全局参数设置为true,但是我敲打了会话。任何建议? – 2010-05-24 12:18:22

4

一个WH_CALLWNDPROC钩可能就足够了,这将允许您监视所有目标窗口的消息。

你问的是如何使用C#创建一个全局钩子,或者你很乐意在C++中创建钩子,然后与.NET中的钩子进行互操作?第二种选择是我会去的路线。

基本上从我的头顶,我会做如下

1在C上创建全局钩子,和导出功能,以InstallHookUninstallHook,它可以从你的C#应用​​程序中使用互操作调用。 InstallHook在你的C#应用​​程序中获取窗口的一个宏。

2-安装的挂钩函数发送一条自定义消息到InstallHook的调用中提供的C#窗口,当您遇到有关您感兴趣的消息(如WM_SIZE)的情况。

3-在C#应用程序中,从钩子接收已发布消息的窗口将覆盖WndProc以处理自定义消息。

这是一种方法的概要。

4

我建议你使用的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); 

参见:event hooks

+0

我相信这是有效的。我刚刚编写了一个利用SetWinEventHook的程序。 – 2014-12-26 08:59:56

1

我在我正在使用的一些代码中遇到同样的事情并发现我无法将一个托管的.DLL注入进程。

因为不想写一个C++的非托管的DLL使用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); 
}