创建一个C#项目,成功地为单个应用程序创建一个whu SHELL钩子

创建一个C#项目,成功地为单个应用程序创建一个whu SHELL钩子,c#,C#,我试图创建一个项目,可以检测当任务栏图标闪烁。您通常通过在应用程序中创建一个钩子并检查适当的消息来实现这一点。在这种情况下,可以通过在应用程序中创建WH_SHELL钩子、等待HSHELL_重画消息并检查lParam变量是否为TRUE来检测消息 根据文档,如果lParam值为true,那么wParam变量中的窗口句柄引用的窗口将闪烁。太好了 问题是,就我的一生而言,我无法弄清楚如何实际创建一个WH_SHELL钩子。创建钩子的代码似乎相当简单: using System; using System.

我试图创建一个项目,可以检测当任务栏图标闪烁。您通常通过在应用程序中创建一个钩子并检查适当的消息来实现这一点。在这种情况下,可以通过在应用程序中创建WH_SHELL钩子、等待HSHELL_重画消息并检查lParam变量是否为TRUE来检测消息

根据文档,如果lParam值为true,那么wParam变量中的窗口句柄引用的窗口将闪烁。太好了

问题是,就我的一生而言,我无法弄清楚如何实际创建一个WH_SHELL钩子。创建钩子的代码似乎相当简单:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

class Flashy {
  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  protected static extern IntPtr SetWindowsHookEx(int code, HookProc func, IntPtr hInstance, int threadID);

  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

  [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern IntPtr GetModuleHandle(string lpModuleName);

  private int WH_SHELL = 10;
  static IntPtr hHook;

  public void doStuff() {
    using (Process process = Process.GetCurrentProcess())
    using (ProcessModule module = process.MainModule)
    {
      IntPtr hModule = GetModuleHandle(module.ModuleName);
      MyDLL.Class1.hHook = SetWindowsHookEx(WH_SHELL, MyDLL.Class1.MyHookProc, hModule,     0);
    }
  }
}
现在,据我所知,WH_SHELL需要一个dll。所以我的看起来像:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace FlashyDLL
{
  public class Class1
  {

    //This is the Import for the CallNextHookEx function.
    //Use this function to pass the hook information to the next hook procedure in chain.
    [DllImport("user32.dll", CharSet = CharSet.Auto,
     CallingConvention = CallingConvention.StdCall)]
    static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    public static IntPtr hHook;

    public static int MyHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
      if (nCode < 0)
        return CallNextHookEx(hHook, nCode, wParam, lParam);

      switch (nCode)
      {
        case 6: //HSHELL_REDRAW
          break;
      }
      return CallNextHookEx(hHook, nCode, wParam, lParam);
    }
  }
}
步骤2:PInvokes:

public class Win32 {
    [DllImport("user32.dll", EntryPoint = "RegisterWindowMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern int RegisterWindowMessage(string lpString);

    [DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern int RegisterShellHookWindow(IntPtr hWnd);

    [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
}
public class Win32 {
    [DllImport("user32.dll")]
    public static extern bool RegisterShellHookWindow(IntPtr handle);
}
步骤3:激活挂钩:

var taf = new TestAlertForm();
myWindow = FindWindowByCaption(0, "My Window Title");
uMsgNotify = RegisterWindowMessage("SHELLHOOK");
Win32.RegisterShellHookWindow(taf.Handle);
var taf = new TestAlertForm();
Win32.RegisterShellHookWindow(taf.Handle);

好吧,事实证明这在.Net中有点棘手——但这是可能的。 您可以使用
SetWindowsHookEx
通过ID直接钩住单个线程,但这种方法更简单,更容易与窗口匹配

警告:这依赖于内部
USER32.dll
调用

步骤1:拾取消息的窗口:

using System;
using System.Windows.Forms;

public class TestAlertForm : Form 
{
    private IntPtr myWindow;
    private int uMsgNotify = 0;
    readonly IntPtr HSHELL_FLASH = new IntPtr(0x8006);

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == uMsgNotify)
        {
            if (m.WParam == HSHELL_FLASH)
            {
                if (m.LParam == myWindow)
                {
                    MessageBox.Show("FLASH!");
                }
            }
        }
        base.WndProc(ref m);
    }
}
using System;
using System.Windows.Forms;

public class TestAlertForm : Form 
{
    readonly IntPtr HSHELL_FLASH = new IntPtr(0x8006);

    protected override void WndProc(ref Message m)
    {
        if (m.WParam == HSHELL_FLASH)
        {
            // TODO: DO WORK HERE
            // m.LParam <-- A handle to the window that is 'flashing'
            //              You should be able to match this to your target.
            MessageBox.Show("FLASH!");
        }
        base.WndProc(ref m);
    }
}
步骤3:激活挂钩:

var taf = new TestAlertForm();
myWindow = FindWindowByCaption(0, "My Window Title");
uMsgNotify = RegisterWindowMessage("SHELLHOOK");
Win32.RegisterShellHookWindow(taf.Handle);
var taf = new TestAlertForm();
Win32.RegisterShellHookWindow(taf.Handle);
在所有这些之后,您应该会看到每个闪光灯的消息框。 这只是一个快速的破解,我还没有做过很多测试


祝你好运

由于跳过了所有错误处理,因此无法找出它不起作用的原因。但是不,这个钩子需要一个可以注入到其他进程中的DLL。这样的DLL不能用C#编写,其他进程没有CLR来执行代码。需要非托管语言。64位操作系统也会给你带来相当大的麻烦。没有什么东西会被钉住。GCHandle.Alloc的唯一原因是防止垃圾收集器破坏委托对象。静态变量也可以。不。请注意,您实际上从未将GCHandleType.Pinned传递到Alloc()或调用GCHandle.AddrOfPinnedObject()。因此代理对象不会固定。避免回调崩溃的唯一要求是确保GC可以看到对对象的引用,因此它不会收集它。使用GCHandle.Alloc()可以,静态变量也可以,甚至类中的实例字段也可以,只要它存在足够长的时间。不,hookDelegate是委托,不是方法。如果你想了解更多,可以通过谷歌搜索“CLR存根堆”。这是分配回调存根的堆,它没有被压缩