How to discover all the objects in a Joystick?

Asked

Viewed 84 times

1

I am compiling a project for Win32 using the Directx Directinput library to manage Joysticks.

I tried to successfully identify if any Joystick is connected through the enumeration:

uses
    Classes,      Winapi.DirectInput,   FMX.Platform.Win,
    SysUtils,     WinAPI.Windows,       FMX.Dialogs,
    StartUpCopy,                        FMX.Forms,
    Generics.Collections;

var
    DI          : IDirectInput;
    KeyBoard    : IDirectInputDevice;
    JoySticks   : TList<IDirectInputDevice>;
    I           : Integer;

begin
    JoySticks := TList<IDirectInputDevice>.Create();
    DirectInput8Create(HInstance, DIRECTINPUT_VERSION, IID_IDirectInput8W, DI, nil);
    DI.EnumDevices(
        DI8DEVCLASS_GAMECTRL, 
        @function(var lpddi: TDIDeviceInstanceW; pvRef: Pointer) : BOOL
        var
            JS : IDirectInputDevice;
        begin
            if not FAILED(DI.CreateDevice(lpddi.guidInstance, JS, nil)) then
            begin
                JoySticks.Add(JS);
                Result := DIENUM_STOP;
            end
            else
                Result := DIENUM_CONTINUE;
        end, nil, DIEDFL_ATTACHEDONLY);
    if (JoySticks.Count < 1) then
    begin
        ShowMessage('Joystick não encontrado.');
        Halt;
    end;  
    // ...

But to actually identify the objects of the Joysticks I have a very strange error in the following excerpt:

    for I := 0 to JoySticks.Count do
    begin
        JoySticks[I].SetDataFormat(c_dfDIJoystick2);
        JoySticks[I].SetCooperativeLevel(ApplicationHWND, DISCL_FOREGROUND or DISCL_NONEXCLUSIVE);
        JoySticks[I].EnumObjects(
            @function(var lpddoi: TDIDeviceObjectInstanceW; pvRef : Pointer) : BOOL
            var
                PropRange : DIPROPRANGE;
            begin
                PropRange.diph.dwSize       := SizeOf(DIPROPRANGE);
                PropRange.diph.dwHeaderSize := SizeOf(DIPROPHEADER);
                PropRange.diph.dwHow        := DIPH_BYID;
                PropRange.diph.dwObj        := lpddoi.dwType;
                PropRange.lMin              := -1000;
                PropRange.lMax              := 1000;
                Result                      := DIENUM_CONTINUE;
                if FAILED(JoySticks[I].SetProperty(DIPROP_RANGE, PropRange.diph)) then
                    Result := DIENUM_STOP;

                { De acordo com o Debugger, o erro (Access Violation) 
                acontece após o fim das instruções no Callback, o que 
                é estranho uma vez que o código do callback em sí não 
                causa erro algum. }

            end, nil, DIDFT_ALL);
        if(JoySticks[I].Acquire <> DI_OK) then
            ShowMessage('Acq Faild');
    end;
end;

I found little documentation in Delphi on the subject, in fact the code itself is a "translation" of an example in C# that I found.

What’s missing in this process? What am I missing? Has anyone implemented this kind of routine in Delphi around here?

1 answer

0


Well, the error is happening because of the type of procedure used in IDirectInputDeviceW.EnumObjects. The method declared in the header is as follows:

function EnumObjects(lpCallback: TDIEnumDeviceObjectsCallbackW; pvRef: Pointer; dwFlags: DWORD): HResult; stdcall;

The guy TDIEnumDeviceObjectsCallbackW, in turn refers to the following type of procedure:

TDIEnumDeviceObjectsCallbackW = function (var lpddoi: TDIDeviceObjectInstanceW; pvRef : Pointer): BOOL; stdcall;

That is, a regular function (regular trial), with the call convention StdCall.

However the function being passed is of the anonymous type (Ference of Function) and without an explicit call convention falling into the standard convention: Register.

This incompatibility generates the Access Violation when escaping the executed function. Here is a code snippet that would solve the problem:

function EnumObjectCallback(var lpdoi : TDIDeviceObjectInstanceW; pvRef : Pointer) : BOOL; StdCall;
begin
    // lpdoi --> conterá o objeto a ser registrado em cada iteração.  
    Result := DIENUM_CONTINUE;
end;

function EnumDeviceCallback(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; StdCall;
var
    D : IDirectInputDevice8W;
begin
    if FAILED(DInput.CreateDevice(lpddi.guidInstance, D, nil)) then
        Result := DIENUM_CONTINUE
    else
    begin
        Result := DIENUM_CONTINUE;
        case lpddi.dwDevType and $1F of
            DI8DEVTYPE_MOUSE: D.SetDataFormat(c_dfDIMouse);
            DI8DEVTYPE_KEYBOARD: D.SetDataFormat(c_dfDIKeyboard);
            DI8DEVTYPE_JOYSTICK: D.SetDataFormat(c_dfDIJoystick);
        end;

        D.SetCooperativeLevel(HWND, DISCL_BACKGROUND or DISCL_NONEXCLUSIVE);
        D.EnumObjects(@EnumObjectCallback, nil, DIDFT_ALL);
    end;

For more references:

Browser other questions tagged

You are not signed in. Login or sign up in order to post.