/***************************************************************************** * * DIApHack.c * * Copyright (c) 1999 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Support routines for app hacks * * Contents: * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ //ISSUE-2001/03/29-timgill Need to sort out a prefixed version of of SquirtSqflPtszV #define sqfl sqflCompat typedef enum { DICOMPATID_REACQUIRE, // Perform auto reaquire if device lost DICOMPATID_NOSUBCLASS, // Do not use subclassing DICOMPATID_MAXDEVICENAMELENGTH } DIAPPHACKID, *LPDIAPPHACKID; typedef struct tagAPPHACKENTRY { LPCTSTR pszName; DWORD cbData; DWORD dwOSMask; } APPHACKENTRY, *LPAPPHACKENTRY; typedef struct tagAPPHACKTABLE { LPAPPHACKENTRY aEntries; ULONG cEntries; } APPHACKTABLE, *LPAPPHACKTABLE; #define BEGIN_DECLARE_APPHACK_ENTRIES(name) \ APPHACKENTRY name[] = { #define DECLARE_APPHACK_ENTRY(name, type, osmask) \ { TEXT(#name), sizeof(type), osmask }, #define END_DECLARE_APPHACK_ENTRIES() \ }; #define BEGIN_DECLARE_APPHACK_TABLE(name) \ APPHACKTABLE name = #define DECLARE_APPHACK_TABLE(entries) \ { entries, cA(entries) } #define END_DECLARE_APPHACK_TABLE() \ ; #define DIHACKOS_WIN2K (0x00000001L) #define DIHACKOS_WIN9X (0x00000002L) BEGIN_DECLARE_APPHACK_ENTRIES(g_aheAppHackEntries) DECLARE_APPHACK_ENTRY(ReAcquire, BOOL, DIHACKOS_WIN2K ) DECLARE_APPHACK_ENTRY(NoSubClass, BOOL, DIHACKOS_WIN2K ) DECLARE_APPHACK_ENTRY(MaxDeviceNameLength, DWORD, DIHACKOS_WIN2K | DIHACKOS_WIN9X ) END_DECLARE_APPHACK_ENTRIES() BEGIN_DECLARE_APPHACK_TABLE(g_ahtAppHackTable) DECLARE_APPHACK_TABLE(g_aheAppHackEntries) END_DECLARE_APPHACK_TABLE() /*************************************************************************** * * AhGetOSMask * * Description: * Gets the mask for the current OS * This mask should be used when we get app hacks for more than just * Win2k such that hacks can be applied selectively per OS. * For now just #define a value as constant. * * Arguments: * none * * Returns: * DWORD: Mask of flags applicable for the current OS. * ***************************************************************************/ #ifdef WINNT #define AhGetOSMask() DIHACKOS_WIN2K #else #define AhGetOSMask() DIHACKOS_WIN9X #endif /*************************************************************************** * * @doc INTERNAL * * @func BOOL | AhGetCurrentApplicationPath | * * Gets the full path to the current application's executable. * * @parm OUT LPTSTR | pszPath | * * The executable full path name * * @parm OUT LPTSTR * | ppszModule | * * Pointer to the first character within the above of the module name * * @returns * * TRUE on success. * ***************************************************************************/ BOOL AhGetCurrentApplicationPath ( LPTSTR pszPath, LPTSTR * ppszModule ) { BOOL fSuccess = TRUE; TCHAR szOriginal[MAX_PATH]; EnterProcI(AhGetCurrentApplicationPath, (_ "")); fSuccess = GetModuleFileName(GetModuleHandle(NULL), szOriginal, cA(szOriginal)); if(fSuccess) { fSuccess = ( GetFullPathName(szOriginal, MAX_PATH, pszPath, ppszModule) != 0 ); } ExitProcF(fSuccess); return fSuccess; } /***************************************************************************** * * @doc INTERNAL * * @func BOOL | AhGetAppDateAndFileLen | * * Gets the data time stamp and file length of the current * application as used to identify the application. * * @parm IN LPTSTR | pszExecutable | * * The executable full path name. * * @returns * * TRUE on success. * *****************************************************************************/ BOOL AhGetAppDateAndFileLen ( LPTSTR pszExecutable ) { HANDLE hFile = NULL; IMAGE_NT_HEADERS nth; IMAGE_DOS_HEADER dh; DWORD cbRead; BOOL fSuccess; EnterProcI(AhGetApplicationId, (_ "")); // Open the executable hFile = CreateFile( pszExecutable, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if( hFile && ( hFile != INVALID_HANDLE_VALUE ) ) { // Read the executable's DOS header fSuccess = ReadFile(hFile, &dh, sizeof(dh), &cbRead, NULL); if(!fSuccess || sizeof(dh) != cbRead) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Unable to read DOS header") ); fSuccess = FALSE; } if(fSuccess && IMAGE_DOS_SIGNATURE != dh.e_magic) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Invalid DOS signature") ); fSuccess = FALSE; } // Read the executable's PE header if(fSuccess) { cbRead = SetFilePointer(hFile, dh.e_lfanew, NULL, FILE_BEGIN); if((LONG)cbRead != dh.e_lfanew) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Unable to seek to PE header") ); fSuccess = FALSE; } } if(fSuccess) { fSuccess = ReadFile(hFile, &nth, sizeof(nth), &cbRead, NULL); if(!fSuccess || sizeof(nth) != cbRead) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Unable to read PE header") ); fSuccess = FALSE; } } if(fSuccess && IMAGE_NT_SIGNATURE != nth.Signature) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Invalid PE signature") ); fSuccess = FALSE; } // Get the executable's size if(fSuccess) { g_dwAppDate = nth.FileHeader.TimeDateStamp; // Assuming < 4 GB g_dwAppFileLen = GetFileSize(hFile, NULL); if( (DWORD)(-1) == g_dwAppFileLen ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("Unable to get file size") ); fSuccess = FALSE; } } // Clean up CloseHandle( hFile ); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("CreateFile failed to open %s with error %lu"), pszExecutable, GetLastError() ); fSuccess = FALSE; } ExitProcF(fSuccess); return fSuccess; } /*************************************************************************** * * AhOpenApplicationKey * * Description: * Opens or creates the application's root key. * * Arguments: * LPCTSTR [in]: application id. * * Returns: * HKEY: registry key handle. * ***************************************************************************/ HKEY AhOpenApplicationKey ( LPCTSTR pszAppId ) { #ifdef DEBUG TCHAR szName[0x100] = { 0 }; LONG cbName = sizeof(szName); #endif // DEBUG HKEY hkeyAll = NULL; HKEY hkeyApp = NULL; HRESULT hr; EnterProcI(AhOpenApplicationKey, (_ "")); // Open the parent key hr = hresMumbleKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_DINPUT TEXT("\\") REGSTR_KEY_APPHACK, KEY_READ, 0, &hkeyAll ); if(SUCCEEDED(hr)) { hr = hresMumbleKeyEx( hkeyAll, pszAppId, KEY_READ, 0, &hkeyApp ); RegCloseKey( hkeyAll ); #ifdef DEBUG // Query for the application description if(SUCCEEDED(hr)) { JoyReg_GetValue( hkeyApp, NULL, REG_SZ, szName, cbName ); SquirtSqflPtszV(sqfl | sqflTrace, TEXT( "Application description: %ls"), szName ); } #endif // DEBUG } ExitProc(); return hkeyApp; } /*************************************************************************** * * AhGetHackValue * * Description: * Queries an apphack value. * * Arguments: * HKEY [in]: application registry key. * DSAPPHACKID [in]: apphack id. * LPVOID [out]: receives apphack data. * DWORD [in]: size of above data buffer. * * Returns: * BOOL: TRUE on success. * ***************************************************************************/ BOOL AhGetHackValue ( HKEY hkey, DWORD dwOSMask, DIAPPHACKID ahid, LPVOID pvData, DWORD cbData ) { HRESULT hr; EnterProcI(AhGetHackValue, (_ "")); AssertF(ahid < (DIAPPHACKID)g_ahtAppHackTable.cEntries); AssertF(cbData == g_ahtAppHackTable.aEntries[ahid].cbData); if( !( dwOSMask & g_ahtAppHackTable.aEntries[ahid].dwOSMask ) ) { hr = DI_OK; } else { hr = JoyReg_GetValue( hkey, g_ahtAppHackTable.aEntries[ahid].pszName, REG_BINARY, pvData, cbData ); if( !SUCCEEDED( hr ) ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("failed to read value \"%s\", code 0x%08x"), g_ahtAppHackTable.aEntries[ahid].pszName, hr); } } ExitProcF(DI_OK == hr); return DI_OK == hr; } /*************************************************************************** * * AhGetAppHacks * * Description: * Gets all app-hacks for the current application. * * Arguments: * LPDSAPPHACKS [out]: receives app-hack data. * * Returns: * BOOL: TRUE if any apphacks exist for the current application. * ***************************************************************************/ BOOL AhGetAppHacks ( LPTSTR tszAppId ) { DIAPPHACKS ahTemp; HKEY hkey = NULL; BOOL fFound; DWORD dwOSMask; EnterProcI(AhGetAppHacks, (_ "s", tszAppId)); /* * Assume defaults as most apps will have no registry entries */ ahTemp = g_AppHacks; /* * Get the OS version mask */ dwOSMask = AhGetOSMask(); SquirtSqflPtszV(sqfl | sqflTrace, TEXT("Finding apphacks for %s..."), tszAppId); /* * Open the application key */ hkey = AhOpenApplicationKey(tszAppId); #define GET_APP_HACK( hackid, field ) \ if( !AhGetHackValue( hkey, dwOSMask, hackid, &ahTemp.##field, sizeof(ahTemp.##field) ) ) \ { \ ahTemp.##field = g_AppHacks.##field; \ } /* * Query all apphack values */ if( hkey && (hkey != INVALID_HANDLE_VALUE ) ) { GET_APP_HACK( DICOMPATID_REACQUIRE, fReacquire ); GET_APP_HACK( DICOMPATID_NOSUBCLASS, fNoSubClass ); GET_APP_HACK( DICOMPATID_MAXDEVICENAMELENGTH, nMaxDeviceNameLength ); /* * Copy back over the defaults */ g_AppHacks = ahTemp; SquirtSqflPtszV(sqfl | sqflTrace, TEXT("fReacquire: %d"), g_AppHacks.fReacquire ); SquirtSqflPtszV(sqfl | sqflTrace, TEXT("fNoSubClass: %d"), g_AppHacks.fNoSubClass ); SquirtSqflPtszV(sqfl | sqflTrace, TEXT("nMaxDeviceNameLength: %d"), g_AppHacks.nMaxDeviceNameLength ); RegCloseKey(hkey); fFound = TRUE; } else { SquirtSqflPtszV(sqfl | sqflTrace, TEXT("No apphacks exist") ); fFound = FALSE; } #undef GET_APP_HACK ExitProc(); return fFound; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | AhAppRegister | * * Registers an app * ISSUE-2001/03/29-timgill Needs more function documentation * * @parm IN DWORD | dwVer | * * The version of DInput for which the application was compiled * * @parm IN DWORD | dwMapper | * * 0 if the caller * * @returns * * TRUE on success. * *****************************************************************************/ HRESULT EXTERNAL AhAppRegister(DWORD dwVer, DWORD dwMapper) { HRESULT hres = S_OK; EnterProcI(AhAppRegister, (_ "xx", dwVer, dwMapper)); /* * It is important to serialize this global processing here so that * elsewhere we can make read-only access to the data set up without * making any further checks. However if an application has already * been found to use the mapper we can safely test that. If another * thread is just about to set g_dwLastMsgSent to * DIMSGWP_DX8MAPPERAPPSTART then it will be done by the time we get * the critical section and we'll just find that there's nothing to * update and no point in sending a message. * DIMSGWP_DX8MAPPERAPPSTART is the only case that needs to be fast * as the other cases normally only happen once. */ if( g_dwLastMsgSent == DIMSGWP_DX8MAPPERAPPSTART ) { /* * Fast out if everything has been done already */ } else { TCHAR szExecutable[MAX_PATH]; LPTSTR pszModule; TCHAR szAppId[MAX_PATH + 8 +8]; DWORD dwAppStat = 0; BOOL fSuccess; hres = E_FAIL; DllEnterCrit(); // Get the application path if( AhGetCurrentApplicationPath( szExecutable, &pszModule ) ) { if( !g_dwLastMsgSent ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("Application executable path: %s"), szExecutable); SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("Application module: %s"), pszModule); fSuccess = AhGetAppDateAndFileLen( szExecutable ); } else { fSuccess = TRUE; } if( fSuccess ) { HKEY hKeyMain; hres = hresMumbleKeyEx( HKEY_CURRENT_USER, REGSTR_PATH_DINPUT, KEY_READ | KEY_WRITE, 0, &hKeyMain ); if( SUCCEEDED( hres ) ) { HKEY hKey; DWORD dwAppIdFlag; DWORD dwAppDate = g_dwAppDate, dwAppSize = g_dwAppFileLen; DWORD cb = cbX(dwAppIdFlag); if( ERROR_SUCCESS == RegQueryValueEx( hKeyMain, DIRECTINPUT_REGSTR_VAL_APPIDFLAG, 0, 0, (PUCHAR)&dwAppIdFlag, &cb ) ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("AppIdFlag: %d"), dwAppIdFlag ); if( dwAppIdFlag & DIAPPIDFLAG_NOTIME ){ dwAppDate = 0; } if( dwAppIdFlag & DIAPPIDFLAG_NOSIZE ){ dwAppSize = 0; } } CharUpper( pszModule ); wsprintf( szAppId, TEXT("%s%8.8lX%8.8lX"), pszModule, dwAppDate, dwAppSize ); SquirtSqflPtszV( sqfl | sqflTrace, TEXT("Application id: %s"), szAppId ); /* * We must only get app hacks once, otherwise a FF driver * that uses DInput will corrupt the application app hacks */ if( !g_dwLastMsgSent ) { AhGetAppHacks( szAppId ); } /* * See if this app has been registered before. */ if( ERROR_SUCCESS == RegOpenKeyEx( hKeyMain, szAppId, 0, KEY_READ, &hKey ) ) { cb = cbX(dwAppStat); if( ERROR_SUCCESS == RegQueryValueEx( hKey, DIRECTINPUT_REGSTR_VAL_MAPPER, 0, 0, (PUCHAR)&dwAppStat, &cb ) ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("Previous application mapper state: %d"), dwAppStat ); if( dwAppStat ) { dwAppStat = DIMSGWP_DX8MAPPERAPPSTART; dwMapper = 1; } else { dwAppStat = DIMSGWP_DX8APPSTART; } } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Missing ") DIRECTINPUT_REGSTR_VAL_MAPPER TEXT(" value for %s"), szAppId); dwAppStat = DIMSGWP_NEWAPPSTART; } RegCloseKey( hKey ); } else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("Unknown application") ); dwAppStat = DIMSGWP_NEWAPPSTART; } /* * Write out the application key if none existed or if * we've just found out that this app uses the mapper. */ if( ( dwAppStat == DIMSGWP_NEWAPPSTART ) || ( ( dwAppStat == DIMSGWP_DX8APPSTART ) && dwMapper ) ) { hres = hresMumbleKeyEx( hKeyMain, szAppId, KEY_WRITE, 0, &hKey ); if( SUCCEEDED(hres) ) { RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_NAME, 0x0, REG_SZ, (PUCHAR)pszModule, cbCtch(lstrlen(pszModule)+1) ); RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_MAPPER, 0x0, REG_BINARY, (PUCHAR) &dwMapper, cbX(dwMapper)); RegCloseKey(hKey); } /* * Make dwAppStat the state to be sent if all goes well */ if( dwMapper ) { dwAppStat = DIMSGWP_DX8MAPPERAPPSTART; } } if( SUCCEEDED(hres) && ( g_dwLastMsgSent != dwAppStat ) ) { hres = hresMumbleKeyEx( hKeyMain, ( dwMapper ) ? DIRECTINPUT_REGSTR_KEY_LASTMAPAPP : DIRECTINPUT_REGSTR_KEY_LASTAPP, KEY_WRITE, 0, &hKey ); if( SUCCEEDED(hres) ) { FILETIME ftSysTime; GetSystemTimeAsFileTime( &ftSysTime ); /* * Update g_dwLastMsgSent while we're still in the * critical section. */ g_dwLastMsgSent = dwAppStat; RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_NAME, 0x0, REG_SZ, (PUCHAR)pszModule, cbCtch(lstrlen(pszModule)+1)); RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_ID, 0x0, REG_SZ, (PUCHAR) szAppId, cbCtch(lstrlen(szAppId)+1) ); RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_VERSION, 0x0, REG_BINARY, (PUCHAR) &dwVer, cbX(dwVer)); RegSetValueEx(hKey, DIRECTINPUT_REGSTR_VAL_LASTSTART, 0x0, REG_BINARY, (PUCHAR)&ftSysTime, cbX(ftSysTime)); RegCloseKey(hKey); } else { /* * Zero dwAppStat to indicate that no message * should be sent. */ dwAppStat = 0; } } else { /* * Zero dwAppStat to indicate that no message * should be sent. */ dwAppStat = 0; } RegCloseKey(hKeyMain); } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("Failed to open DirectInput application status key (0x%08x)"), hres ); } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("Failed to get application details") ); } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("Failed to get application path") ); } DllLeaveCrit(); /* * If there's a message to send, broadcast it outside the critical section */ if( dwAppStat ) { PostMessage( HWND_BROADCAST, g_wmDInputNotify, dwAppStat, 0L); } } ExitOleProc(); return hres; }