TolikPylypchuk / SharpHook

SharpHook provides a cross-platform global keyboard and mouse hook, event simulation, and text entry simulation for .NET
https://sharphook.tolik.io
MIT License
342 stars 32 forks source link

SharpHook is thrown an expeption while Debugging in VS 2022 #12

Closed FelixT42 closed 2 years ago

FelixT42 commented 2 years ago

Hey,

I noticed that the SharpHook Package is not compatible with the Debugger (compiled exe workes fine) in Visual Studio 2022 Update 17.2.4. If the hook is started Async, it worked for a short period of time but then a "System.ExecutionEngineExeption" is thrown in line 95 of the GlobalHookBase.cs file. In the Debug window is also the following Exception shown:

Process terminated. A callback was made on a garbage collected delegate of type 'SharpHook!SharpHook.Native.DispatchProc::Invoke'. Repeat 2 times:

at SharpHook.Native.UioHook.Run()

at SharpHook.GlobalHookBase+<>c__DisplayClass12_0.b__0() at System.Threading.Thread+StartHelper.Callback(System.Object) at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Threading.Thread.StartCallback()

If I try to start a hook in the Main Thread, the Application is not even start up. It hangs on the Step loading the SharpHook.dll file. I could reproduce this issue on two different computers, but not shure if it is a Bug in SharpHook or VS.

Everythings worked absolutly fine in 17.2.0 and I am an happy user of this library.

TolikPylypchuk commented 2 years ago

Hi! Thanks for posting this issue!

I'm going to investigate this in the nearest future, but at first glance, it appears that it may be a bug in Visual Studio itself (it it works fine in 17.2.0, but not in 17.2.4 then it's really fishy).

System.ExecutionEngineExeption is thrown when there is an internal error in the CLR itself, and I think this means that the callback which is passed to the global hook is garbage-collected prematurely, and the native code expects it to still be there.

I'll give you an update after investigating further.

Just one question though - is your project using .NET Framework, or .NET Core/.NET?

FelixT42 commented 2 years ago

Thank you for the fast response! Yes it could really be a bug in 17.2.4, I tried to downgrade but with the Community-Version it´s not that easy so I can not confirm that a downgrade solves the problem. But I can say for sure that the Upgrade on a second PC who worked also fine, throws the same exeption after the upgrade.

I´m currently developing .NET Framework 6 for this project

TolikPylypchuk commented 2 years ago

Just tried to reproduce the issue, and everything works fine - the sample app can be debugged in Visual Studio 17.2.4 without problems (breakpoints also work fine). Both Run and RunAsync work in all hook types (SimpleGlobalHook, TaskPoolGlobalHook, and SimpleReactiveGlobalHook).

Could you please provide a minimal reproducible example project and attach it here as a ZIP-file? There's not much I can do without reproduction unfortunately.

dtagesson commented 2 years ago

Hi! Also a happy user of this library, and also one who has the same problem (in VS 17.2.4). I built a small winforms app which creates a SimpleGlobalHook, does RunAsync and then starts consuming memory basically. This reproduces it for me, hope it helps! Cheers! SharpHookTest.zip

FelixT42 commented 2 years ago

Hi, I tested the Sample Project provided by dtagesson and get the same error on this project. Maybe it´s a problem with the specific version of .net 6.0, after installing the update in VS 2022 I also get the error in VS 2019. But i could get the program to run normal by changing the target Framework back to the unsupported Version 5.0

The .NET SDK with the problem on my side is: 6.0.301 in the x64 version

TolikPylypchuk commented 2 years ago

Yup, the issue can be reproduced, thanks @dtagesson! I will see what I can do to fix it.

TolikPylypchuk commented 2 years ago

Some weird stuff is going on. The exception is thrown if I use the classes from SharpHook, but if I copy these classes to the test project (specifically, GlobalHookBase and SimpleGlobalHook) and use them instead of the classes from SharpHook then the exception is not thrown. It's literally the same code, ~one is just loaded from a separate DLL, and the other is not~ (edit:) one is just build with the Debug configuration, and the other - with the Release configuration.

This makes me think that it really is a bug in the debugger, but I'm going to investigate a little further.

As a workaround, you can copy these classes to your projects - this way you'll be able use the debugger - but that's a huge hack.

TolikPylypchuk commented 2 years ago

If I try to start a hook in the Main Thread, the Application is not even start up. It hangs on the Step loading the SharpHook.dll file.

Regarding this part of the issue - you really shouldn't start the global hook on the main thread if you are building a UI app (e.g. WinForms, WPF, or Avalonia), because then the main thread must be used for the UI stuff and message pumping. Starting the hook on the main thread will block it and the UI will not be able to load.

TolikPylypchuk commented 2 years ago

OK, I finally figured it out. It's not a bug in the debugger - it's a really weird bug in SharpHook which only presents itself when debugging a Release build of the global hooks.

Inside GlobalHookBase.Run and GlobalHookBase.RunAsync there is this line of code:

UioHook.SetDispatchProc(this.HandleHookEventIfNeeded, IntPtr.Zero);

This code is implicitly translated to the following:

UioHook.SetDispatchProc(new DispatchProc(this.HandleHookEventIfNeeded), IntPtr.Zero);

And it appears that the delegate can be garbage-collected (why this only happens when debugging a Release build I have no idea).

So I did this instead:

private readonly DispatchProc dispatchProc;

protected GlobalHookBase() =>
    this.dispatchProc = this.HandleHookEventIfNeeded;

// ...

UioHook.SetDispatchProc(this.dispatchProc, IntPtr.Zero);

And now debugging works, and no ExecutionEngineException is thrown.

The fix will be available in version 3.0.1 which I will release shortly.

TolikPylypchuk commented 2 years ago

Version 3.0.1 is released and this issue is fixed there.