Now I'm trying to implement a global shell hook. I have succeeded to a
certain extent. i.e., my code works for a while but then it breaks.
Let
me explain.
I am able to setup the hook successfully. The SetWindowsHookEx
function
returns a valid hook handle. My program also captures the activation
of
it's form. (that is a HSHELL_WINDOWACTIVATED message). I also have a
CMD prompt window open. When I click on that window, my program is
able
to capture the click. When I subsequently click back on my programs
form, it still works fine. If I call UnhookWindowsHookEx now, it
returns a value of True (indicating that the hook was successfully
removed).
The problem occurs when I try clicking on any window other than the
CMD
prompt. My program does not capture the click, and when I try to
unhook
after this, the function returns False.
My code and output are attached below. This is driving me nuts. I've
been looking up and down for a week now to try and get this code to
work. If global hooks really aren't supported in .NET, then how does
the mouse hook work? And why doesn't the same thing work with shell
hooks?
This is the class where I've defined the API calls. I've implemented
it
as a DLL (which is what MSDN says I have to do if I want to use global
hooks):
Imports System.Runtime.InteropServices
Public Class Class1
Const WH_SHELL As Integer = 10
Public Delegate Function HookCallBack(ByVal nCode As Integer,
ByVal
wParam As Integer, ByVal lParam As Integer) As Integer
Private MyHook As IntPtr
Private Class Hook
Declare Auto Function SetWindowsHookEx Lib "user32.dll" (ByVal
idHook As Integer, ByVal lpfn As HookCallBack, ByVal hMod As Integer,
ByVal dwThreadID As UInt32) As IntPtr
Declare Auto Function UnhookWindowsHookEx Lib "user32.dll"
(ByVal hhk As IntPtr) As Boolean
End Class
Public Function ShellProc(ByVal nCode As Integer, ByVal wParam As
Integer, ByVal lParam As Integer) As Integer
Console.WriteLine(nCode & " " & wParam & " " & lParam)
End Function
Public Function SetHook() As IntPtr
MyHook = Hook.SetWindowsHookEx(WH_SHELL, AddressOf ShellProc,
Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32,
UInt32.Parse(0))
Return MyHook
End Function
Public Function ReleaseHook() As Boolean
Return (Hook.UnhookWindowsHookEx(MyHook))
End Function
End Class
And here is the code I'm using in my form to start the hook:
Dim s As New sh_hook.Class1()
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As
System.EventArgs) Handles Button1.Click
Console.WriteLine(s.SetHook())
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e
As
System.EventArgs) Handles Button2.Click
Console.WriteLine(s.ReleaseHook())
End Sub
The output from my program is below:
50201303 (Hook handle)
4 328420 0 (Registered a click on my form)
4 1900958 0 (Registered a click on the CMD window)
True (UnHook returns True)
18547381 (New hook)
4 328420 0 (Click on form)
4 1900958 0 (Click on CMD window)
4 328420 0 (Click on form)
4 1900958 0 (Click on CMD window)
True (UnHook returns True)
18612917 (New hook)
4 328420 0 (Click on form)
4 1900958 0 (Click on CMD window)
<<<<<<<<<<<<<<< I clicked on Notepad here >>>>>>>>>>>>>>>>>>>>>
False (UnHook returns false)
Anyone who has any idea what's going wrong, please post!
Thanks,
ShellD00d
> I have read on http://support.microsoft.com/?id=319524 that .NET does
> not support implementation of global hooks. Yet, I tried the sample
> code provided as FAQ on vbCity to implement a global hook on the mouse
> and it seems to work.
It's very and very strange, I a bit modified .NET hook code from MSDN
Magazine article "Windows Hooks in the .NET Framework" by "Dino Esposito",
to test global mouse hook, SetWindowsHookEx returned non-NULL value, but
hook procedure of course isn't called at all. I'd be interested to know
whether your hook DLL is loaded into other processes (for example it's
possible to track it by SysInternals's Process Explorer
http://www.sysinternals.com/ntw2k/freeware/procexp.shtml,) and it makes
sense replace Console.WriteLine call with OutputDebugString API, and include
process ID into each log by GetCurrentProcessId call, both functions
implemented in kernel32.dll. DebugView monitor intercepts and traces all
OutputDebugString output
(http://www.sysinternals.com/ntw2k/freeware/debugview.shtml).
..
Regards,
Vadim.
I've re-written my shell hook in VC++ and made a dll file. The hook
works great, but now I need a way to pass messages from my DLL to my
.net form. I've been trying SendMessage, PostMessage,
SendMessageNotify.... aaarghhh..!!
In my form, I've setup a thread-specific hook (which is officially
supported, phew!) to trap all messages. I'm using a WH_GETMESSAGE hook
to do this. The problem now is that my form doesn't trap all the
messages I'm trying to send.
My global hook is triggering fine. I've been dumping output to a file
and it captures all the events very nicely. But when I try to use
PostMessage to send a message to my .NET app, it seems to get lost.
Either that or my local hook isn't working right. SendMessage doesn't
seem to work at all.
What's the best way to send these notifications? Is there any way I
can make my DLL call a function in my .net app?
This is my callback function in the DLL:
LRESULT CALLBACK ShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HSHELL_WINDOWACTIVATED)
{
//SendMessage(CallerWindow, WM_USER+1, wParam, lParam);
char strText[255];
PostMessage(CallerWindow, WM_USER+2, wParam, lParam);
//SendNotifyMessage(HWND_TOPMOST, WM_USER+3, wParam, lParam);
GetWindowText((HWND)wParam, strText, 255);
FILE *myfile;
if ( (myfile = fopen("e:\\dll.out", "a")) != NULL )
{
fprintf(myfile, "\n%d %d %d %s", nCode, wParam, lParam, strText);
fclose(myfile);
}
}
//PostThreadMessage(myThread, (WM_USER + 1), wParam, lParam);
return 0;
}
As you can see, I've been trying all sorts of functions to get my
messages across... the 'Post' functions are the only ones that seem to
work, but my vb.net app only seems to trap the message when I click on
the form itself.
I remember reading somewhere that there's no way for Windows to send
messages to a window if it doesn't have focus. Is this true? If so,
how to get around it?
Thanks in advance for your thoughts!
Regards,
ShellD00d.
> What's the best way to send these notifications? Is there any way I
> can make my DLL call a function in my .net app?
Hooks and interprocess communication are painful themes. SendMessage is bad
idea for current case at all. PostMessage or PostThreadMessage should work.
Just in case it's better use RegisterWindowMessage API to retrieve message
number instead of (WM_USER+XXX). Sometimes PostMessage/PostThreadMessage
fail, so you need monitor these situation and check return value. Definitely
WH_GETMESSAGE hook intercepts posted messages, when recipient retrieves it
by GetMessage/PeekMessage call. The fact it didn't mean that something is
incorrect with your code or problem with application message loop.
I didn't try this with .NET, but some idea to check - create window in .NET
app and update it window procedure to intercept custom message, (use
RegisterWindowMessage to create message id). Pass handle to this window to
all instances of loaded hook DLLs, (for example via shared data section,
memory mapping file, etc.). Send notification from hook code using
PostMessage API.
http://www.codeproject.com/dll/hooks.asp
http://www.codeproject.com/system/hooksys.asp
> I remember reading somewhere that there's no way for Windows to send
> messages to a window if it doesn't have focus. Is this true?
I don't think it's true.
..
Regards,
Vadim.
I have solved the previous problem. The reason my messages weren't
getting through was that the different instances of the DLL that
attached to new processes are not able to share the handle of my
application's window. This is somewhat unnerving, because I have now
resorted to doing a FindWindow to get the HWND of my applications
window. The problem is that the class name has to be hardcoded into
the procedure as "WindowsForms10.Window.8.app1".
I doubt whether it is safe to do so as this class name will probably
change on subsequent releases of the .net framework.
Is there anyway to maintain the contents of a variable across multiple
instances of the DLL? I came across something called Thread Local
Storage, but I am unsure if this is the right direction. I am new to
DLL programming and VC++. Your thoughts please...!
Thanks,
ShellD00d.
> Is there anyway to maintain the contents of a variable across multiple
> instances of the DLL?
See my previous post, e.g. shared data segment and codeproject articles. For
more details see also IPC (interprocess communication) topics in MSDN
library (shared memory, memory mapped files, WM_COPYDATA, sockets, pipes,
etc).
> I came across something called Thread Local
> Storage, but I am unsure if this is the right direction. I am new to
> DLL programming and VC++. Your thoughts please...!
TLS won't allow to share data between processes, it's designed to store only
thread specific data. By default global and static variables shared between
all threads in the given process, TLS variables are not shared between
threads.
Seem like last posts don't related to .NET interop at all, please post it to
appropriate newsgroups.
..
Regards,
Vadim.
I received your message only after I posted mine. All problems have
finally been resolved. I used shared data segment. Thanks for your
help. Sorry if the posts weren't directly related to .net interop, but
that's where the problems began! Thanks again.
Regards.