Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

joystick Exception: E_HANDLE: Invalid handle (Console-only) #193

Open
cjroehrig opened this issue Aug 29, 2020 · 18 comments
Open

joystick Exception: E_HANDLE: Invalid handle (Console-only) #193

cjroehrig opened this issue Aug 29, 2020 · 18 comments

Comments

@cjroehrig
Copy link

cjroehrig commented Aug 29, 2020

This error only happens in the Console version (v1.11.731.0 compiled with VisualStudio 2019) running on Windows 10v1903.

When run under the GUI, it works fine. I've had no other issues with the Console version (using mouse, keyboard, vJoy, etc). It happens even when run as administrator.

My attached joysticks are a vJoy (2.1.9) and an EDTracker 2.
It also happens when I attach and try to access a Logitech Wingman by name or index.

I diff'd the FreePIE source vs an older version (1.9.633.0) that was working (under Win7) and the only changes that seem promising are some changes to how the GlobalIndexer works and how the JoystickPlugin uses it (but I can't see anything specific to the Console version). When I get a chance I'll revert those and see if it fixes it.

Here's a simple example to demonstrate the issue (ensure at least one joystick is attached):

if starting:
    import sys, traceback
    try:
        joy = joystick[0]
    except:
        traceback.print_exc(file=sys.stdout)
sys.exit(0)

Using the Console version, I get the E_HANDLE exception and a traceback. Using the GUI it exits with no error.

@MarijnS95
Copy link
Contributor

@cjroehrig How about including the exception and traceback in this bug report?

FWIW I haven't looked into FreePIE for a long time but this could easily be the case where the HWND for a console window isn't properly retrieved. A backtrace should help point that out though.

@cjroehrig
Copy link
Author

cjroehrig commented Aug 29, 2020

@MarijnS95 Here's the output with traceback. Not much more info than what I described. Line 4 is the joy=joystick[0]

Starting script parser
Traceback (most recent call last):
  File "<string>", line 4, in <module>
Exception: E_HANDLE: Invalid handle (-2147024890)
SystemExit
Stopping script parser

@MarijnS95
Copy link
Contributor

That's unfortunate (but expected). Would you be able to attach the Visual Studio debugger and catch a C# exception (with stacktrace) before it bubbles to the Python engine?

@AndersMalmgren
Copy link
Owner

I wonder if vjoy or dxinput plugins are trying to get the hwnd from the host application and fails for the console

@MarijnS95
Copy link
Contributor

MarijnS95 commented Aug 29, 2020

@AndersMalmgren That's likely it, or we're resolving and passing it incorrectly somewhere. I had a personal console application utilizing vjoy and dxinput long ago and that worked just fine, and iirc had to pass HWND to dxinput creation.

@AndersMalmgren
Copy link
Owner

AndersMalmgren commented Aug 29, 2020

Point of interest to investigate when debugging

var handle = Process.GetCurrentProcess().MainWindowHandle;

var handle = Process.GetCurrentProcess().MainWindowHandle;

@MarijnS95
Copy link
Contributor

MarijnS95 commented Aug 29, 2020

Checked it, I was using the exact same MainWindowHandle... 🤔

EDIT: Is it possible that we're re-opening the console in some way, voiding this Window handle? Or otherwise spawning it after initialzing the plugin? Seems far-fetched, better wait for a C# trace.

@AndersMalmgren
Copy link
Owner

I just tried (without vjoy mind you) and I get a handle and I can read values from my gampad

@cjroehrig
Copy link
Author

I'll tackle the C# trace in a couple days when I get a chance to sit down and figure out VisualStudio (I'm a unix guy).

@cjroehrig
Copy link
Author

I got it to run within VisualStudio by opening up the FreePIE.Console.csproj file, editing the FreePIE.Console Debug properties to give the path to my test.py in the command-line arguments, and selecting Debug > Start without Debugging, but I get:

Starting script parser
Traceback (most recent call last):
  File "<string>", line 4, in <module>
NameError: name 'joystick' is not defined
SystemExit
Stopping script parser
Press any key to continue . . .

I've never used the GUI version of Visual Studio or debugged with it. I originally compiled FreePIE using the command-line BuildTools but it seems like VS-GUI isn't building the same thing and is missing the FreePIE globals (i.e. 'joystick', etc).

Can you give me some tips about how to build the FreePIE.Console using the VisualStudio GUI?
Or if there is a way to set breakpoints and get a traceback without using the VisualStudio GUI, that would be even better.

@AndersMalmgren
Copy link
Owner

AndersMalmgren commented Sep 1, 2020

The console project is not mainted as properly as the GUI.
From GUI project properties copy the settings for build events (Post build events) to the consle project

In the properties for the plugin project change the build output path from the GUI to the console project

That shuld do the trick

@cjroehrig
Copy link
Author

cjroehrig commented Sep 5, 2020

Here is the traceback. After finally getting it to build correctly, it turns out the error doesn't happen when debugging under VS, so I inserted WriteLine(e.StackTrace) in all the catch blocks I could find.

E_HANDLE: Invalid handle (-2147024890)
   at SlimDX.Result.Throw[T](Object dataKey, Object dataValue)
   at SlimDX.Result.Record[T](Int32 hr, Boolean failed, Object dataKey, Object dataValue)
   at SlimDX.DirectInput.Device.SetCooperativeLevel(IntPtr handle, CooperativeLevel flags)
   at FreePIE.Core.Plugins.JoystickPlugin.<>c__DisplayClass1_0.<CreateGlobal>b__0(DeviceInstance d) in C:\Users\cc\Documents\CJRControllers\build\FreePIE\Free
PIE.FOO\FreePIE.Core.Plugins\JoystickPlugin.cs:line 27
   at FreePIE.Core.Plugins.JoystickPlugin.<>c__DisplayClass1_0.<CreateGlobal>b__1(Int32 intIndex) in C:\Users\cc\Documents\CJRControllers\build\FreePIE\FreePI
E.FOO\FreePIE.Core.Plugins\JoystickPlugin.cs:line 35
   at FreePIE.Core.Plugins.Globals.GlobalIndexer`2.get_Item(TIndex index) in C:\Users\cc\Documents\CJRControllers\build\FreePIE\FreePIE.FOO\FreePIE.Core.Plugi
ns\Globals\GlobalIndexer.cs:line 30
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
   at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
   at IronPython.Compiler.PythonScriptCode.Run(Scope scope)
   at IronPython.Compiler.RuntimeScriptCode.InvokeTarget(Scope scope)
   at IronPython.Compiler.RuntimeScriptCode.Run(Scope scope)
   at Microsoft.Scripting.Hosting.CompiledCode.Execute(ScriptScope scope)
   ...

@cjroehrig
Copy link
Author

cjroehrig commented Sep 5, 2020

Point of interest to investigate when debugging

var handle = Process.GetCurrentProcess().MainWindowHandle;

var handle = Process.GetCurrentProcess().MainWindowHandle;

I added a WriteLine after this and indeed the handle is zero. MSDN says the handle is non-zero only if the process has a GUI.

https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.mainwindowhandle?redirectedfrom=MSDN&view=netcore-3.1#System_Diagnostics_Process_MainWindowHandle

@cjroehrig
Copy link
Author

cjroehrig commented Sep 5, 2020

I tried wrapping the SetCooperativeLevel call in JoystickPlugin.cs with a null check:

if (handle != IntPtr.Zero)
    controller.SetCooperativeLevel(handle, CooperativeLevel.Exclusive | CooperativeLevel.Background);

and I no longer get the E_HANDLE error, but don't get any joystick events either. I'll look into how to access DirectInput devices from console windows...

@cjroehrig
Copy link
Author

cjroehrig commented Sep 5, 2020

I got things working by calling joystick.Poll() in JoystickState if the window handle is null. Here's my patch:
console-joy.patch.txt

@AndersMalmgren
Copy link
Owner

Ah ok, when debugging you get the vs handle :) I will see what I can do

@AndersMalmgren
Copy link
Owner

AndersMalmgren commented Sep 5, 2020

I looked at your patch, but I cant get it to work.

I removed controller.SetCooperativeLevel(handle, CooperativeLevel.Exclusive | CooperativeLevel.Background);

And refactored the state code so now it does

        public JoystickState State
        {
            get { return state; }
        }

        public void Reset()
        {
            joystick.Poll();
            state = joystick.GetCurrentState();
        }

Stil get the handle error

@cjroehrig
Copy link
Author

cjroehrig commented Sep 5, 2020

I only do the Poll() if window handle was null when originally enumerating the devices in CreateGlobal (which indicates it is not a GUI app). I remember that and save it in a new private variable winhandle in the Device class. I tried to make it so the code path doesn't change at all for the GUI version, so Poll() doesn't get called if it is the GUI version. I think you still need the SetCooperativeLevel for the GUI version which (I'm guessing) does DirectInput polling in its event loop.

The idea with the Poll is that there is no event loop in a Console app, so you need to poll it every time you want to get the state (i.e. in State()). (I looked at the SlimDX code and GetCurrentState only returns the a copy of the current state, it doesn't update it.)

I'm guessing that this issue will affect every other DirectX plugin that doesn't do polling, but at least it is a template for a solution.

Here is the entire patched file:
JoystickPlugin.cs.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants