0
\$\begingroup\$

Good morning,

I'd like to write an application that uses game controllers and I decided to use DirectInput. The problem is that I get an "access violation execution" (when I run the program inside the debugger of Visual Studio 2017). I'd like to add that I am on Windows 10 and I had a lot of trouble following the documentation on MSDN (the syntax of the calls do not work everytime for me, I have to use macros that I found the SDL2 source code uses; I also need to cast my callback function otherwise the compiler is angry)

Here is a sample program that enumerate the game controllers that are plugged, counts them and then display a simple window.

#include <stdio.h>
#include <windows.h>

#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")

HINSTANCE g_instance;

// My callback function that is called each time a new joystick is detected.
// I simply count them for now.
// The 'LPVOID pvRef' is a pointer to some user data; I pass the adress of the integer counting the joysticks.
BOOL DIEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) {
  LPDIRECTINPUTDEVICE8 gameController;
  int *p_numJoysticks = (int*)pvRef;

  ++(*p_numJoysticks);
  return DIENUM_CONTINUE;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
  // Register the window class.
  const wchar_t CLASS_NAME[] = L"Sample Window Class";

  WNDCLASS wc = { 0 };

  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = CLASS_NAME;

  RegisterClass(&wc);

  g_instance = hInstance; // Save the HINSTANCE in a global in case I need it elsewhere

  // Create the window.

  HWND hwnd = CreateWindowEx(0,                              // Optional window styles.
                 CLASS_NAME,                     // Window class
                 L"Learn to Program Windows",    // Window text
                 WS_OVERLAPPEDWINDOW,            // Window style

                 // Size and position
                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

                 NULL,       // Parent window    
                 NULL,       // Menu
                 hInstance,  // Instance handle
                 NULL        // Additional application data
                 );

  if (hwnd == NULL) {
    return 0;
  }

  ShowWindow(hwnd, nCmdShow);

  LPDIRECTINPUT8 directInputInterface = NULL; // the main DirectInput device
  HRESULT result = DirectInput8Create(g_instance,
                      DIRECTINPUT_VERSION,
                      IID_IDirectInput8A,
                      (void **)&directInputInterface,
                      NULL);
  switch (result) {

  case DI_OK: { // No error
    // Enumerate the joysticks
    int g_numJoysticks = 0; // We'll pass the address of this integer to EnumDevices which will then pass it to my callback function each time a joystick is detected

    // This function call raises an execption: access violation
    result = directInputInterface->EnumDevices(DI8DEVCLASS_GAMECTRL,
                           (LPDIENUMDEVICESCALLBACKW)DIEnumDevicesCallback, // The callback I defined at the top of this file
                           &g_numJoysticks, // Pointer to some data I'd like the callback to receive
                           DIEDFL_ATTACHEDONLY); // flags

    // Inspect the variable :numJoysticks: in the debugger
    if (DI_OK != result) {
      OutputDebugStringA("Error in IDirectInput8_EnumDevices\n");
    }

    else {
      int dummyStatement = 0; // break here and inspect :numJoysticks:
    }
    break;
  }

  default: { // handle error
    break;
  }

  // Run the message loop.

  MSG msg = { 0 };
  while (GetMessage(&msg, NULL, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

  return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
    {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    case WM_PAINT:
      {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
    EndPaint(hwnd, &ps);
      }
      return 0;
    }
  return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Remark 1: why not Xinput? Because I have a SNES controller and a PS4 controller that do not work with XInput.

Remark 2: why not a higher level API like SDL2? SDL2 can detect my controller but does not detect any button press. (see this post)

\$\endgroup\$
4
  • \$\begingroup\$ Have you tried running the DirectInput samples from Microsoft? Do they work? I have no idea if this is related to your issue, but callbacks should be declared as BOOL CALLBACK CallbackName(...). \$\endgroup\$
    – user35344
    Nov 1, 2018 at 12:14
  • \$\begingroup\$ Adding the CALLBACK resolved the issue indeed. Thanks a lot!!!! \$\endgroup\$
    – Itsbananas
    Nov 1, 2018 at 12:29
  • \$\begingroup\$ AIght, I'll write up an answer. \$\endgroup\$
    – user35344
    Nov 1, 2018 at 12:33
  • \$\begingroup\$ Note that the current version of those DirectInput samples is on GitHub \$\endgroup\$ Nov 1, 2018 at 15:47

1 Answer 1

3
\$\begingroup\$

Callbacks in WinAPI need to be defined with the CALLBACK specifier (which expands to __stdcall), so you need to change the callback signature to

BOOL CALLBACK DIEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .