Da się w .NET założyć globalnego hooka. Tylko jest mały problem z kompatybilnością managed <=> unmanaged i póki co potrafię zastosować tylko obejście, które obniża wydajność.
Nie wiem dlaczego odrzucasz na początku konieczność korzystania z zewnętrznej biblioteki, ale to konieczność, jeżeli chcemy założyć globalny hook, ponieważ ta biblioteka musi być wstrzykiwana.
Tak więc mamy projekt biblioteki:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
using namespace System;
namespace GlobalMessageHook {
public ref class Hook
{
private:
Hook() { }
static Hook^ instance;
public:
HHOOK hook;
property static Hook^ Instance
{
Hook^ get()
{
if(instance == nullptr)
instance = gcnew Hook();
return instance;
}
}
void Initialize()
{
hook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProc, NULL, GetCurrentThreadId());
}
delegate void NewMessageEventHandler(IntPtr^ hwnd, int message, int lParam, int wParam);
NewMessageEventHandler^ newMessageEventHandler;
event NewMessageEventHandler^ OnNewMessage
{
void raise(IntPtr^ hwnd, int message, int lParam, int wParam)
{
newMessageEventHandler(hwnd, message, lParam, wParam);
}
void add(NewMessageEventHandler^ eventHandler)
{
newMessageEventHandler += eventHandler;
}
void remove(NewMessageEventHandler^ eventHandler)
{
newMessageEventHandler -= eventHandler;
}
}
};
}
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
GlobalMessageHook::Hook^ hook = GlobalMessageHook::Hook::Instance;
if(nCode < 0)
return CallNextHookEx(hook->hook, nCode, wParam, lParam);
CWPSTRUCT* messageInfo = (CWPSTRUCT*)lParam;
hook->OnNewMessage(gcnew IntPtr(messageInfo->hwnd),
messageInfo->message,
messageInfo->lParam,
messageInfo->wParam);
return CallNextHookEx(hook->hook, nCode, wParam, lParam);
}
Do której możemy dodać sobie referencję gdziekolwiek chcemy i podłączyć się pod event:
Hook.Instance.Initialize();
Hook.Instance.OnNewMessage += new GlobalMessageHook.Hook.NewMessageEventHandler(Instance_OnNewMessage);
Queue<Message> messageQueue = new Queue<Message>();
void Instance_OnNewMessage(ValueType hwnd, int message, int lParam, int wParam)
{
this.messageQueue.Enqueue(Message.Create((IntPtr)hwnd, message, (IntPtr)lParam, (IntPtr)wParam));
}
I już mówię dlaczego wykorzystałem kolejkę: dowolne odwołanie się do jakiejkolwiek kontrolki Windows Forms kończyło się wyjątkiem naruszenia pamięci. Dlaczego? Nie wiem. Ale już z tej kolejki możemy pobierać komunikaty i robić z nimi co chcemy.
edit: zapomniałem o UnhookWindowsHookEx, ale już idę spać ;).