views:

1641

answers:

3

Hi,
I'm trying to create a hook to monitor the current position of the mouse cursor. Nothing important, I just need to count some pixels during interface design and wanted to learn how to create a hook, so I decided to go for a hard way instead of a sane way.

I've found example code which declares the following function:

 <DllImport("User32.dll", CharSet:=CharSet.Auto, _
 CallingConvention:=CallingConvention.StdCall)> _
 Public Overloads Shared Function SetWindowsHookEx _
      (ByVal idHook As Integer, ByVal HookProc As CallBack, _
       ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
End Function

When the function is called, the following code is used:

        hHook = SetWindowsHookEx(WH_MOUSE, _
                                 hookproc, _
                                 IntPtr.Zero, _
                                 AppDomain.GetCurrentThreadId())

But Appdomain.GetCurrentThreadID generates the warning: "'Public Shared Function GetCurrentThreadId() As Integer' is obsolete: 'AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread."

I've tried using ManagedThreadID, but that doesn't work. The thread ID returned seems to be the logical thread ID of the thread, as it runs in the .net runtime, rather than the Win32 thread identifier.

Calling the function ith AppDomain.GetCurrentThreadID works, but I really would like to have a "stable identifier" for my thread.

Can someone explain to me whether it is possible to use ManagedThreadID in this context (I assume not) and, if not, the things I need to avoid in order to stop the AppDomain.CurrentThreadID from becoming "unstable"?

Cheers

+3  A: 

It is not possible to use ManagedThreadId in this context. This is a completely managed concept and has no real representation in the native world. Hence it doesn't make any sense to the API's you're passing it to.

The reason ManagedThreadId exists is because there is not necessarily a 1-1 mapping between a native and managed thread. The CLR is free to use multiple native threads to run a single managed thread as long as the native thread is compatible with the one it's replacing. It cannot for instance, be in a different COM apartment.

In some ways you're a bit stuck here. AFAIK, there is no way to 100% guarantee that you will have the same native thread for a given managed thread. You can achieve a very high level of guarantee though if you are for instance running a WinForms or WPF application and the call to native code occurs on the UI thread. The reason being that both of these UI frameworks live in STA apartments which makes it very hard (if even possible) for the CLR to switch out from under you.

Short Version: If you're in a WinForms or WPF application and running this on the UI thread, you can assume a reasonable level of stability for this identifier.

JaredPar
+1  A: 

For future readers: There are also System.Threading.Thread.BeginThreadAffinity() / EndThreadAffinity() functions that purportedly stop the VM from switching between different physical threads. I don't believe these guarantee stability, but I think they're more likely to be stable.

BinarySplit
A: 

Curious as to whether you were able to find a workaround for this issue?

amadib
Not really, no. I basically just took JaredPar's word for it that the threadID would be reasonably stable, used this during testing, and then changed the interface to react to OnMouseMove events instead for the final deployment. I didn't do that originally because it means passing events up through a whole chain of Controls and is generally just a pain in the ass. It is, however, more reliable than the above method, which only failed once or twice during the whole of testing, but did fail.
Frosty840