1 (edited by ynew 2019-06-30 04:05:42)

Topic: Fix for HIDSharp when it blocks exiting .net app

EDIT: After further investigation I am modifying this and presenting it as a solution rather than as a question.

How can I unblock HIDSharp when an application wishes to exit?

I have a native windows application (not written by me) that is hosting and running a .net dll plugin (that is written by us).
The native app is taking a very long time to shut down.
The native app is MS Word and we are running a .net VSTO AddIn in it.
Eventually MS Word forcefully shuts itself down and will sometimes disable our AddIn as a result on the next time it starts.

Running HIdSharp's DeviceList.GetHidDevices method starts code that prevents shutdown, but calling that method in a .net console app does not prevent the app shutdown.

Debugging HIDSharp it turns out there is a message loop that is started on an invisible window when calling DeviceList.GetHidDevices.
This message loop calls a native method called GetMessage, that blocks the thread that called it until a message is received.
Because the thread is blocked by unmanaged code, even when calling the managed thread's abort method during shutdown the code does not exit the GetMessage call because at the point it is blocked the thread is in native code and is not managed.

A workable solution to the problem is, during out shutdown method's execution, to free the blocked thread my supplying a message to the program. That message is then picked up by the GetMessage.

That in and of itself isn't quite enough though for two reasons:

1.) As GetMessage runs in a loop the message must be of a value that causes the loop to exit (i.e. cause GetMessage to return 0 or 1).
2.) As GetMessage is listening on a specific window the message I send must be to the main program window.

So using the native method PostMessageA  in (winuser.h) as follows does the trick:

PostMessageA(hWndOfMessageLoopWindow, WM_QUIT, IntPtr.Zero, IntPtr.Zero);

Using WM_QUIT causes GetMessage to return 0. (This is by design in Windows).
hWndOfMessageLoopWindow is obtained using the native method FindWindow and you pass the class name and window name HidSharpWindowMonitor (which is the class name given to the window in the WinHidManager.cs class)

One plus side to this solution is that it works without any having to make any changes to HIDSharp's code.

However, it relies on knowing HIDSharp's internals (which could, in theory change in the future).

Sending WM_QUIT to this specific message loop window not a problem because it only shuts down that window rather than the application.

It would be much better if HIDSharp offered this functionality itself. But for the time being, this is a good enough workaround.

2

Re: Fix for HIDSharp when it blocks exiting .net app

Try https://www.zer7.com/files/oss/hidsharp … -07-06.zip

It will register a call to AppDomain.CurrentDomain.ProcessExit/DomainUnload, to destroy the listener window. That should return from GetMessage.

This is a good find. I haven't encountered the problem in a standalone environment with .NET - it seems to unload fine. But if it doesn't in a hosted environment, that ought to be fixed. In this environment, is the AppDomain reloaded at any point, or does this only happen at exit?

3

Re: Fix for HIDSharp when it blocks exiting .net app

Hello Zer,

I cannot tell you for sure what the AppDomain lifecycle is in a .net Visual Studio Tools For Office (VSTO) app, but I would pretty surprised if an AppDomain reload were going on because we would presumably loose state and cease to work.

4

Re: Fix for HIDSharp when it blocks exiting .net app

I will give your attempted fix a try and get back to you.

Alternatively, if you want quicker feedback, you can create a VSTO project using the default templates in Visual Studio assuming you have MS Office installed. E.g. if you want to create an MS Word Add-In you can choose the Word 2013 & 2016 project template when creating a new project in Visual Studio.

5

Re: Fix for HIDSharp when it blocks exiting .net app

We have tried your fix in our scenario, and it did not work. i.e. The application does not exit.