|
|
/*****************************************************************************
* * 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; }
|