我正在实现一个小应用程序(观察者),需要将自己"附加"到另一个窗口的底部(观察到).后者不是应用程序内部的窗口.
此时我通过获取窗口的hWnd并在线程中定期查询观察窗口的位置来解决,相应地移动观察者窗口.
然而,这是一个非常不优雅的解决方案.我想做的是听取观察窗口的resize事件,以便观察者只在必要时做出反应.
我假设我应该使用一个钩子,并且我找到了很多方法,但是我对C WinAPI缺乏了解阻碍了我需要创建哪个钩子以及如何(pinvoke/parameters/etc).
我很确定这是非常简单的,你们中的一些熟悉C/C++和WinAPI就可以随时得到答案;)
谢谢
扩展Chris Taylor的答案:您可以使用包含类的ManagedWinApi,而不是自己进行本地互操作Hook.
编辑:使用ManagedWinApi.代码中的某个地方:
Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false);
MyHook.Callback += MyHookCallback;
MyHook StartHook();
Run Code Online (Sandbox Code Playgroud)
对于回调,引用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
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我在使用的一些代码中遇到了同样的问题,并发现我无法将托管 .DLL 注入到进程中。
我不想编写一个使用 SetWindowsHook 的 C++ 非托管 DLL,所以我使用了 的组合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);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11343 次 |
| 最近记录: |