如何写一个管理全局钩子窗口创建和销毁

介绍 在几乎所有程序员的职业生涯中,都会遇到以下问题: 他的应用程序需要某种闪亮的特性,需要他在打开或关闭新窗口时得到通知。 在c++的时代,这是相当简单和直接的。只需将你的应用程序注册为WM_DESTROY和WM_CREATE的钩子。 不幸的是,随着。net和托管应用程序的引入,这种情况发生了改变,实现起来更加困难。 在本文中,我们将讨论一个实现,该实现允许您注册事件,当创建新窗口或旧窗口被销毁时,这些事件将向您发出信号。 背景 我目前正在写一个应用程序,在其他窗口上执行操作,并正在寻找一个简单的方法,以获得回调每当一个新窗口被创建。我用c#来做这个。 经过几天的internet搜索,我意识到。net中有一种方法可以得到这样的回调。 在Win32 API中有一种方法是通过调用方法SetWindowsHookEx。然而,要让它在所有的windows(不仅仅是在你的AppDomain中创建的windows)中工作,你需要注册一个所谓的“全局钩子”,这在. net中是不可能的。要使全局钩子工作,需要将DLL注入到其他进程中。这在托管dll中是不可能的! 因此,我提出了一个与SetWindowsHookEx几乎相同的解决方案,但仅限于在创建或销毁窗口时发送事件。 有关更多背景信息,请参见此链接。 使用的代码 我的解决方案包含在一个文件中,由两个类组成: 隐藏,复制Code

public class WindowHookEventArgs
{
    public IntPtr Handle = IntPtr.Zero;
    public string WindowTitle = null;
    public string WindowClass = null;

    public override string ToString()
    { 
        return "[WindowHookEventArgs|Title:" + WindowTitle + "|Class:"
                + WindowClass + "|Handle:" + Handle + "]";
    }
}

这个类只包含有关所引发事件的信息,并为您提供窗口句柄(在c++中通常称为hWnd)。 另一个类是WindowHookNet,它是一个单例。我选择将它实现为一个单例,以避免当这个类的十二个或更多实例持续拉取所有打开的窗口时出现性能问题。 首先,你有两个委托: 隐藏,复制Code

public delegate void WindowHookDelegate(object aSender, WindowHookEventArgs aArgs);
private delegate bool EnumDelegate(IntPtr hWnd, int lParam);

WindowHookDelegate用于注册由WindowHookNet引发的事件。EnumDelegate在内部用于检索当前打开的所有窗口的列表。 接下来是四个事件。两个私人和两个公共活动: 隐藏,收缩,复制Code

private event WindowHookDelegate InnerWindowCreated;
private event WindowHookDelegate InnerWindowDestroyed;

/// <summary>
/// register for this event if you want to be informed about
/// the creation of a window
/// </summary>
public event WindowHookDelegate WindowCreated
{
    add
    {
        InnerWindowCreated += value;
        if (!iRun)
        {
            startThread();
        }
    }
    remove
    {
        InnerWindowCreated -= value;

        // if no more listeners for the events
        if (null == InnerWindowCreated &&
            null == InnerWindowDestroyed)
            iRun = false;
    }
}

/// <summary>
/// register for this event, if you want to be informed about
/// the destruction of a window
/// </summary>
public event WindowHookDelegate WindowDestroyed
{
    add
    {
        InnerWindowDestroyed += value;
        if (!iRun)
        {
            startThread();
        }
    }
    remove
    {
        InnerWindowDestroyed -= value;

        // if no more listeners for the events
        if (null == InnerWindowCreated &&
            null == InnerWindowDestroyed)
            iRun = false;
    }
}

private void onWindowCreated(WindowHookEventArgs aArgs)
{
    if (null != InnerWindowCreated)
        InnerWindowCreated(this, aArgs);
}

private void onWindowDestroyed(WindowHookEventArgs aArgs)
{
    if (null != InnerWindowDestroyed)
        InnerWindowDestroyed(this, aArgs);
}
#endregion 

当创建或销毁一个新窗口时,会引发InnerWindowCreated和InnerWindowDestroyed事件。公共事件(WindowCreated, WindowDestroyed)将新的委托附加到内部事件并启动和停止线程,这取决于是否仍然有相应内部事件的注册侦听器。onWindowCreated, onWindowDestroyed是用来防止NullReferenceException的。 在那之后,有一些PInvoke / DLLImports: 隐藏,复制Code

DllImport("user32.dll", EntryPoint = "EnumDesktopWindows",
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool _EnumDesktopWindows(IntPtr hDesktop,
EnumDelegate lpEnumCallbackFunction, IntPtr lParam);

DllImport("user32.dll", EntryPoint = "GetWindowText",
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int _GetWindowText(IntPtr hWnd,
StringBuilder lpWindowText, int nMaxCount);

// GetClassName
[DllImport("user32.dll", EntryPoint = "GetClassName", ExactSpelling = false,
            CharSet = CharSet.Auto, SetLastError = true)]
private static extern int _GetClassName(IntPtr hwnd, StringBuilder lpClassName,
            int nMaxCount);

EnumDesktopWindows用于获取所有打开窗口的当前列表。GetWindowText用于获取窗口的标题,GetClassName用于获取窗口的类。 隐藏,复制Code

private void run()

基本上,它只是每500ms就轮询一次新windows,并处理WindowHookNet的启动和终止。 fireatedwindows扫描iNewWindowList中不在iOldWindowList中的条目,因此代表新的窗口。如果找到这样一个条目,它将被放入一个特殊的列表中,扫描完成后,所有的“new window事件”将被触发。 隐藏,复制Code

private void fireCreatedWindows()
        {
            iEventsToFire.Clear();
            foreach (IntPtr tPtr in iNewWindowList.Keys)
            {
                // if the new list contains a key that is not
                // in the old list, that window has been created
                // add it into the fire list and to the "old" list
                if (!iOldWindowList.ContainsKey(tPtr))
                {
                    iEventsToFire.Add(iNewWindowList[tPtr]);
                    //cLogger.Debug("new window found:" + 
                    //iEventsToFire[iEventsToFire.Count - 1]);
                }
            }

            // you need to remove / add things later, because
            // you are not allowed to alter the dictionary during iteration
            foreach (WindowHookEventArgs tArg in iEventsToFire)
            {
                //cLogger.Debug("sending WM_CREATE:" + tArg);
                iOldWindowList.Add(tArg.Handle, tArg);
                onWindowCreated(tArg);
            }
        }

完全相同的情况发生在fireClosedWindows,但反过来(扫描iOldWindowList条目不再在iNewWindowList)。 EnumWindowsProc是EnumDelegate的回调方法,每个打开的窗口都会调用它。它检索窗口的类和窗口的标题,并创建WindowHookEventArgs对象。 历史 添加了关于这个类工作方式和创建它的原因的大量信息 本文转载于:http://www.diyabc.com/frontweb/news5169.html

posted @ 2020-08-09 13:11  Dincat  阅读(377)  评论(0编辑  收藏  举报