/**************************************************************************** * * File: inptinfo.cpp * Project: DxDiag (DirectX Diagnostic Tool) * Author: Mike Anderson (manders@microsoft.com) * Purpose: Gather information about input devices on this machine * * (C) Copyright 1998 Microsoft Corp. All rights reserved. * ****************************************************************************/ #define DIRECTINPUT_VERSION 0x0800 #include #include #include #include #include #include #include #include #include #include "mmddk.h" #include "reginfo.h" #include "sysinfo.h" // for BIsPlatformNT #include "inptinfo.h" #include "fileinfo.h" #include "resource.h" static HRESULT Get9xInputDeviceInfo(InputInfo* pInputInfo); static HRESULT GetNTInputDeviceInfo(InputInfo* pInputInfo); static VOID GetJoystickTypeDesc(DWORD dwType, TCHAR* pszDesc); static HRESULT CheckRegistry(InputInfo* pInputInfo, RegError** ppRegErrorFirst); /**************************************************************************** * * GetInputInfo * ****************************************************************************/ HRESULT GetInputInfo(InputInfo** ppInputInfo) { HRESULT hr; *ppInputInfo = new InputInfo; if (*ppInputInfo == NULL) return E_OUTOFMEMORY; ZeroMemory(*ppInputInfo, sizeof(InputInfo)); (*ppInputInfo)->m_bNT = BIsPlatformNT(); if ((*ppInputInfo)->m_bNT) { if (FAILED(hr = GetNTInputDeviceInfo(*ppInputInfo))) return hr; } else { if (FAILED(hr = Get9xInputDeviceInfo(*ppInputInfo))) return hr; } if (FAILED(hr = CheckRegistry(*ppInputInfo, &(*ppInputInfo)->m_pRegErrorFirst))) return hr; return S_OK; } // Have to do the LoadLibrary/GetProcAddress thing for dinput.dll and setupapi.dll: typedef HRESULT (WINAPI* PfnDirectInputCreateA)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter); typedef HRESULT (WINAPI* PfnDirectInputCreateW)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter); typedef WINSETUPAPI HDEVINFO (WINAPI* PfnSetupDiGetClassDevsA)(IN CONST GUID *ClassGuid, IN PCSTR Enumerator, IN HWND hwndParent, IN DWORD Flags); typedef WINSETUPAPI HDEVINFO (WINAPI* PfnSetupDiGetClassDevsW)(IN CONST GUID *ClassGuid, IN PCWSTR Enumerator, IN HWND hwndParent, IN DWORD Flags); typedef WINSETUPAPI BOOL (WINAPI* PfnSetupDiEnumDeviceInterfaces)(IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN CONST GUID *InterfaceClassGuid, IN DWORD MemberIndex, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData); typedef WINSETUPAPI BOOL (WINAPI* PfnSetupDiGetDeviceInterfaceDetailA)(IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, IN DWORD DeviceInterfaceDetailDataSize, OUT PDWORD RequiredSize, OUT PSP_DEVINFO_DATA DeviceInfoData); typedef WINSETUPAPI BOOL (WINAPI* PfnSetupDiGetDeviceInterfaceDetailW)(IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, IN DWORD DeviceInterfaceDetailDataSize, OUT PDWORD RequiredSize, OUT PSP_DEVINFO_DATA DeviceInfoData); typedef WINSETUPAPI BOOL (WINAPI* PfnSetupDiDestroyDeviceInfoList)(IN HDEVINFO DeviceInfoSet); typedef CMAPI CONFIGRET (WINAPI* PfnCM_Get_Parent)(OUT PDEVINST pdnDevInst, IN DEVINST dnDevInst, IN ULONG ulFlags); typedef CMAPI CONFIGRET (WINAPI* PfnCM_Get_DevNode_Status)(OUT PULONG pulStatus, OUT PULONG pulProblemNumber, IN DEVINST dnDevInst, IN ULONG ulFlags); typedef CMAPI CONFIGRET (WINAPI* PfnCM_Get_DevNode_Registry_PropertyW)(IN DEVINST dnDevInst, IN ULONG ulProperty, OUT PULONG pulRegDataType, OPTIONAL OUT PVOID Buffer, OPTIONAL IN OUT PULONG pulLength, IN ULONG ulFlags); typedef CMAPI CONFIGRET (WINAPI* PfnCM_Get_DevNode_Registry_PropertyA)(IN DEVINST dnDevInst, IN ULONG ulProperty, OUT PULONG pulRegDataType, OPTIONAL OUT PVOID Buffer, OPTIONAL IN OUT PULONG pulLength, IN ULONG ulFlags); /**************************************************************************** * * GetNTInputDeviceInfo * ****************************************************************************/ HRESULT GetNTInputDeviceInfo(InputInfo* pInputInfo) { HINSTANCE hInstDInput = NULL; HINSTANCE hInstSetupApi = NULL; LPDIRECTINPUT pDI = NULL; GUID guidHid; HDEVINFO hdev = NULL; SP_DEVICE_INTERFACE_DETAIL_DATA* pdidd; InputDeviceInfoNT* pInputDeviceInfoNTNew; PfnCM_Get_Parent FnCM_Get_Parent = NULL; PfnCM_Get_DevNode_Status FnCM_Get_DevNode_Status = NULL; PfnSetupDiEnumDeviceInterfaces FnSetupDiEnumDeviceInterfaces = NULL; PfnSetupDiDestroyDeviceInfoList FnSetupDiDestroyDeviceInfoList = NULL; #ifdef UNICODE PfnDirectInputCreateW FnDirectInputCreate = NULL; PfnCM_Get_DevNode_Registry_PropertyW FnCM_Get_DevNode_Registry_Property = NULL; PfnSetupDiGetClassDevsW FnSetupDiGetClassDevs = NULL; PfnSetupDiGetDeviceInterfaceDetailW FnSetupDiGetDeviceInterfaceDetail = NULL; #else PfnDirectInputCreateA FnDirectInputCreate = NULL; PfnCM_Get_DevNode_Registry_PropertyA FnCM_Get_DevNode_Registry_Property = NULL; PfnSetupDiGetClassDevsA FnSetupDiGetClassDevs = NULL; PfnSetupDiGetDeviceInterfaceDetailA FnSetupDiGetDeviceInterfaceDetail = NULL; #endif // Apparently one must initialize DInput before enumerating HID devices hInstDInput = LoadLibrary(TEXT("dinput.dll")); if (hInstDInput == NULL) goto LEnd; #ifdef UNICODE FnDirectInputCreate = (PfnDirectInputCreateW)GetProcAddress(hInstDInput, "DirectInputCreateW"); if (FnDirectInputCreate == NULL) goto LEnd; #else FnDirectInputCreate = (PfnDirectInputCreateA)GetProcAddress(hInstDInput, "DirectInputCreateA"); if (FnDirectInputCreate == NULL) goto LEnd; #endif if (SUCCEEDED(FnDirectInputCreate(NULL, 0x0300, &pDI, NULL))) pDI->Release(); // immediately drop DI interface; we don't actually use it hInstSetupApi = LoadLibrary(TEXT("setupapi.dll")); if (hInstSetupApi == NULL) goto LEnd; FnCM_Get_Parent = (PfnCM_Get_Parent)GetProcAddress(hInstSetupApi, "CM_Get_Parent"); if (FnCM_Get_Parent == NULL) goto LEnd; FnCM_Get_DevNode_Status = (PfnCM_Get_DevNode_Status)GetProcAddress(hInstSetupApi, "CM_Get_DevNode_Status"); if (FnCM_Get_DevNode_Status == NULL) goto LEnd; FnSetupDiEnumDeviceInterfaces = (PfnSetupDiEnumDeviceInterfaces)GetProcAddress(hInstSetupApi, "SetupDiEnumDeviceInterfaces"); if (FnSetupDiEnumDeviceInterfaces == NULL) goto LEnd; FnSetupDiDestroyDeviceInfoList = (PfnSetupDiDestroyDeviceInfoList)GetProcAddress(hInstSetupApi, "SetupDiDestroyDeviceInfoList"); if (FnSetupDiDestroyDeviceInfoList == NULL) goto LEnd; #ifdef UNICODE FnCM_Get_DevNode_Registry_Property = (PfnCM_Get_DevNode_Registry_PropertyW)GetProcAddress(hInstSetupApi, "CM_Get_DevNode_Registry_PropertyW"); if (FnCM_Get_DevNode_Registry_Property == NULL) goto LEnd; FnSetupDiGetClassDevs = (PfnSetupDiGetClassDevsW)GetProcAddress(hInstSetupApi, "SetupDiGetClassDevsW"); if (FnSetupDiGetClassDevs == NULL) goto LEnd; FnSetupDiGetDeviceInterfaceDetail = (PfnSetupDiGetDeviceInterfaceDetailW)GetProcAddress(hInstSetupApi, "SetupDiGetDeviceInterfaceDetailW"); if (FnSetupDiGetDeviceInterfaceDetail == NULL) goto LEnd; #else FnCM_Get_DevNode_Registry_Property = (PfnCM_Get_DevNode_Registry_PropertyA)GetProcAddress(hInstSetupApi, "CM_Get_DevNode_Registry_PropertyA"); if (FnCM_Get_DevNode_Registry_Property == NULL) goto LEnd; FnSetupDiGetClassDevs = (PfnSetupDiGetClassDevsA)GetProcAddress(hInstSetupApi, "SetupDiGetClassDevsA"); if (FnSetupDiGetClassDevs == NULL) goto LEnd; FnSetupDiGetDeviceInterfaceDetail = (PfnSetupDiGetDeviceInterfaceDetailA)GetProcAddress(hInstSetupApi, "SetupDiGetDeviceInterfaceDetailA"); if (FnSetupDiGetDeviceInterfaceDetail == NULL) goto LEnd; #endif guidHid = GUID_CLASS_INPUT; hdev = FnSetupDiGetClassDevs(&guidHid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hdev == INVALID_HANDLE_VALUE || hdev == NULL) return E_FAIL; int idev; // There is no way to query the number of devices. // You just have to keep incrementing until you run out. // To avoid infinite looping on internal errors, break on any // error once we have tried more than chdiMax devices, since that's the most // HID will ever give us. 64 is a resonable value for chidMax. It is the // max allowed USB/HID devices. for (idev = 0; idev < 64/*chdiMax*/; idev++) { SP_DEVICE_INTERFACE_DATA did; did.cbSize = sizeof(did); if (!FnSetupDiEnumDeviceInterfaces(hdev, 0, &guidHid, idev, &did)) { if(GetLastError() == ERROR_NO_MORE_ITEMS) break; else continue; } /* * Ask for the required size then allocate it then fill it. * * Note that we don't need to free the memory on the failure * path; our caller will do the necessary memory freeing. * * Sigh. Windows NT and Windows 98 implement * SetupDiGetDeviceInterfaceDetail differently if you are * querying for the buffer size. * * Windows 98 returns FALSE, and GetLastError() returns * ERROR_INSUFFICIENT_BUFFER. * * Windows NT returns TRUE. * * So we allow the cases either where the call succeeds or * the call fails with ERROR_INSUFFICIENT_BUFFER. */ SP_DEVINFO_DATA dinf; DWORD cbRequired; if (FnSetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA*)(new BYTE[cbRequired]); if (pdidd == NULL) continue; ZeroMemory(pdidd, cbRequired); pdidd->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); dinf.cbSize = sizeof(dinf); if (!FnSetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, &dinf)) { delete pdidd; continue; } delete pdidd; DEVINST dinst; if (CR_SUCCESS != FnCM_Get_Parent(&dinst, dinf.DevInst, 0)) continue; pInputDeviceInfoNTNew = new InputDeviceInfoNT; if (pInputDeviceInfoNTNew == NULL) return E_OUTOFMEMORY; ZeroMemory(pInputDeviceInfoNTNew, sizeof(InputDeviceInfoNT)); if (pInputInfo->m_pInputDeviceInfoNTFirst == NULL) { pInputInfo->m_pInputDeviceInfoNTFirst = pInputDeviceInfoNTNew; } else { InputDeviceInfoNT* pInputDeviceInfoNT; for (pInputDeviceInfoNT = pInputInfo->m_pInputDeviceInfoNTFirst; pInputDeviceInfoNT->m_pInputDeviceInfoNTNext != NULL; pInputDeviceInfoNT = pInputDeviceInfoNT->m_pInputDeviceInfoNTNext) { } pInputDeviceInfoNT->m_pInputDeviceInfoNTNext = pInputDeviceInfoNTNew; } CONFIGRET cr; TCHAR sz[200]; ULONG ulLength; ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinst, CM_DRP_DEVICEDESC, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szName, &ulLength, NULL); // Friendly name is preferably to device desc, but is often (always?) missing ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinst, CM_DRP_FRIENDLYNAME, NULL, (BYTE*)sz, &ulLength, NULL); if (cr == CR_SUCCESS) lstrcpy(pInputDeviceInfoNTNew->m_szName, sz); ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinst, CM_DRP_MFG, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szProvider, &ulLength, NULL); ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinst, CM_DRP_HARDWAREID, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szId, &ulLength, NULL); cr = FnCM_Get_DevNode_Status(&pInputDeviceInfoNTNew->m_dwStatus, &pInputDeviceInfoNTNew->m_dwProblem, dinst, 0); DEVINST dinstPort; if (CR_SUCCESS != FnCM_Get_Parent(&dinstPort, dinst, 0)) continue; ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinstPort, CM_DRP_DEVICEDESC, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szPortName, &ulLength, NULL); // Friendly name is preferably to device desc, but is often (always?) missing ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinstPort, CM_DRP_FRIENDLYNAME, NULL, (BYTE*)sz, &ulLength, NULL); if (cr == CR_SUCCESS) lstrcpy(pInputDeviceInfoNTNew->m_szPortName, sz); ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinstPort, CM_DRP_MFG, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szPortProvider, &ulLength, NULL); ulLength = 200; cr = FnCM_Get_DevNode_Registry_Property(dinstPort, CM_DRP_HARDWAREID, NULL, (BYTE*)pInputDeviceInfoNTNew->m_szPortId, &ulLength, NULL); cr = FnCM_Get_DevNode_Status(&pInputDeviceInfoNTNew->m_dwPortStatus, &pInputDeviceInfoNTNew->m_dwPortProblem, dinstPort, 0); } } LEnd: if (hdev != NULL) FnSetupDiDestroyDeviceInfoList(hdev); if (hInstSetupApi != NULL) FreeLibrary(hInstSetupApi); if (hInstDInput != NULL) FreeLibrary(hInstDInput); return S_OK; } /**************************************************************************** * * Get9xInputDeviceInfo * ****************************************************************************/ HRESULT Get9xInputDeviceInfo(InputInfo* pInputInfo) { DWORD dwDevNum; JOYCAPS jc; HKEY hkBase; HKEY hkDrv; HKEY hkData; JOYREGHWCONFIG jhwc; DWORD dwBufferLen; INT i; TCHAR szKey[256]; TCHAR szOEMKey[256]; HKEY hkOEMBase; HKEY hkOEMData; TCHAR szOEMName[256]; TCHAR szOEMCallout[256]; InputDeviceInfo* pInputDeviceInfoNew; InputDeviceInfo* pInputDeviceInfo; TCHAR szPath[MAX_PATH]; TCHAR sz[200]; dwDevNum = (DWORD)-1; if (JOYERR_NOERROR == joyGetDevCaps(dwDevNum, &jc, sizeof jc)) { if ((ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYCONFIG, 0, KEY_READ, &hkBase)) && (ERROR_SUCCESS == RegOpenKeyEx(hkBase, jc.szRegKey, 0, KEY_READ, &hkDrv)) && (ERROR_SUCCESS == RegOpenKeyEx(hkDrv, REGSTR_KEY_JOYCURR, 0, KEY_READ, &hkData))) { for (i = 0; i < 20; i++) { wsprintf(szKey, REGSTR_VAL_JOYNCONFIG, i + 1); dwBufferLen = sizeof JOYREGHWCONFIG; if (ERROR_SUCCESS == RegQueryValueEx(hkData, szKey, 0, NULL, (LPBYTE)&jhwc, &dwBufferLen)) { // Skip devices whose type is JOY_HW_NONE. if (jhwc.dwType == JOY_HW_NONE) continue; pInputDeviceInfoNew = new InputDeviceInfo; if (pInputDeviceInfoNew == NULL) return E_OUTOFMEMORY; ZeroMemory(pInputDeviceInfoNew, sizeof(InputDeviceInfo)); if (pInputInfo->m_pInputDeviceInfoFirst == NULL) { pInputInfo->m_pInputDeviceInfoFirst = pInputDeviceInfoNew; } else { for (pInputDeviceInfo = pInputInfo->m_pInputDeviceInfoFirst; pInputDeviceInfo->m_pInputDeviceInfoNext != NULL; pInputDeviceInfo = pInputDeviceInfo->m_pInputDeviceInfoNext) { } pInputDeviceInfo->m_pInputDeviceInfoNext = pInputDeviceInfoNew; } pInputDeviceInfoNew->m_dwUsageSettings = jhwc.dwUsageSettings; wsprintf(pInputDeviceInfoNew->m_szSettings, TEXT("0x%08x"), jhwc.dwUsageSettings); if (JOY_US_PRESENT & jhwc.dwUsageSettings) { LoadString(NULL, IDS_JOYSTICKPRESENT, sz, 200); lstrcat(pInputDeviceInfoNew->m_szSettings, sz); } // Try reading an OEM name wsprintf(szKey, REGSTR_VAL_JOYNOEMNAME, i + 1); dwBufferLen = sizeof szOEMKey; szOEMKey[0] = 0; szOEMName[0] = 0; if (ERROR_SUCCESS == RegQueryValueEx(hkData, szKey, 0, NULL, (LPBYTE)szOEMKey, &dwBufferLen)) { hkOEMBase = 0; hkOEMData = 0; // If there is an OEM name, look in the PrivateProperties to find out // the name of the device as shown in the control panel applet. if((szOEMKey[0] != 0) && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYOEM, 0, KEY_READ, &hkOEMBase)) && (ERROR_SUCCESS == RegOpenKeyEx(hkOEMBase, szOEMKey, 0, KEY_READ, &hkOEMData)) && (ERROR_SUCCESS == RegQueryValueEx(hkOEMData, REGSTR_VAL_JOYOEMNAME, 0, NULL, NULL, &dwBufferLen)) && dwBufferLen) { dwBufferLen = sizeof szOEMName; RegQueryValueEx(hkOEMData, REGSTR_VAL_JOYOEMNAME, 0, NULL, (LPBYTE)szOEMName, &dwBufferLen); } if (hkOEMData) RegCloseKey(hkOEMData); } if (hkOEMBase) RegCloseKey(hkOEMBase); if (szOEMName[0] != 0) lstrcpy(pInputDeviceInfoNew->m_szDeviceName, szOEMName); else GetJoystickTypeDesc(jhwc.dwType, pInputDeviceInfoNew->m_szDeviceName); wsprintf(szKey, REGSTR_VAL_JOYNOEMCALLOUT, i + 1); dwBufferLen = sizeof szOEMCallout; if (ERROR_SUCCESS == RegQueryValueEx(hkData, szKey, 0, NULL, (LPBYTE)&szOEMCallout, &dwBufferLen)) { lstrcpy(pInputDeviceInfoNew->m_szDriverName, szOEMCallout); GetSystemDirectory(szPath, MAX_PATH); lstrcat(szPath, TEXT("\\")); lstrcat(szPath, szOEMCallout); GetFileVersion(szPath, pInputDeviceInfoNew->m_szDriverVersion, pInputDeviceInfoNew->m_szDriverAttributes, pInputDeviceInfoNew->m_szDriverLanguageLocal, pInputDeviceInfoNew->m_szDriverLanguage, &pInputDeviceInfoNew->m_bBeta, &pInputDeviceInfoNew->m_bDebug); GetFileDateAndSize(szPath, pInputDeviceInfoNew->m_szDriverDateLocal, pInputDeviceInfoNew->m_szDriverDate, &pInputDeviceInfoNew->m_numBytes); FileIsSigned(szPath, &pInputDeviceInfoNew->m_bDriverSigned, &pInputDeviceInfoNew->m_bDriverSignedValid); } else { LoadString(NULL, IDS_DEFAULT, pInputDeviceInfoNew->m_szDriverName, 100); } } } } } return S_OK; } /**************************************************************************** * * GetJoystickTypeDesc * ****************************************************************************/ VOID GetJoystickTypeDesc(DWORD dwType, TCHAR* pszDesc) { LONG ids; switch(dwType) { case JOY_HW_NONE: ids = IDS_JOY_HW_NONE; break; case JOY_HW_CUSTOM: ids = IDS_JOY_HW_CUSTOM; break; case JOY_HW_2A_2B_GENERIC: ids = IDS_JOY_HW_2A_2B_GENERIC; break; case JOY_HW_2A_4B_GENERIC: ids = IDS_JOY_HW_2A_4B_GENERIC; break; case JOY_HW_2B_GAMEPAD: ids = IDS_JOY_HW_2B_GAMEPAD; break; case JOY_HW_2B_FLIGHTYOKE: ids = IDS_JOY_HW_2B_FLIGHTYOKE; break; case JOY_HW_2B_FLIGHTYOKETHROTTLE: ids = IDS_JOY_HW_2B_FLIGHTYOKETHROTTLE; break; case JOY_HW_3A_2B_GENERIC: ids = IDS_JOY_HW_3A_2B_GENERIC; break; case JOY_HW_3A_4B_GENERIC: ids = IDS_JOY_HW_3A_4B_GENERIC; break; case JOY_HW_4B_GAMEPAD: ids = IDS_JOY_HW_4B_GAMEPAD; break; case JOY_HW_4B_FLIGHTYOKE: ids = IDS_JOY_HW_4B_FLIGHTYOKE; break; case JOY_HW_4B_FLIGHTYOKETHROTTLE: ids = IDS_JOY_HW_4B_FLIGHTYOKETHROTTLE; break; default: ids = IDS_JOY_UNKNOWN; break; } LoadString(NULL, ids, pszDesc, 60); } /**************************************************************************** * * GetInputDriverInfo * ****************************************************************************/ HRESULT GetInputDriverInfo(InputInfo* pInputInfo) { HKEY hkBase; HKEY hkDrv; HKEY hkMedia; HKEY hkMediaDriver; DWORD dwIndex = 0; TCHAR szName[100]; DWORD dwNameSize; TCHAR szClass[100]; DWORD dwClassSize; InputDriverInfo* pInputDriverInfoNew; InputDriverInfo* pInputDriverInfo; DWORD dwBufferLen; TCHAR szActive[10]; TCHAR szSubMediaKey[10]; if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYCONFIG, 0, KEY_READ, &hkBase)) return S_OK; // This key doesn't exist on NT, so exit silently for now. dwNameSize = 100; dwClassSize = 100; while (ERROR_SUCCESS == RegEnumKeyEx(hkBase, dwIndex, szName, &dwNameSize, NULL, szClass, &dwClassSize, NULL)) { if (szName[dwNameSize - 1] == '>' && szName[dwNameSize - 6] == '<') { // It's a driver pInputDriverInfoNew = new InputDriverInfo; if (pInputDriverInfoNew == NULL) return E_OUTOFMEMORY; ZeroMemory(pInputDriverInfoNew, sizeof(InputDriverInfo)); if (pInputInfo->m_pInputDriverInfoFirst == NULL) { pInputInfo->m_pInputDriverInfoFirst = pInputDriverInfoNew; } else { for (pInputDriverInfo = pInputInfo->m_pInputDriverInfoFirst; pInputDriverInfo->m_pInputDriverInfoNext != NULL; pInputDriverInfo = pInputDriverInfo->m_pInputDriverInfoNext) { } pInputDriverInfo->m_pInputDriverInfoNext = pInputDriverInfoNew; } lstrcpy(pInputDriverInfoNew->m_szRegKey, szName); // Read info from reg key if (ERROR_SUCCESS != RegOpenKeyEx(hkBase, szName, 0, KEY_READ, &hkDrv)) return E_FAIL; dwBufferLen = 100; RegQueryValueEx(hkDrv, TEXT("DeviceID"), 0, NULL, (LPBYTE)pInputDriverInfoNew->m_szDeviceID, &dwBufferLen); dwBufferLen = 10; RegQueryValueEx(hkDrv, TEXT("Active"), 0, NULL, (LPBYTE)szActive, &dwBufferLen); if (lstrcmp(szActive, TEXT("1")) == 0) pInputDriverInfoNew->m_bActive = TRUE; dwBufferLen = 100; RegQueryValueEx(hkDrv, TEXT("Driver"), 0, NULL, (LPBYTE)pInputDriverInfoNew->m_szDriver16, &dwBufferLen); RegCloseKey(hkDrv); // Open corresponding key under Services\Class\Media and read more info lstrcpy(szSubMediaKey, &szName[dwNameSize - 5]); szSubMediaKey[4] = '\0'; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_CLASS TEXT("\\") REGSTR_KEY_MEDIA_CLASS, 0, KEY_READ, &hkMedia)) { if (ERROR_SUCCESS == RegOpenKeyEx(hkMedia, szSubMediaKey, 0, KEY_READ, &hkMediaDriver)) { dwBufferLen = 100; RegQueryValueEx(hkMediaDriver, TEXT("MatchingDeviceId"), 0, NULL, (LPBYTE)pInputDriverInfoNew->m_szMatchingDeviceID, &dwBufferLen); dwBufferLen = 100; RegQueryValueEx(hkMediaDriver, TEXT("Driver"), 0, NULL, (LPBYTE)pInputDriverInfoNew->m_szDriver32, &dwBufferLen); RegCloseKey(hkMediaDriver); } RegCloseKey(hkMedia); } } dwNameSize = 100; dwClassSize = 100; dwIndex++; } RegCloseKey(hkBase); return S_OK; } /**************************************************************************** * * CheckRegistry * ****************************************************************************/ HRESULT CheckRegistry(InputInfo* pInputInfo, RegError** ppRegErrorFirst) { HRESULT hr; HKEY HKCR = HKEY_CLASSES_ROOT; TCHAR szVersion[100]; HKEY hkey; DWORD cbData; ULONG ulType; DWORD dwMajor = 0; DWORD dwMinor = 0; DWORD dwRevision = 0; DWORD dwBuild = 0; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\DirectX"), 0, KEY_READ, &hkey)) { cbData = 100; RegQueryValueEx(hkey, TEXT("Version"), 0, &ulType, (LPBYTE)szVersion, &cbData); RegCloseKey(hkey); if (lstrlen(szVersion) > 6 && lstrlen(szVersion) < 20) { _stscanf(szVersion, TEXT("%d.%d.%d.%d"), &dwMajor, &dwMinor, &dwRevision, &dwBuild); } } // No registry checking on DX versions before DX7 if (dwMinor < 7) return S_OK; // 34644: check for poll flags DWORD dwData = 0; DWORD dwSize = sizeof(dwData); DWORD dwType; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\control\\MediaProperties\\PrivateProperties\\Joystick\\OEM\\Standard Gameport"), 0, KEY_READ, &hkey)) { RegQueryValueEx(hkey, TEXT("PollFlags"), NULL, &dwType, (BYTE *)&dwData, &dwSize); RegCloseKey(hkey); } pInputInfo->m_bPollFlags = ( dwData == 0x00000001 ); // From dinput.inf: if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E0-B259-11CF-BFC7-444553540000}"), TEXT(""), TEXT("*")))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E0-B259-11CF-BFC7-444553540000}\\InProcServer32"), TEXT(""), TEXT("dinput.dll"), CRF_LEAF))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E0-B259-11CF-BFC7-444553540000}\\InprocServer32"), TEXT("ThreadingModel"), TEXT("Both")))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E1-B259-11CF-BFC7-444553540000}"), TEXT(""), TEXT("*")))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E1-B259-11CF-BFC7-444553540000}\\InProcServer32"), TEXT(""), TEXT("dinput.dll"), CRF_LEAF))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{25E609E1-B259-11CF-BFC7-444553540000}\\InprocServer32"), TEXT("ThreadingModel"), TEXT("Both")))) return hr; if (!BIsPlatformNT()) { if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{92187326-72B4-11d0-A1AC-0000F8026977}"), TEXT(""), TEXT("*")))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{92187326-72B4-11d0-A1AC-0000F8026977}\\ProgID"), TEXT(""), TEXT("*")))) return hr; // Bug 119850: gchand.dll doesn't need to be on any DX7 OS. // if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{92187326-72B4-11d0-A1AC-0000F8026977}\\InProcHandler32"), TEXT(""), TEXT("gchand.dll"), CRF_LEAF))) // return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{92187326-72B4-11d0-A1AC-0000F8026977}\\InProcServer32"), TEXT(""), TEXT("gcdef.dll"), CRF_LEAF))) return hr; if (FAILED(hr = CheckRegString(ppRegErrorFirst, HKCR, TEXT("CLSID\\{92187326-72B4-11d0-A1AC-0000F8026977}\\InprocServer32"), TEXT("ThreadingModel"), TEXT("Apartment")))) return hr; } return S_OK; } /**************************************************************************** * * DestroyInputInfo * ****************************************************************************/ VOID DestroyInputInfo(InputInfo* pInputInfo) { if( pInputInfo ) { DestroyReg( &pInputInfo->m_pRegErrorFirst ); InputDeviceInfo* pInputDeviceInfo; InputDeviceInfo* pInputDeviceInfoNext; for (pInputDeviceInfo = pInputInfo->m_pInputDeviceInfoFirst; pInputDeviceInfo != NULL; pInputDeviceInfo = pInputDeviceInfoNext) { pInputDeviceInfoNext = pInputDeviceInfo->m_pInputDeviceInfoNext; delete pInputDeviceInfo; } InputDeviceInfoNT* pInputDeviceNTInfo; InputDeviceInfoNT* pInputDeviceNTInfoNext; for (pInputDeviceNTInfo = pInputInfo->m_pInputDeviceInfoNTFirst; pInputDeviceNTInfo != NULL; pInputDeviceNTInfo = pInputDeviceNTInfoNext) { pInputDeviceNTInfoNext = pInputDeviceNTInfo->m_pInputDeviceInfoNTNext; delete pInputDeviceNTInfo; } InputDriverInfo* pInputDriverInfo; InputDriverInfo* pInputDriverInfoNext; for (pInputDriverInfo = pInputInfo->m_pInputDriverInfoFirst; pInputDriverInfo != NULL; pInputDriverInfo = pInputDriverInfoNext) { pInputDriverInfoNext = pInputDriverInfo->m_pInputDriverInfoNext; delete pInputDriverInfo; } delete pInputInfo; } } /**************************************************************************** * * DiagnoseInput * ****************************************************************************/ VOID DiagnoseInput(SysInfo* pSysInfo, InputInfo* pInputInfo) { InputDeviceInfo* pInputDeviceInfo; InputDeviceInfoNT* pInputDeviceInfoNT; TCHAR szDebug[200]; TCHAR szBeta[200]; LONG lwNumDebug; LONG lwNumBeta; TCHAR szListContinuer[30]; TCHAR szListEtc[30]; TCHAR szFmt[300]; TCHAR szMessage[300]; BOOL bProblem = FALSE; if( pInputInfo == NULL ) return; lwNumDebug = 0; lwNumBeta = 0; LoadString(NULL, IDS_LISTCONTINUER, szListContinuer, 30); LoadString(NULL, IDS_LISTETC, szListEtc, 30); for (pInputDeviceInfo = pInputInfo->m_pInputDeviceInfoFirst; pInputDeviceInfo != NULL; pInputDeviceInfo = pInputDeviceInfo->m_pInputDeviceInfoNext) { if (pInputDeviceInfo->m_bBeta) { pInputDeviceInfo->m_bProblem = TRUE; bProblem = TRUE; lwNumBeta++; if (lwNumBeta == 1) { lstrcpy(szBeta, pInputDeviceInfo->m_szDriverName); } else if (lwNumBeta < 4) { lstrcat(szBeta, szListContinuer); lstrcat(szBeta, pInputDeviceInfo->m_szDriverName); } else if (lwNumBeta < 5) { lstrcat(szBeta, szListEtc); } } if (pInputDeviceInfo->m_bDebug) { pInputDeviceInfo->m_bProblem = TRUE; bProblem = TRUE; lwNumDebug++; if (lwNumDebug == 1) { lstrcpy(szDebug, pInputDeviceInfo->m_szDriverName); } else if (lwNumDebug < 4) { lstrcat(szDebug, szListContinuer); lstrcat(szDebug, pInputDeviceInfo->m_szDriverName); } else if (lwNumDebug < 5) { lstrcat(szDebug, szListEtc); } } } _tcscpy( pSysInfo->m_szInputNotes, TEXT("") ); _tcscpy( pSysInfo->m_szInputNotesEnglish, TEXT("") ); for (pInputDeviceInfoNT = pInputInfo->m_pInputDeviceInfoNTFirst; pInputDeviceInfoNT != NULL; pInputDeviceInfoNT = pInputDeviceInfoNT->m_pInputDeviceInfoNTNext) { if (pInputDeviceInfoNT->m_dwProblem != 0) { bProblem = TRUE; pInputDeviceInfoNT->m_bProblem = TRUE; LoadString(NULL, IDS_INPUTDEVPROBLEMFMT, szFmt, 300); wsprintf(szMessage, szFmt, pInputDeviceInfoNT->m_szName, pInputDeviceInfoNT->m_dwProblem); _tcscat( pSysInfo->m_szInputNotes, szMessage ); LoadString(NULL, IDS_INPUTDEVPROBLEMFMT_ENGLISH, szFmt, 300); wsprintf(szMessage, szFmt, pInputDeviceInfoNT->m_szName, pInputDeviceInfoNT->m_dwProblem); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } if (pInputDeviceInfoNT->m_dwPortProblem != 0) { bProblem = TRUE; pInputDeviceInfoNT->m_bProblem = TRUE; LoadString(NULL, IDS_INPUTPORTPROBLEMFMT, szFmt, 300); wsprintf(szMessage, szFmt, pInputDeviceInfoNT->m_szPortName, pInputDeviceInfoNT->m_dwPortProblem); _tcscat( pSysInfo->m_szInputNotes, szMessage ); LoadString(NULL, IDS_INPUTPORTPROBLEMFMT_ENGLISH, szFmt, 300); wsprintf(szMessage, szFmt, pInputDeviceInfoNT->m_szPortName, pInputDeviceInfoNT->m_dwPortProblem); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } } if (lwNumBeta > 0) { if (lwNumBeta == 1) LoadString(NULL, IDS_BETADRIVERFMT1, szFmt, 300); else LoadString(NULL, IDS_BETADRIVERFMT2, szFmt, 300); wsprintf(szMessage, szFmt, szBeta); _tcscat( pSysInfo->m_szInputNotes, szMessage ); if (lwNumBeta == 1) LoadString(NULL, IDS_BETADRIVERFMT1_ENGLISH, szFmt, 300); else LoadString(NULL, IDS_BETADRIVERFMT2_ENGLISH, szFmt, 300); wsprintf(szMessage, szFmt, szBeta); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } if (lwNumDebug > 0) { if (lwNumDebug == 1) LoadString(NULL, IDS_DEBUGDRIVERFMT1, szFmt, 300); else LoadString(NULL, IDS_DEBUGDRIVERFMT2, szFmt, 300); wsprintf(szMessage, szFmt, szDebug); _tcscat( pSysInfo->m_szInputNotes, szMessage ); if (lwNumDebug == 1) LoadString(NULL, IDS_DEBUGDRIVERFMT1_ENGLISH, szFmt, 300); else LoadString(NULL, IDS_DEBUGDRIVERFMT2_ENGLISH, szFmt, 300); wsprintf(szMessage, szFmt, szDebug); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } if (pInputInfo->m_pInputDeviceInfoFirst == NULL && pInputInfo->m_pInputDeviceInfoNTFirst == NULL) { LoadString(NULL, IDS_NOINPUT, szMessage, 300); _tcscat( pSysInfo->m_szInputNotes, szMessage ); LoadString(NULL, IDS_NOINPUT_ENGLISH, szMessage, 300); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } if (pInputInfo->m_pRegErrorFirst != NULL) { bProblem = TRUE; LoadString(NULL, IDS_REGISTRYPROBLEM, szMessage, 300); _tcscat( pSysInfo->m_szInputNotes, szMessage ); LoadString(NULL, IDS_REGISTRYPROBLEM_ENGLISH, szMessage, 300); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } if (!bProblem) { LoadString(NULL, IDS_NOPROBLEM, szMessage, 300); _tcscat( pSysInfo->m_szInputNotes, szMessage ); LoadString(NULL, IDS_NOPROBLEM_ENGLISH, szMessage, 300); _tcscat( pSysInfo->m_szInputNotesEnglish, szMessage ); } }