How to find out all objects on a JoyStick?

1

I am compiling a project for Win32 using the DirectX DirectInput library to manage Joysticks.

I have successfully tried to 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 Joystick objects I have a pretty weird error in the following section:

    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 about it, actually the code itself is a "translation" of an example in C # I found.

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

    
asked by anonymous 28.12.2015 / 16:48

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 type 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 procedure ), with the calling convention StdCall .

However, the function being passed is of anonymous type ( reference of function ) and without an explicit calling convention falling into the default convention: Register .

This incompatibility generates Access Violation by escaping the executed function. Here is a snippet of code 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:

16.01.2016 / 17:25