/* The Logitech Controller Input SDK, including all accompanying documentation, is protected by intellectual property laws. All rights not expressly granted by Logitech are reserved. */ #include "LogiControllerInputUtils.h" #include #include #include #include #include #include #include #pragma comment(lib,"setupapi.lib") #define LOGITECH_SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } LogitechControllerInput::Utils* LogitechControllerInput::Utils::m_instance = 0;// initialize pointer LogitechControllerInput::Utils* LogitechControllerInput::Utils::Instance() { static Utils inst; return &inst; } LogitechControllerInput::Utils::Utils() { } BOOL LogitechControllerInput::Utils::IsXInputDevice(CONST GUID* pGuidProductFromDirectInput) { IWbemLocator* pIWbemLocator = NULL; IEnumWbemClassObject* pEnumDevices = NULL; IWbemClassObject* pDevices[20] = {0}; IWbemServices* pIWbemServices = NULL; BSTR bstrNamespace = NULL; BSTR bstrDeviceID = NULL; BSTR bstrClassName = NULL; DWORD uReturned = 0; bool bIsXinputDevice= false; UINT iDevice = 0; VARIANT var; HRESULT hr; // CoInit if needed hr = CoInitialize(NULL); bool bCleanupCOM = SUCCEEDED(hr); // Create WMI hr = CoCreateInstance( __uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*) &pIWbemLocator); if( FAILED(hr) || pIWbemLocator == NULL ) goto LCleanup; bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; // Connect to WMI hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices ); if( FAILED(hr) || pIWbemServices == NULL ) goto LCleanup; // Switch security level to IMPERSONATE. CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); if( FAILED(hr) || pEnumDevices == NULL ) goto LCleanup; // Loop over all devices for( ;; ) { // Get 20 at a time hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); if( FAILED(hr) ) goto LCleanup; if( uReturned == 0 ) break; for( iDevice=0; iDeviceGet( bstrDeviceID, 0L, &var, NULL, NULL ); if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) { // Check if the device ID contains "IG_". If it does, then it's an XInput device // This information can not be found from DirectInput if( wcsstr( var.bstrVal, L"IG_" ) ) { // If it does, then get the VID/PID from var.bstrVal DWORD dwPid = 0, dwVid = 0; WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" ); if( strVid && swscanf_s( strVid, L"VID_%4X", &dwVid ) != 1 ) dwVid = 0; WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" ); if( strPid && swscanf_s( strPid, L"PID_%4X", &dwPid ) != 1 ) dwPid = 0; // Compare the VID/PID to the DInput device DWORD dwVidPid = MAKELONG( dwVid, dwPid ); if (NULL == pGuidProductFromDirectInput) goto LCleanup; if( dwVidPid == pGuidProductFromDirectInput->Data1 ) { bIsXinputDevice = true; goto LCleanup; } } } LOGITECH_SAFE_RELEASE( pDevices[iDevice] ); } } LCleanup: if(bstrNamespace) SysFreeString(bstrNamespace); if(bstrDeviceID) SysFreeString(bstrDeviceID); if(bstrClassName) SysFreeString(bstrClassName); for( iDevice=0; iDevice<20; iDevice++ ) LOGITECH_SAFE_RELEASE( pDevices[iDevice] ); LOGITECH_SAFE_RELEASE( pEnumDevices ); LOGITECH_SAFE_RELEASE( pIWbemLocator ); LOGITECH_SAFE_RELEASE( pIWbemServices ); if( bCleanupCOM ) CoUninitialize(); return bIsXinputDevice; } VOID LogitechControllerInput::Utils::LogiTrace(LPCTSTR lpszFormat, ...) { va_list args; va_start(args, lpszFormat); int nBuf; TCHAR szBuffer[512]; nBuf = _vsntprintf_s(szBuffer, _countof(szBuffer), sizeof(szBuffer) / sizeof(TCHAR), lpszFormat, args); // was there an error? was the expanded string too long? _ASSERT(nBuf >= 0); OutputDebugString(szBuffer); va_end(args); //va_list args; //va_start(args, strFormat); //nPos += _vsntprintf(pBuffer+nPos,nMaxSize,strFormat,args); //va_end(args); //// print the end of the line to buffer //_stprintf(pBuffer+nPos,_T("\r\n")); } BOOL LogitechControllerInput::Utils::IsEven(INT nbr) { if (0 == nbr % 2) { return TRUE; } return FALSE; } LogitechControllerInput::STRING LogitechControllerInput::Utils::StringToUpper(LogitechControllerInput::STRING myString) { for(unsigned int index_=0; index_< myString.length(); index_++) { myString[index_] = static_cast(_totupper(myString[index_])); } return myString; } HRESULT LogitechControllerInput::Utils::GetUniqueIDFromDbcc_name(TCHAR* outputString, CONST TCHAR* inputString) { STRING dbccName_(inputString); size_t beginning_ = dbccName_.find(_T("#")); _ASSERT(-1 != beginning_); beginning_ = dbccName_.find(_T("#"), beginning_ + 1); ++beginning_; size_t end_ = dbccName_.find(_T("#"), beginning_ + 1); _ASSERT(-1 != end_); _ASSERT(end_ > beginning_); if (-1 == end_ || end_ < beginning_) return E_FAIL; STRING outputString_ = dbccName_.substr(beginning_, end_ - beginning_); _tcscpy_s(outputString, MAX_PATH, outputString_.c_str()); return S_OK; } VOID LogitechControllerInput::Utils::SetRecurringTimer(INT id, DWORD initialTickCounts, DWORD endTickCounts, DWORD intervals) { INT idAlreadyUsed_ = -1; // check if ID not already taken for (UINT index_ = 0; index_ < m_recurringTimerData.size(); index_++) { if (m_recurringTimerData[index_].id == id) { idAlreadyUsed_ = index_; break; } } if (-1 != idAlreadyUsed_) { m_recurringTimerData[idAlreadyUsed_].initialTickCounts = initialTickCounts; m_recurringTimerData[idAlreadyUsed_].endTickCounts = endTickCounts; m_recurringTimerData[idAlreadyUsed_].intervals = intervals; m_recurringTimerData[idAlreadyUsed_].previousTickZone = -1; } else { RecurringTimerData data_; ZeroMemory(&data_, sizeof(data_)); data_.id = id; data_.initialTickCounts = initialTickCounts; data_.endTickCounts = endTickCounts; data_.intervals = intervals; data_.previousTickZone = -1; m_recurringTimerData.push_back(data_); } } BOOL LogitechControllerInput::Utils::TimerTriggered(INT id) { INT correspondingIndex_ = -1; for (UINT index_ = 0; index_ < m_recurringTimerData.size(); index_++) { if (m_recurringTimerData[index_].id == id) { correspondingIndex_ = index_; break; } } if (-1 == correspondingIndex_) { return FALSE; } DWORD initialTickCounts_ = m_recurringTimerData[correspondingIndex_].initialTickCounts; DWORD endTickCounts_ = m_recurringTimerData[correspondingIndex_].endTickCounts; DWORD intervals_ = m_recurringTimerData[correspondingIndex_].intervals; DWORD currentTicks_ = GetTickCount(); _ASSERT(endTickCounts_ >= initialTickCounts_); if (currentTicks_ >= endTickCounts_ || endTickCounts_ < initialTickCounts_ || initialTickCounts_ > currentTicks_) { return FALSE; } INT currentTickZone_ = static_cast(static_cast(currentTicks_ - initialTickCounts_) / static_cast(intervals_)); if (m_recurringTimerData[correspondingIndex_].previousTickZone != currentTickZone_) { m_recurringTimerData[correspondingIndex_].previousTickZone = currentTickZone_; return TRUE; } return FALSE; } INT LogitechControllerInput::Utils::FindIG_Number(STRING deviceIDString) { // Find IG_ number, if present size_t beginning_ = deviceIDString.find(_T("IG_")); if (-1 != beginning_) { beginning_ = beginning_ + 3; size_t end_ = deviceIDString.find(_T("\\"), beginning_); STRING numberString_ = deviceIDString.substr(beginning_, end_ - beginning_); INT number_ = _ttoi(numberString_.c_str()); _ASSERT(number_ >= 0); return number_; } return -1; } LogitechControllerInput::STRING LogitechControllerInput::Utils::GetUniqueID(LogitechControllerInput::STRING deviceIDString) { // Find unique ID size_t beginning_ = deviceIDString.find(_T("\\")); ++beginning_; beginning_ = deviceIDString.find(_T("\\"), beginning_); ++beginning_; return deviceIDString.substr(beginning_, deviceIDString.length() - beginning_ ); } HRESULT LogitechControllerInput::Utils::GetDeviceIDStringFromSetupDI(STRING_VECTOR& deviceID, CONST DWORD vid, CONST DWORD pid) { TCHAR vidString_[MAX_PATH] = {'\0'}; INT ret_ = wsprintf( vidString_, TEXT("%.4x"), vid); TCHAR pidString_[MAX_PATH] = {'\0'}; ret_ = wsprintf( pidString_, TEXT("%.4x"), pid); STRING searchString_ = _T(""); searchString_.append(_T("HID\\VID_")); searchString_.append(vidString_); searchString_.append(_T("&PID_")); searchString_.append(pidString_); searchString_ = Utils::Instance()->StringToUpper(searchString_); HDEVINFO devInfo_ = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_PROFILE); if( INVALID_HANDLE_VALUE == devInfo_ ) { /*switch(GetLastError()) { case ERROR_INVALID_PARAMETER: LOGICONTROLLERTRACE(_T("ERROR SetupDiGetClassDevs: ERROR_INVALID_PARAMETER\n")); break; case ERROR_INVALID_FLAGS: LOGICONTROLLERTRACE(_T("ERROR SetupDiGetClassDevs: ERROR_INVALID_FLAGS\n")); break; }*/ return E_FAIL; } SP_DEVINFO_DATA* devInfoData_ = (SP_DEVINFO_DATA*)HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); if (NULL == devInfoData_) return E_FAIL; devInfoData_->cbSize = sizeof(SP_DEVINFO_DATA); for(INT ii = 0; SetupDiEnumDeviceInfo(devInfo_,ii,devInfoData_); ii++) { DWORD size_ = 0; TCHAR buf_[MAX_PATH]; if ( !SetupDiGetDeviceInstanceId(devInfo_, devInfoData_, buf_, _countof(buf_), &size_) ) { break; } STRING tempText_(buf_); //LOGICONTROLLERTRACE(_T("%s\n"), buf_); size_t beginning_ = tempText_.find(searchString_); if (-1 != beginning_) { deviceID.push_back(tempText_); } } if ( devInfoData_ ) HeapFree(GetProcessHeap(), 0, devInfoData_); SetupDiDestroyDeviceInfoList(devInfo_); return S_OK; } HRESULT LogitechControllerInput::Utils::GetVidPid(CONST STRING deviceIDString, DWORD &vid, DWORD &pid) { // "HID\VID_046D&PID_C298\8&2A00AA53&A&0000" // VID size_t beginning_ = deviceIDString.find(_T("VID_")); if (-1 == beginning_) { return E_FAIL; } beginning_ = beginning_ + 4; STRING vidString_ = deviceIDString.substr(beginning_, 4); _ASSERT(4 == vidString_.size()); if (4 != vidString_.size()) { return E_FAIL; } vid = HexStringToInt(vidString_.c_str()); // PID beginning_ = deviceIDString.find(_T("PID_")); if (-1 == beginning_) { return E_FAIL; } beginning_ = beginning_ + 4; STRING pidString_ = deviceIDString.substr(beginning_, 4); _ASSERT(4 == pidString_.size()); if (4 != pidString_.size()) { return E_FAIL; } pid = HexStringToInt(pidString_.c_str()); return S_OK; } #ifdef _UNICODE template bool from_string(T& t, const std::wstring& s, std::ios_base& (*f)(std::ios_base&)) { std::wistringstream iss(s); return !(iss >> f >> t).fail(); } #else template bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&)) { std::stringstream iss(s); return !(iss >> f >> t).fail(); } #endif INT LogitechControllerInput::Utils::HexStringToInt(CONST TCHAR *value) { int result_; #ifdef _UNICODE if(from_string(result_, std::wstring(value), std::hex)) #else if(from_string(result_, std::string(value), std::hex)) #endif { return result_; } return 0; }