jtackabury

I am trying to globally catch all the WM_NCHITTEST messages (not just ones for my application), but I'm having some problems. I know that I will probably need a C++ DLL to handle all the messages and send them to my application, no problem, I've got that setup. But what hook do I use I've tried WH_MOUSE and WH_MOUSE_LL and the callbacks never seem to catch the "WM_NCHITTEST" message. Am I going about this in the wrong way

Thanks,
Jon



Re: Visual C# General Global Hook for WM_NCHITTEST

OmegaMan

See these posts to learn how these people have done it in C#





Re: Visual C# General Global Hook for WM_NCHITTEST

Karthikeya Pavan Kumar .B

Check this GripPanel






Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

These links are both for hanlding the WM_NCHITTEST message for your own application, which I can already do. I want to handle all the WM_NCHITTEST messages for all the windows. I still haven't found a way to do this yet though.

Thanks,
Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

It looks like I have got parts of it working. Using a C++ DLL to hook WH_MOUSE, the DLL sends me the messages and I now receive a "WM_NCLBUTTONDOWN" message. However, when I try to do this:

MOUSEHOOKSTRUCT MyMouseHookStruct = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(MOUSEHOOKSTRUCT));

For a message that belongs to a window other than mine I get this error:
AccessVioliationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Is this by design "m.LParam" isn't 0 (or null), it is above zero, so I'm assuming it should be pointing to a valid memory location, but it fails everytime, unless it's for my own window.

Does anyone have any thoughts

Thanks,
Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

just.a.nerd

Low level hooks (WH_MOUSE_LL and WH_KEYBOARD_LL) are (afaik) the only hooks that work globally in .net.

It's because these hooks switch to your application's context when processing, while other hooks require that your code gets injected into the other processes.

You haven't mentioned what exactly you're trying to achieve, but may be you can simply check the coordinates for mousedown messages and then use something like 'GetWindowFromPoint' WinApi function to get the target window.






Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

Thanks for the heads up. Maybe I should take a step back and explain what I am trying to achieve, I might be approaching this in the wrong way. I want to tell if a click was in the title bar of any window on the screen. I have tried handling NCLBUTTON mesages, but they just don't work, and sometimes the non-client area is more than just the title bar. I also don't want to know about any clicks that occur on any of the control buttons in the titlebar, like the minimize or maximize boxes. I though the WH_MOUSE would work well (not WH_MOUSE_LL) because WH_MOUSE returns a useful struct called "MOUSEHOOKSTRUCT", which includes a "wHitTestCode" property that includes the exact information that I need. However, when I try to access this struct for any window except my own I get this access violation exception. Is this exception by design, or am I just doing something wrong Or is there an easier way to do this

Thanks!
Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

just.a.nerd

The exception occurs because .net has this limitation regarding injecting code into another process. So if you really want to use that approach, use all native code.

I guess, you can call it 'by design' - or by accident, if you prefer :-)

However, in this case, I don't think the native approach will help you for your goal (although you will get rid of the exception).

The reason is, that hooks are processed, BEFORE the mouse messages are delivered to the application (you can in fact cancel them, so they never reach the app, for which they were intended). Hence the wHitTestCode might not tell the right values. I mean, non-rectangular forms process the WM_NCHITTEST to specify which parts are considered 'inside'. I might be wrong about this, I'm just guessing here :-)

An idea (not tested):

Use WH_MOUSE_LL to get the mouse position. WindowFromPoint to get the window.

Send a WM_NCHITTEST explicitly to the window and get the result.

Hope this helps.






Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

THanks for the information, I have been having problems tracking down specific documentation on what I am trying to do. Your idea sounds exactly what I am looking for, but how would I send a message to another window, then read the response Would I just use the API call SendMessage If so, I am completely lost. I found this documentation on SendMessage:
http://msdn2.microsoft.com/en-us/library/ms644950.aspx

And it looks simple enough, I want to send a WM_NCHITTEST message to the hWnd that I get from WindowFromPoint, but what do I use for the L and W params And what structure will the LResult be in

Thanks,
Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

just.a.nerd

Anything regarding platform invoke (WinAPI), visit:

http://www.pinvoke.net/index.aspx

There you will find syntax and constant values/structure definitions for almost everything, you can imagine.

Just search!

Also check MSDN Online.

WM_NCHITTEST use LParam for the screencoordinates. Use the values the hook gives you (they need a little tweak):

(hitPoint is the Point from hook)

LParam = new IntPtr(hitPoint.x + hitPoint.y << 16)

The result will be an IntPtr with the integer value of one of the hittest enumerations.






Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

I have done a bunch of reading (including your post, thanks) and have come up with this (that doesn't work):
IntPtr hWnd = ChildWindowFromPoint(GetDesktopWindow(), hookStruct.pt);
uint lMsg = RegisterWindowMessage("WM_NCHITTEST");
IntPtr lRes = IntPtr.Zero;
IntPtr lP = new IntPtr(hookStruct.pt.x + hookStruct.pt.y << 16);
IntPtr lRes2 = SendMessageTimeout(hWnd, lMsg, IntPtr.Zero, lP, 0x0002, 1000, out lRes);

When I do this lRes is always "0" and lRes2 is always "1".

So, I tried using this:
IntPtr lRes2 = SendMessage(new HandleRef(lMsg, hWnd), lMsg, IntPtr.Zero, lP);

And lRes2 is "0".

So I tried setting up "lP" (which is the lParam) like this:
IntPtr lP = Marshal.AllocHGlobal(Marshal.SizeOf(hookStruct.pt));
Marshal.StructureToPtr(hookStruct.pt, lP, true);

No dice, same problems.

I'm assuming it should be returning something other than a 0 or 1. No matter where I click, this is what I get. If I click on the desktop, or on the taskbar or a window's title bar, anywhere gives the same numbers, which I would expect to different.

Any thoughts
Thanks again,
Jon






Re: Visual C# General Global Hook for WM_NCHITTEST

just.a.nerd

Don't use : RegisterWindowMessage! That is only for your own private messages. WM_NCHITTEST is already registered.

Try this:

public const Int32 WM_NCHITTEST = 0x84;

IntPtr IRes2 = SendMessage(hWnd, WM_NCHITTEST, 0, IP);

On pinvoke.net, search for hittest, you will find an enum definition of all the hittest values, the message should return one of these values (after casting).

Also: use WindowFromPoint, not ChildWindowFromPoint, that will only find childwindows.






Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

Thank you for being patient, I'm still quite new to this Win32 API business. Smile I tried what you suggested, but with no luck. It still always just returns 0, which according to pinvoke.net means "nowhere". I tried setting lP like this:

IntPtr lP = Marshal.AllocHGlobal(Marshal.SizeOf(hookStruct.pt));
Marshal.StructureToPtr(hookStruct.pt, lP, true);

And like this:
IntPtr lP = new IntPtr(hookStruct.pt.x + hookStruct.pt.y << 16);

Both always return 0. I also got rid of the ChildWindowFromPoint stuff, just in case it was giving the wrong hWnd, and used "this.Handle" instead for my own hWnd, and when clicking in my own window I still get a return value of 0. I must really be missing something here. This might be asking too much of you, but would I be able to send you the code so you can give it a try there and let me know if I'm going crazy

Thanks,
Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

jtackabury

Hurray! I got it to work. After messing around forever, and Googling like crazy I came across this fix:
Instead of setting up lParam like this:
IntPtr lP = new IntPtr(hookStruct.pt.x + hookStruct.pt.y << 16);

I found that this works:
IntPtr lP = new IntPtr(oHookStruct.pt.y * 65536 + oHookStruct.pt.x);


Thank you VERY much for all your help, I have learned a ton about Win32 APIs. Smile

Jon





Re: Visual C# General Global Hook for WM_NCHITTEST

just.a.nerd

ops, my mistake

It should have been:

IntPtr IP = new IntPtr(oHookStruct.pt.x + (oHookStruct.pt.y << 16));

I just checked, apparently it was calculated like: (oHookStruct.pt.x + oHookStruct.pt.y) << 16

which is obviously not the same

You can see the operator precedence rules here:

http://msdn2.microsoft.com/en-us/library/aa691323(VS.71).aspx

Multiplying (*) is above addition (+) while shift (<<) is below. That's why your method works, while mine failed.

Glad, you got it working, I was not sure, whether the idea was totally out.