/***************************************************************************** * * DIJoyCfg.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * IDirectInputJoyConfig * * Contents: * * CJoyCfg_New * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflJoyCfg #if DIRECTINPUT_VERSION > 0x0300 BOOL fVjoydDeviceNotExist = TRUE; #pragma BEGIN_CONST_DATA /***************************************************************************** * * Declare the interfaces we will be providing. * * WARNING! If you add a secondary interface, you must also change * CJoyCfg_New! * *****************************************************************************/ Primary_Interface(CJoyCfg, IDirectInputJoyConfig); Interface_Template_Begin(CJoyCfg) Primary_Interface_Template(CJoyCfg, IDirectInputJoyConfig) Interface_Template_End(CJoyCfg) /***************************************************************************** * * @doc INTERNAL * * @struct CJoyCfg | * * The object. Note that this is * aggregated onto the main object. * * @field IDirectInputJoyConfig | djc | * * The object (containing vtbl). * * @field BOOL | fAcquired:1 | * * Set if joystick configuration has been acquired. * * @field BOOL | fCritInited:1 | * * Set if the critical section has been initialized. * * @field HKEY | hkTypesW | * * Read/write key to access the joystick types. * This key is created only while acquired. * * @field DWORD | idJoyCache | * * The identifier of the joystick in the effect shepherd cache, * if there is anything in the cache at all. * * @field IDirectInputEffectShepherd * | pes | * * The cached effect shepherd itself. * * @field LONG | cCrit | * * Number of times the critical section has been taken. * Used only in XDEBUG to check whether the caller is * releasing the object while another method is using it. * * @field DWORD | thidCrit | * * The thread that is currently in the critical section. * Used only in DEBUG for internal consistency checking. * * @field CRITICAL_SECTION | crst | * * Object critical section. Must be taken when accessing * volatile member variables. * *****************************************************************************/ typedef struct CJoyCfg { /* Supported interfaces */ IDirectInputJoyConfig djc; BOOL fAcquired:1; BOOL fCritInited:1; HKEY hkTypesW; HWND hwnd; DWORD discl; DWORD idJoyCache; LPDIRECTINPUTEFFECTSHEPHERD pes; RD(LONG cCrit;) D(DWORD thidCrit;) CRITICAL_SECTION crst; } CJoyCfg, JC, *PJC; typedef LPDIRECTINPUTJOYCONFIG PDJC; #define ThisClass CJoyCfg #define ThisInterface IDirectInputJoyConfig #define ThisInterfaceT IDirectInputJoyConfig /***************************************************************************** * * Forward references * * Not really needed; just a convenience, because Finalize * calls Unacquire to clean up in the case where the caller forgot. * *****************************************************************************/ STDMETHODIMP CJoyCfg_InternalUnacquire(PV pdd); /***************************************************************************** * * @doc INTERNAL * * @func TCHAR | CJoyCfg_CharFromType | * * Convert a predefined type number to a character. * * @func UINT | CJoyCfg_TypeFromChar | * * Convert a character back to a predefined type number. * *****************************************************************************/ #define CJoyCfg_CharFromType(t) ((TCHAR)(L'0' + t)) #define CJoyCfg_TypeFromChar(tch) ((tch) - L'0') /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputJoyConfig | EnterCrit | * * Enter the object critical section. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * *****************************************************************************/ void INLINE CJoyCfg_EnterCrit(PJC this) { EnterCriticalSection(&this->crst); D(this->thidCrit = GetCurrentThreadId()); RD(InterlockedIncrement(&this->cCrit)); } /***************************************************************************** * * @doc INTERNAL * * @method void | IDirectInputJoyConfig | LeaveCrit | * * Leave the object critical section. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * *****************************************************************************/ void INLINE CJoyCfg_LeaveCrit(PJC this) { #ifdef XDEBUG AssertF(this->cCrit); AssertF(this->thidCrit == GetCurrentThreadId()); if(InterlockedDecrement(&this->cCrit) == 0) { D(this->thidCrit = 0); } #endif LeaveCriticalSection(&this->crst); } /***************************************************************************** * * @doc INTERNAL * * @mfunc BOOL | CJoyCfg | InCrit | * * Nonzero if we are in the critical section. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * *****************************************************************************/ #ifdef DEBUG BOOL INTERNAL CJoyCfg_InCrit(PJC this) { return this->cCrit && this->thidCrit == GetCurrentThreadId(); } #endif /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | IsAcquired | * * Check that the device is acquired. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @returns * * Returns * if all is well, or if * the device is not acquired. * * *****************************************************************************/ #ifndef XDEBUG \ #define CJoyCfg_IsAcquired_(pdd, z) \ _CJoyCfg_IsAcquired_(pdd) \ #endif HRESULT INLINE CJoyCfg_IsAcquired_(PJC this, LPCSTR s_szProc) { HRESULT hres; if(this->fAcquired) { hres = S_OK; } else { RPF("ERROR %s: Not acquired", s_szProc); hres = DIERR_NOTACQUIRED; } return hres; } #define CJoyCfg_IsAcquired(pdd) \ CJoyCfg_IsAcquired_(pdd, s_szProc) \ /***************************************************************************** * * CJoyCfg::QueryInterface (from IUnknown) * CJoyCfg::AddRef (from IUnknown) * CJoyCfg::Release (from IUnknown) * *****************************************************************************/ /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | QueryInterface | * * Gives a client access to other interfaces on an object. * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * * @returns * * Returns a COM error code. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | AddRef | * * Increments the reference count for the interface. * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | QIHelper | * * We don't have any dynamic interfaces and simply forward * to . * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | AppFinalize | * * We don't have any weak pointers, so we can just * forward to . * * @parm PV | pvObj | * * Object being released from the application's perspective. * *****************************************************************************/ #ifdef DEBUG Default_QueryInterface(CJoyCfg) Default_AddRef(CJoyCfg) Default_Release(CJoyCfg) #else #define CJoyCfg_QueryInterface Common_QueryInterface #define CJoyCfg_AddRef Common_AddRef #define CJoyCfg_Release Common_Release #endif #define CJoyCfg_QIHelper Common_QIHelper #define CJoyCfg_AppFinalize Common_AppFinalize /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | InternalUnacquire | * * Do the real work of an unacquire. * * See for more * information. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @returns * * Returns a COM error code. * See for more * information. * *****************************************************************************/ STDMETHODIMP CJoyCfg_InternalUnacquire(PJC this) { HRESULT hres; EnterProc(CJoyCfg_InternalUnacquire, (_ "p", this)); /* * Must protect with the critical section to prevent somebody from * interfering with us while we're unacquiring. */ CJoyCfg_EnterCrit(this); if(this->fAcquired) { AssertF(this->hkTypesW); RegCloseKey(this->hkTypesW); this->hkTypesW = 0; Invoke_Release(&this->pes); Excl_Unacquire(&IID_IDirectInputJoyConfig, this->hwnd, this->discl); this->fAcquired = 0; hres = S_OK; } else { hres = S_FALSE; } CJoyCfg_LeaveCrit(this); ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func void | CJoyCfg_Finalize | * * Releases the resources of the device. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CJoyCfg_Finalize(PV pvObj) { PJC this = pvObj; #ifdef XDEBUG if(this->cCrit) { RPF("IDirectInputJoyConfig::Release: Another thread is using the object; crash soon!"); } #endif if(this->fAcquired) { CJoyCfg_InternalUnacquire(this); } AssertF(this->pes == 0); if(this->hkTypesW) { RegCloseKey(this->hkTypesW); } if(this->fCritInited) { DeleteCriticalSection(&this->crst); } } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | SetCooperativeLevel | * * Establish the cooperativity level for the instance of * the device. * * The only cooperative levels supported for the * interface are * and . * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm HWND | hwnd | * * The window associated with the interface. This parameter * must be non-NULL and must be a top-level window. * * It is an error to destroy the window while it is still * associated with an interface. * * @parm DWORD | dwFlags | * * Flags which describe the cooperativity level associated * with the device. * * The value must be * . * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : The *

parameter is not a valid pointer. * *****************************************************************************/ STDMETHODIMP CJoyCfg_SetCooperativeLevel(PDJC pdjc, HWND hwnd, DWORD dwFlags) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::SetCooperativityLevel, (_ "pxx", pdjc, hwnd, dwFlags)); if(SUCCEEDED(hres = hresPv(pdjc))) { PJC this = _thisPvNm(pdjc, djc); if(dwFlags != (DISCL_EXCLUSIVE | DISCL_BACKGROUND)) { RPF("%s: Cooperative level must be " "DISCL_EXCLUSIVE | DISCL_BACKGROUND", s_szProc); hres = E_NOTIMPL; } else if(GetWindowPid(hwnd) == GetCurrentProcessId()) { this->hwnd = hwnd; this->discl = dwFlags; hres = S_OK; } else { RPF("ERROR %s: window must belong to current process", s_szProc); hres = E_HANDLE; } } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | Acquire | * * Acquire "joystick configuration mode". Only one application can * be in joystick configuration mode at a time; subsequent * applications will receive the error . * * After entering configuration mode, the application may * make alterations to the global joystick configuration * settings. It is encouraged that the application * re-check the existing settings before installing the new * ones in case another application had changed the settings * in the interim. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Another application is already * in joystick configuration mode. * * : The current user does not have * the necessary permissions to alter the joystick configuration. * * : Another application has changed * the global joystick configuration. The interface needs * to be re-initialized. * *****************************************************************************/ STDMETHODIMP CJoyCfg_Acquire(PDJC pdjc) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::Acquire, (_ "p", pdjc)); if(SUCCEEDED(hres = hresPv(pdjc))) { PJC this = _thisPvNm(pdjc, djc); /* * Must protect with the critical section to prevent somebody from * acquiring or changing the data format while we're acquiring. */ CJoyCfg_EnterCrit(this); if(this->discl == 0) { RPF("%s: Cooperative level not yet set", s_szProc); hres = E_FAIL; goto done; } if(this->fAcquired) { AssertF(this->hkTypesW); hres = S_FALSE; } else if(SUCCEEDED(hres = Excl_Acquire(&IID_IDirectInputJoyConfig, this->hwnd, this->discl))) { AssertF(this->hkTypesW == 0); hres = hresMumbleKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYOEM, DI_KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, &this->hkTypesW); if(SUCCEEDED(hres) ) { this->fAcquired = 1; } else { RegCloseKey(this->hkTypesW); this->hkTypesW = 0; hres = DIERR_INSUFFICIENTPRIVS; } } done:; CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | Unacquire | * * Unacquire "joystick configuration mode". Before unacquiring * configuration mode, the application must perform an * to propagate * the changes in the joystick configuration * to all device drivers and applications. * * Applications which hold interfaces to a joystick which is * materially affected by a change in configuration will * receive the error code until the * device is re-initialized. * * Examples of material changes to configuration include * altering the number of axes or the number of buttons. * In comparison, changes to device calibration * are handled internally by * DirectInput and are transparent to the application. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration mode was * not acquired. * *****************************************************************************/ STDMETHODIMP CJoyCfg_Unacquire(PDJC pdjc) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::Unacquire, (_ "p", pdjc)); if(SUCCEEDED(hres = hresPv(pdjc))) { PJC this = _thisPvNm(pdjc, djc); hres = CJoyCfg_InternalUnacquire(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | SendNotify | * * Notifies device drivers and applications that changes to * the device configuration have been made. An application * which changes device configurations must invoke this * method after the changes have been made (and before * unacquiring). * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration mode was * not acquired. * *****************************************************************************/ STDMETHODIMP CJoyCfg_SendNotify(PDJC pdjc) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::SendNotify, (_ "p", pdjc)); if(SUCCEEDED(hres = hresPv(pdjc))) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(this->fAcquired) { #ifdef WINNT Excl_SetConfigChangedTime( GetTickCount() ); PostMessage(HWND_BROADCAST, g_wmJoyChanged, 0, 0L); #else joyConfigChanged(0); #endif /* * If we don't have a joyConfigChanged, it's probably just * because we're running on NT and don't need it. */ hres = S_OK; } else { hres = DIERR_NOTACQUIRED; } CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | JoyCfg_ConvertCurrentConfigs | * * Converts any OEMType name matching the first input string and * replaces it with the other input string. * * @parm IN LPTSTR | szFindType | * * String to match. * * @parm IN LPTSTR | szReplaceType | * * String to replace any matches instances. * * @returns * * A COM success code unless the current configuration key could not * be opened, or a type that needed to be replaced could not be * overwritten. * *****************************************************************************/ HRESULT JoyCfg_ConvertCurrentConfigs( LPTSTR szFindType, LPTSTR szReplaceType ) { HRESULT hres; LONG lRc; HKEY hkCurrCfg; UINT JoyId; TCHAR szTestType[MAX_JOYSTRING]; TCHAR szTypeName[MAX_JOYSTRING]; DWORD cb; EnterProcI(JoyCfg_ConvertCurrentConfigs, (_ "ss", szFindType, szReplaceType )); hres = JoyReg_OpenConfigKey( (UINT)(-1), KEY_WRITE, NULL, REG_OPTION_NON_VOLATILE, &hkCurrCfg ); if( SUCCEEDED( hres ) ) { for( JoyId = 0; (JoyId < 16) || ( lRc == ERROR_SUCCESS ); JoyId++ ) { wsprintf( szTypeName, REGSTR_VAL_JOYNOEMNAME, JoyId+1 ); cb = sizeof( szTestType ); lRc = RegQueryValueEx( hkCurrCfg, szTypeName, 0, NULL, (PBYTE)szTestType, &cb ); if( lRc == ERROR_SUCCESS ) { if( !lstrcmpi( szTestType, szFindType ) ) { cb = sizeof( szReplaceType) * (1 + lstrlen( szReplaceType )); lRc = RegSetValueEx( hkCurrCfg, szTypeName, 0, REG_SZ, (PBYTE)szReplaceType, cb ); if( lRc != ERROR_SUCCESS ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("RegSetValueEx failed to replace type of %s 0x%08x"), szTypeName, lRc ); /* This is the only error that counts as an error in this loop */ hres = hresReg( lRc ); } } } } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("JoyReg_OpenConfigKey failed code 0x%08x"), hres ); } ExitOleProc(); return hres; } /* JoyCfg_ConvertCurrentConfigs */ #ifdef WINNT /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | JoyCfg_FixHardwareId | * * Fixes the hardwareId for an analog type by assinging a VID/PID to * it and recreating the type using that hardwareId. * * @parm IN HKEY | hkTypesR | * * Handle of key opened to the root of types. * * @parm IN HKEY | hkSrc | * * Handle of key opened to the original type. * * @parm IN PTCHAR | ptszPrefName | * * VID&PID name from the HardwareID if present, NULL otherwise * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The key is valid * = The key should be ignored * * = The key has been fixed but enumeration * must be restarted. * = : Out of memory. * *****************************************************************************/ HRESULT INTERNAL JoyCfg_FixHardwareId( HKEY hkTypesR, HKEY hkSrc, PTCHAR szSrcType , PTCHAR ptszPrefName) { HRESULT hres; HKEY hkNew; BYTE PIDlow; DWORD ClassLen; PTCHAR szClassName; TCHAR szDestType[sizeof( ANALOG_ID_ROOT ) + 2]; //Two digits will be appended TCHAR szHardwareId[MAX_JOYSTRING]; EnterProcI(JoyCfg_FixHardwareId, (_ "xxss", hkTypesR, hkSrc, szSrcType, ptszPrefName)); hres = hresReg( RegQueryInfoKey( hkSrc, // handle to key to query NULL, // Class &ClassLen, // ClassLen NULL, // Reserved NULL, NULL, NULL, // NumSubKeys, MaxSubKeyLen, MaxClassLen NULL, NULL, NULL, // NumValues, MaxValueNameLen, MaxValueLen NULL, NULL ) ); // Security descriptor, last write if( SUCCEEDED( hres ) ) { ClassLen++; hres = AllocCbPpv( ClassLen * sizeof(szClassName[0]), &szClassName ); if( SUCCEEDED( hres ) ) { hres = hresReg( RegQueryInfoKey( hkSrc, // handle to key to query szClassName, // Class &ClassLen, // ClassLen NULL, // Reserved NULL, NULL, NULL, // NumSubKeys, MaxSubKeyLen, MaxClassLen NULL, NULL, NULL, // NumValues, MaxValueNameLen, MaxValueLen NULL, NULL ) ); // Security descriptor, last write if( FAILED( hres ) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("RegQueryInfoKey on type %s for class name failed 0x%04x"), szSrcType, LOWORD(hres) ); } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("Failed to allocate %d bytes for class name of type %s, error 0x%04x"), ClassLen, szSrcType, LOWORD(hres) ); } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("RegQueryInfoKey on type %s for class name length failed 0x%04x"), szSrcType, LOWORD(hres) ); /* Make sure not to free an uninitialized pointer */ szClassName = NULL; } if( SUCCEEDED( hres ) ) { for( PIDlow = JOY_HW_PREDEFMAX; PIDlow; PIDlow++ ) { if (ptszPrefName) { lstrcpy( szDestType, ptszPrefName); #ifdef UNICODE CharUpperW(szDestType); #else CharUpper(szDestType); #endif } else { wsprintf( szDestType, TEXT("%s%02X"), ANALOG_ID_ROOT, PIDlow ); } hres = hresRegCopyKey( hkTypesR, szSrcType, szClassName, hkTypesR, szDestType, &hkNew ); if( hres == S_OK ) { hres = hresRegCopyBranch( hkSrc, hkNew ); if( SUCCEEDED( hres ) ) { if (!ptszPrefName) { #ifdef MULTI_SZ_HARDWARE_IDS /* * Make up the hardwareId using the assigned PID with a generic hardwareId appended */ int CharIdx = 0; while( TRUE ) { CharIdx += wsprintf( &szHardwareId[CharIdx], TEXT("%s%s%02X"), TEXT("GamePort\\"), ANALOG_ID_ROOT, PIDlow ); CharIdx++; /* Leave NULL terminator in place */ if( PIDlow ) { PIDlow = 0; /* Trash this value to make the generic PID on second iteration */ } else { break; } } szHardwareId[CharIdx++] = TEXT('\0'); /* MULTI_SZ */ hres = hresReg( RegSetValueEx( hkNew, REGSTR_VAL_JOYOEMHARDWAREID, 0, REG_MULTI_SZ, (PBYTE)szHardwareId, (DWORD)( sizeof(szHardwareId[0]) * CharIdx ) ) ); if( FAILED( hres ) ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("JoyCfg_FixHardwareId: failed to write hardware ID %s"), szHardwareId ); } #else /* * Make up the hardwareId using the assigned PID */ int CharIdx = 0; CharIdx = wsprintf( szHardwareId, TEXT("%s%s%02X"), TEXT("GamePort\\"), ANALOG_ID_ROOT, PIDlow ); CharIdx++; /* Leave NULL terminator in place */ hres = hresReg( RegSetValueEx( hkNew, REGSTR_VAL_JOYOEMHARDWAREID, 0, REG_SZ, (PBYTE)szHardwareId, (DWORD)( sizeof(szHardwareId[0]) * CharIdx ) ) ); if( FAILED( hres ) ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("JoyCfg_FixHardwareId: failed to write hardware ID %s"), szHardwareId ); } #endif } } RegCloseKey( hkNew ); if( SUCCEEDED( hres ) ) { hres = JoyCfg_ConvertCurrentConfigs( szSrcType, szDestType ); } DIWinnt_RegDeleteKey( hkTypesR, ( SUCCEEDED( hres ) ) ? szSrcType : szDestType ); break; } else if( SUCCEEDED( hres ) ) { /* * The key already existed so keep looking */ RegCloseKey( hkNew ); } else { /* * RegCopyKey should have already posted errors */ break; } } if( !PIDlow ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("JoyCfg_FixHardwareId: no free analog keys for type %s"), szSrcType ); hres = DIERR_NOTFOUND; } } if( szClassName ) { FreePpv( &szClassName ); } ExitOleProc(); return( hres ); } /* JoyCfg_FixHardwareId */ #endif /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | JoyCfg_CheckTypeKey | * * Checks the contents of a type key for validity on the current OS * and if not valid, try to make it so. * * Only custom analog types can be fixed and this only needs to be * done on a WDM enabled OS as non-WDM requirements are a sub-set of * the WDM ones. * * @parm IN HKEY | hkTypesR | * * Handle of key opened to the root of types. * * @parm IN LPTSTR | szType | * * Receives a pointer either an ansi or UNICODE key name to test. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The key is valid * = The key should be ignored * * = The key has been fixed but enumeration * must be restarted. * = : Out of memory. * *****************************************************************************/ HRESULT INTERNAL JoyCfg_CheckTypeKey( HKEY hkTypesR, LPTSTR szType ) { HRESULT hres; HKEY hk; LONG lRc; DWORD cb; TCHAR tszCallout[MAX_JOYSTRING]; TCHAR tszHardwareId[MAX_JOYSTRING]; #ifdef WINNT JOYREGHWSETTINGS hws; TCHAR* ptszLastSlash=NULL; #endif EnterProcI(JoyCfg_CheckTypeKey, (_ "xs",hkTypesR, szType)); /* * Open read only just in case we don't have better permission to any * of the type sub-keys. */ lRc = RegOpenKeyEx( hkTypesR, szType, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hk ); if(lRc == ERROR_SUCCESS ) { /* * Gather the needed results using standard registry functions so * that the exact return code is known. */ lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, NULL ); #ifdef WINNT if(lRc == ERROR_SUCCESS ) { cb = cbX(hws); lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMDATA, NULL, NULL, (PBYTE)&hws, &cb ); if( ( lRc == ERROR_SUCCESS ) && ( hws.dwFlags & JOY_HWS_AUTOLOAD ) ) { /* * WARNING goto * If we have a name and JOY_HWS_AUTOLOAD is set, that's all we need */ RegCloseKey( hk ); hres = S_OK; goto fast_out; } if( lRc == ERROR_FILE_NOT_FOUND ) { hws.dwFlags = 0; lRc = ERROR_SUCCESS; } } #endif if(lRc == ERROR_SUCCESS ) { cb = cbX(tszCallout); lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMCALLOUT, NULL, NULL, (PBYTE)tszCallout, &cb ); if( lRc == ERROR_FILE_NOT_FOUND ) { tszCallout[0] = TEXT('\0'); lRc = ERROR_SUCCESS; } } if(lRc == ERROR_SUCCESS ) { cb = cbX(tszHardwareId); lRc = RegQueryValueEx(hk, REGSTR_VAL_JOYOEMHARDWAREID, NULL, NULL, (PBYTE)tszHardwareId, &cb ); if( lRc == ERROR_FILE_NOT_FOUND ) { tszHardwareId[0] = TEXT('\0'); lRc = ERROR_SUCCESS; } #ifdef WINNT else { TCHAR* ptsz; for (ptsz = tszHardwareId;*ptsz!='\0';++ptsz) { if (*ptsz == '\\') { ptszLastSlash = ptsz; } } if (ptszLastSlash) { ptszLastSlash++; //next char is the one we want } } #endif } if(lRc != ERROR_SUCCESS ) { RegCloseKey( hk ); } } if(lRc == ERROR_SUCCESS ) { #ifdef WINNT SHORT DontCare; #endif WCHAR wszType[18]; TToU( wszType, cA(wszType),szType ); /* * Work out the status of this type based on the OS and the registry data * * Note on 98 we allow WDM types to be enumerated but do not convert * analog types to WDM. We may want to convert analog types if we get * WDM gameport drivers appear for gameports that are incompatible with * msanalog. */ #define HAS_VIDPID ( ParseVIDPID( &DontCare, &DontCare, wszType ) ) #define HAS_HARDWARE_ID ( tszHardwareId[0] != TEXT('\0') ) #define HAS_OEMCALLOUT ( tszCallout[0] != TEXT('\0') ) #define IS_ANALOG \ ( tszHardwareId[ sizeof( ANALOG_ID_ROOT ) - 1 ] = TEXT('\0'), \ ( !lstrcmpi( tszHardwareId, ANALOG_ID_ROOT ) ) ) #define IS_WIN98 (HidD_GetHidGuid) #ifdef WINNT if (HAS_HARDWARE_ID) { //Need to check if there is a VID and PID in the HW ID if (ParseVIDPID( &DontCare, &DontCare, ptszLastSlash )) { //If the type VIDPID doesn't match the HardwareId VIDPID //we need to fix it if (!lstrcmpi(ptszLastSlash, wszType)) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("OEMHW %s(%s) and/or Type %s have matching VID/PID"), tszHardwareId,ptszLastSlash,wszType); hres = S_OK; } else { hres = OLE_E_ENUM_NOMORE; SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("OEMHW %s(%s) and/or Type %s have non-matching VID/PID. Fix Needed."), tszHardwareId,ptszLastSlash,wszType); } } else { hres = S_OK; //no VIDPID in the type SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("OEMHW %s(%s) and/or Type %s have no VID/PID"), tszHardwareId,ptszLastSlash,wszType); } } else { if (HAS_VIDPID) { hres = DIERR_MOREDATA; } else { if (HAS_OEMCALLOUT) { hres = S_FALSE; } else { hres = OLE_E_ENUM_NOMORE; } } } #else hres = (IS_WIN98) ? S_OK /* Anything goes on 98 */ : (HAS_OEMCALLOUT) ? S_OK /* Win9x device, OK */ : (HAS_HARDWARE_ID) ? (IS_ANALOG) ? S_OK /* Analog type, OK */ : S_FALSE /* WDM device, ignore */ : S_OK; /* Analog type, OK */ #endif switch( hres ) { #ifdef WINNT case DIERR_MOREDATA: /* * The device is not marked as autoload but has a VID/PID type * name. If the OEMCallout is blank or "joyhid.vxd" we'll assume * the type should be autoload and correct it. * If there's any other value, we could assume either that we * have a bogus Win9x driver type key and hide it or that the * device is autoload. * Safest route, now that our expose code is smart enough to not * expose a device without a hardware ID, is to enumerate it as * non-autoload as Win2k did. It won't work if you try to add * it but at least the type will be enumerated if the device * does show up from PnP (so nobody will get confused by a * device without a type). * * ISSUE-2001/01/04-MarcAnd should use common joyhid string * Not sure if the compiler/linker will resolve the various * instances of L"joyhid.vxd" to a single string. Should * reference the same one to be certain. */ if( !HAS_OEMCALLOUT || ( !lstrcmpi( tszCallout, L"joyhid.vxd" ) ) ) { HKEY hkSet; /* * Need to open a new handle for the key as the one we have * is read-only. */ lRc = RegOpenKeyEx( hkTypesR, szType, 0, KEY_SET_VALUE, &hkSet ); if( lRc == ERROR_SUCCESS ) { hws.dwFlags |= JOY_HWS_AUTOLOAD; cb = cbX(hws); lRc = RegSetValueEx( hkSet, REGSTR_VAL_JOYOEMDATA, 0, REG_BINARY, (PBYTE)&hws, (DWORD)( cbX(hws) ) ); if( lRc == ERROR_SUCCESS ) { SquirtSqflPtszV(sqfl | sqflTrace, TEXT("FIXED Type %s to have JOY_HWS_AUTOLOAD"), szType ); } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Failed to set JOY_HWS_AUTOLOAD on Type %s (rc=%d,le=%d)"), szType, lRc, GetLastError() ); } RegCloseKey( hkSet ); } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Failed to open Type %s to fix JOY_HWS_AUTOLOAD(rc=%d,le=%d)"), szType, lRc, GetLastError() ); } } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Type %s with OEMCallout<%s> has no HardwareId so cannot be added"), szType, tszCallout ); } /* * Whether or not we fixed this, we want to enumerate the key. */ hres = S_OK; break; case OLE_E_ENUM_NOMORE: { HRESULT hres0; hres0 = JoyCfg_FixHardwareId( hkTypesR, hk, szType , ptszLastSlash); if( FAILED( hres0 ) ) { /* * Failed to fix type it must be ignored to avoid an infinite loop */ SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Ignoring type %s as fix failed"), szType ); hres = S_FALSE; } else { SquirtSqflPtszV(sqfl | sqflTrace, TEXT("FIXED Type %s with HardwareId<%s> and OEMCallout<%s>"), szType, tszHardwareId, tszCallout ); } } break; #endif case S_FALSE: SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Ignoring type %s with HardwareId<%s> and OEMCallout<%s>"), szType, tszHardwareId, tszCallout ); break; case S_OK: SquirtSqflPtszV(sqfl | sqflTrace, TEXT("Enumerating type %s with HardwareId<%s> and OEMCallout<%s>"), szType, tszHardwareId, tszCallout ); break; } RegCloseKey( hk ); #undef HAS_VIDPID #undef HAS_HARDWARE_ID #undef HAS_OEMCALLOUT #undef IS_ANALOG #undef IS_WIN98 } else { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Ignoring type %s due to registry error 0x%08x"), szType, lRc ); /* * It seems a bit bogus, to return success for an error but this * makes sure the key is ignored and enumeration will proceed. */ hres = S_FALSE; } #ifdef WINNT fast_out:; #endif ExitOleProc(); return( hres ); } /* JoyCfg_CheckTypeKey */ /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CDIJoyCfg | SnapTypes | * * Snapshot the list of subkeys for OEM types. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm OUT LPWSTR * | ppwszz | * * Receives a pointer to a UNICODEZZ * list of type names. Note that the returned list * is pre-populated with the predefined types, too. * * We need to snapshot the names up front because * the caller might create or delete OEM types during the * enumeration. * * As we enumerate we check each key for validity and repair any * analog custom configurations that we can. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : Out of memory. * *****************************************************************************/ HRESULT INTERNAL CJoyCfg_SnapTypes(PJC this, LPWSTR *ppwszz) { HRESULT hres; LONG lRc; HKEY hkTypesR; DWORD chkSub; BOOL fRetry; EnterProcI(CJoyCfg_SnapTypes, (_ "p", this)); RD(*ppwszz = 0); /* * If an analog configuration needs to be fixed, the enumeration is * restarted because adding/removing keys may mess with the key indicies. * Since registry keys can go stale, start from scratch. */ do { fRetry=FALSE; /* * Note that it is not safe to cache the registry key in * the object. If somebody deletes the registry key, our * cached handle goes stale and becomes useless. */ lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_JOYOEM, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkTypesR); /* * Note also that if the registry key is not available, * we still want to return the predefined types. */ if(lRc == ERROR_SUCCESS) { lRc = RegQueryInfoKey(hkTypesR, 0, 0, 0, &chkSub, 0, 0, 0, 0, 0, 0, 0); if(lRc == ERROR_SUCCESS ) { } else { chkSub = 0; } } else { hkTypesR = 0; chkSub = 0; } /* * Each predefined name is of the form #n\0, * which is 3 characters. */ hres = AllocCbPpv(cbCwch( chkSub * MAX_JOYSTRING + (JOY_HW_PREDEFMAX - JOY_HW_PREDEFMIN) * 3 + 1), ppwszz); // Not really a bug,we never get to this point with a NULL ptr, // but lets keep prefix happy Manbugs: 29340 if(SUCCEEDED(hres) && *ppwszz != NULL ){ DWORD dw; LPWSTR pwsz; /* * First add the predef keys. */ for(dw = JOY_HW_PREDEFMIN, pwsz = *ppwszz; dw < JOY_HW_PREDEFMAX; dw++) { *pwsz++ = L'#'; *pwsz++ = CJoyCfg_CharFromType(dw); *pwsz++ = L'\0'; } /* * Now add the named keys. */ for(dw = 0; dw < chkSub; dw++) { #ifdef UNICODE lRc = RegEnumKey(hkTypesR, dw, pwsz, MAX_JOYSTRING); #else CHAR sz[MAX_JOYSTRING]; lRc = RegEnumKey(hkTypesR, dw, sz, MAX_JOYSTRING); #endif if(lRc == ERROR_SUCCESS ) { #ifdef UNICODE hres = JoyCfg_CheckTypeKey( hkTypesR, pwsz ); #else hres = JoyCfg_CheckTypeKey( hkTypesR, sz ); #endif if( FAILED( hres ) ) { /* * Had to fix type so restart */ FreePpv( ppwszz ); break; } if( hres != S_OK ) { /* * Ignore this type */ continue; } #ifdef UNICODE pwsz += lstrlenW(pwsz) + 1; #else pwsz += AToU(pwsz, MAX_JOYSTRING, sz); #endif } else { } } if( SUCCEEDED( hres ) ) { *pwsz = L'\0'; /* Make it ZZ */ hres = S_OK; } else { fRetry = TRUE; } } if(hkTypesR) { RegCloseKey(hkTypesR); } } while( fRetry ); ExitOleProcPpv(ppwszz); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | EnumTypes | * * Enumerate the joystick types currently supported by * DirectInput. A "joystick type" describes how DirectInput * should communicate with a joystick device. It includes * information such as the presence and * locations of each of the axes and the number of buttons * supported by the device. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm LPDIJOYTYPECALLBACK | lpCallback | * * Points to an application-defined callback function. * For more information, see the description of the * callback function. * * @parm IN LPVOID | pvRef | * * Specifies a 32-bit application-defined * value to be passed to the callback function. This value * may be any 32-bit value; it is prototyped as an * for convenience. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * Note that if the callback stops the enumeration prematurely, * the enumeration is considered to have succeeded. * * = : The * callback procedure returned an invalid status code. * * @cb BOOL CALLBACK | DIEnumJoyTypeProc | * * An application-defined callback function that receives * DirectInput joystick types as a result of a call to the * method. * * @parm IN LPCWSTR | pwszTypeName | * * The name of the joystick type. A buffer of * characters will be sufficient to hold the type name. * The type name should never be shown to the end user; instead, * the "display name" should be shown. Use * to obtain the * display name of a joystick type. * * Type names that begin with a sharp character ("#") * represent predefined types which cannot be modified * or deleted. * * @parm IN OUT LPVOID | pvRef | * Specifies the application-defined value given in the * function. * * @returns * * Returns to continue the enumeration * or to stop the enumeration. * * @devnote * * EnumTypes must snapshot because people will try to get/set/delete * during the enumeration. * * EnumTypes enumerates the predefined types as "#digit". * *****************************************************************************/ STDMETHODIMP CJoyCfg_EnumTypes(PDJC pdjc, LPDIJOYTYPECALLBACK ptc, LPVOID pvRef) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::EnumTypes, (_ "ppx", pdjc, ptc, pvRef)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidPfn(ptc, 1))) { PJC this = _thisPvNm(pdjc, djc); LPWSTR pwszKeys; hres = CJoyCfg_SnapTypes(this, &pwszKeys); if(SUCCEEDED(hres)) { LPWSTR pwsz; /* * Surprise! Win95 implements lstrlenW. */ for(pwsz = pwszKeys; *pwsz; pwsz += lstrlenW(pwsz) + 1) { BOOL fRc; /* * WARNING! "goto" here! Make sure that nothing * is held while we call the callback. */ fRc = Callback(ptc, pwsz, pvRef); switch(fRc) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("%s: Invalid return value from callback", s_szProc); ValidationException(); break; } } FreePpv(&pwszKeys); hres = DIPort_SnapTypes(&pwszKeys); if(SUCCEEDED(hres)) { LPWSTR pwsz; /* * Surprise! Win95 implements lstrlenW. */ for(pwsz = pwszKeys; *pwsz; pwsz += lstrlenW(pwsz) + 1) { BOOL fRc; /* * WARNING! "goto" here! Make sure that nothing * is held while we call the callback. */ fRc = Callback(ptc, pwsz, pvRef); switch(fRc) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("%s: Invalid return value from callback", s_szProc); ValidationException(); break; } } } enumdoneok:; FreePpv(&pwszKeys); hres = S_OK; } hres = S_OK; } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | GetTypeInfo | * * Obtain information about a joystick type. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm LPCWSTR | pwszTypeName | * * Points to the name of the type, previously obtained * from a call to . * * @parm IN OUT LPDIJOYTYPEINFO | pjti | * * Receives information about the joystick type. * The caller "must" initialize the * field before calling this method. * * @parm DWORD | dwFlags | * * Zero or more flags * which specify which parts of the structure pointed * to by

are to be filled in. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * * : The joystick type was not found. * *****************************************************************************/ STDMETHODIMP CJoyCfg_GetTypeInfo(PDJC pdjc, LPCWSTR pwszType, LPDIJOYTYPEINFO pjti, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::GetTypeInfo, (_ "pWpx", pdjc, pwszType, pjti, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) && #if DIRECTINPUT_VERSION >= 0x05B2 SUCCEEDED(hres = hresFullValidWritePxCb2(pjti, DIJOYTYPEINFO_DX6, DIJOYTYPEINFO_DX5, 2)) && #else SUCCEEDED(hres = hresFullValidWritePxCb(pjti, DIJOYTYPEINFO, 2)) && #endif SUCCEEDED( (pjti->dwSize == cbX(DIJOYTYPEINFO_DX6) ? ( hres = hresFullValidFl(fl, DITC_GETVALID, 3) ) : ( hres = hresFullValidFl(fl, DITC_GETVALID_DX5, 3)) ) ) ) { PJC this = _thisPvNm(pdjc, djc); GUID guid; BOOL fParseGuid; #ifndef UNICODE TCHAR tszType[MAX_PATH/4]; UToT( tszType, cA(tszType), pwszType ); fParseGuid = ParseGUID(&guid, tszType); #else fParseGuid = ParseGUID(&guid, pwszType); #endif if(pwszType[0] == TEXT('#')) { hres = JoyReg_GetPredefTypeInfo(pwszType, pjti, fl); } else if( fParseGuid ) { hres = DIBusDevice_GetTypeInfo(&guid, pjti, fl); }else { hres = JoyReg_GetTypeInfo(pwszType, pjti, fl); } } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresFullValidStructStr | * * Validate a string field in a struct. * * @parm IN LPCWSTR | pwsz | * * String to be validated. * * @parm UINT | cwch | * * Maximum string length. * * @parm LPCSTR | pszName | * * Field name. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ #ifndef XDEBUG \ #define hresFullValidStructStr_(pwsz, cwch, pszName, z, i) \ _hresFullValidStructStr_(pwsz, cwch) \ #endif #define hresFullValidStructStr(Struct, f, iarg) \ hresFullValidStructStr_(Struct->f, cA(Struct->f), #f, s_szProc,iarg)\ HRESULT INLINE hresFullValidStructStr_(LPCWSTR pwsz, UINT cwch, LPCSTR pszName, LPCSTR s_szProc, int iarg) { HRESULT hres; if(SUCCEEDED(hres = hresFullValidReadStrW(pwsz, cwch, iarg))) { } else { #ifdef XDEBUG RPF("%s: Invalid value for %s", s_szProc, pszName); #endif } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | SetTypeInfo | * * Creates a new joystick type * or redefine information about an existing joystick type. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm LPCWSTR | pwszTypeName | * * Points to the name of the type. The name of the type may * not exceed MAX_JOYSTRING characters, including the terminating * null character. * * If the type name does not already exist, then it is created. * * You cannot change the type information for a predefined type. * * The name may not begin with * a "#" character. Types beginning with "#" are reserved * by DirectInput. * * @parm IN LPDIJOYTYPEINFO | pjti | * * Contains information about the joystick type. * * @parm DWORD | dwFlags | * * Zero or more flags * which specify which parts of the structure pointed * to by

contain values which are to be set. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can alter joystick configuration settings. * * = : One or more * parameters was invalid. * * : Attempted to change a predefined type. * *****************************************************************************/ STDMETHODIMP CJoyCfg_SetTypeInfo(PDJC pdjc, LPCWSTR pwszType, LPCDIJOYTYPEINFO pjti, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::SetTypeInfo, (_ "pWpx", pdjc, pwszType, pjti, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) && #if DIRECTINPUT_VERSION >= 0x05B2 SUCCEEDED(hres = hresFullValidReadPxCb2(pjti, DIJOYTYPEINFO_DX6, DIJOYTYPEINFO_DX5, 2)) && (!fWinnt || SUCCEEDED(hres = hresFullValidFl(pjti->dwFlags1, JOYTYPE_FLAGS1_SETVALID, 3) ) ) && #else SUCCEEDED(hres = hresFullValidWritePxCb(pjti, DIJOYTYPEINFO, 2)) && #endif SUCCEEDED( (pjti->dwSize == cbX(DIJOYTYPEINFO_DX6) ? ( hres = hresFullValidFl(fl, DITC_SETVALID, 3) ) : ( hres = hresFullValidFl(fl, DITC_SETVALID_DX5, 3)) ) ) && fLimpFF(fl & DITC_HARDWAREID, SUCCEEDED(hres = hresFullValidStructStr(pjti, wszHardwareId, 2))) && fLimpFF(fl & DITC_DISPLAYNAME, SUCCEEDED(hres = hresFullValidStructStr(pjti, wszDisplayName, 2))) && fLimpFF(fl & DITC_CALLOUT, SUCCEEDED(hres = hresFullValidStructStr(pjti, wszCallout, 2))) ) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this))) { switch(pwszType[0]) { case L'\0': RPF("%s: Invalid pwszType (null)", s_szProc); hres = E_INVALIDARG; break; case L'#': RPF("%s: Invalid pwszType (predefined)", s_szProc); hres = DIERR_READONLY; break; default: hres = JoyReg_SetTypeInfo(this->hkTypesW, pwszType, pjti, fl); break; } } CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | DeleteType | * * Removes information about a joystick type. * * Use this method with caution; it is the caller's responsibility * to ensure that no joystick refers to the deleted type. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm LPCWSTR | pwszTypeName | * * Points to the name of the type. The name of the type may * not exceed characters, including the terminating * null character. * * The name may not begin with * a "#" character. Types beginning with "#" are reserved * by DirectInput. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can alter joystick configuration settings. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CJoyCfg_DeleteType(PDJC pdjc, LPCWSTR pwszType) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::DeleteType, (_ "pW", pdjc, pwszType)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidReadStrW( pwszType, MAX_JOYSTRING, 1))) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this))) { LONG lRc; switch(pwszType[0]) { case L'\0': RPF("%s: Invalid pwszType (null)", s_szProc); hres = E_INVALIDARG; break; case L'#': RPF("%s: Invalid pwszType (predefined)", s_szProc); hres = DIERR_READONLY; break; default: if( fWinnt ) { #ifdef UNICODE lRc = DIWinnt_RegDeleteKey(this->hkTypesW, (LPTSTR)pwszType); #else CHAR sz[MAX_PATH]; UToA( sz, cA(sz), pwszType ); lRc = DIWinnt_RegDeleteKey(this->hkTypesW, (LPTSTR)sz); #endif } else { #ifdef UNICODE lRc = RegDeleteKey(this->hkTypesW, (LPTSTR)pwszType); #else CHAR sz[MAX_PATH]; UToA( sz, cA(sz), pwszType ); lRc = RegDeleteKey(this->hkTypesW, (LPTSTR)sz); #endif } /* #ifdef WINNT lRc = DIWinnt_RegDeleteKey(this->hkTypesW, pwszType); #else lRc = RegDeleteKeyW(this->hkTypesW, pwszType); #endif */ if(lRc == ERROR_SUCCESS) { hres = S_OK; } else { if(lRc == ERROR_KEY_DELETED || lRc == ERROR_BADKEY) { lRc = ERROR_FILE_NOT_FOUND; } hres = hresLe(lRc); } break; } } CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | GetConfig | * * Obtain information about a joystick's configuration. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm UINT | uiJoy | * * Joystick identification number. This is a nonnegative * integer. To enumerate joysticks, begin with joystick * zero and increment the joystick number by one until the * function returns . * * Yes, it's different from all other DirectX enumerations, * but I felt rather strange enumerating integers. * * @parm IN OUT LPDIJOYCONFIG | pjc | * * Receives information about the joystick configuration. * The caller "must" initialize the * field before calling this method. * * @parm DWORD | dwFlags | * * Zero or more flags * which specify which parts of the structure pointed * to by

are to be filled in. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : The specified joystick has not yet been * configured. * * = : One or more * parameters was invalid. * * : No more joysticks. * *****************************************************************************/ STDMETHODIMP CJoyCfg_GetConfig(PDJC pdjc, UINT uiJoy, LPDIJOYCONFIG pjc, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::GetConfig, (_ "pupx", pdjc, uiJoy, pjc, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && #if DIRECTINPUT_VERSION >= 0x05B2 SUCCEEDED(hres = hresFullValidWritePxCb2(pjc, DIJOYCONFIG_DX6, DIJOYCONFIG_DX5, 2)) && #else SUCCEEDED(hres = hresFullValidWritePxCb(pjc, DIJOYCONFIG, 2)) && #endif SUCCEEDED( (pjc->dwSize == cbX(DIJOYCONFIG) ? (hres = hresFullValidFl(fl, DIJC_GETVALID, 3) ) : (hres = hresFullValidFl(fl, DIJC_GETVALID_DX5, 3)))) ) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); /* * Note that we always get the DIJC_REGHWCONFIGTYPE because * we need to check if the joystick type is "none". */ hres = JoyReg_GetConfig(uiJoy, NULL, pjc, fl | DIJC_REGHWCONFIGTYPE); if(SUCCEEDED(hres)) { #ifndef WINNT static WCHAR s_wszMSGAME[] = L"MSGAME.VXD"; if(memcmp(pjc->wszCallout, s_wszMSGAME, cbX(s_wszMSGAME)) == 0) { ; // do nothing } else #endif if(fInOrder(JOY_HW_PREDEFMIN, pjc->hwc.dwType, JOY_HW_PREDEFMAX)) { pjc->wszType[0] = TEXT('#'); pjc->wszType[1] = CJoyCfg_CharFromType(pjc->hwc.dwType); pjc->wszType[2] = TEXT('\0'); } if(pjc->hwc.dwType == JOY_HW_NONE) { hres = S_FALSE; } else { hres = S_OK; } /* * In DEBUG, re-scramble the hwc and type if the caller * didn't ask for it. */ if(!(fl & DIJC_REGHWCONFIGTYPE)) { ScrambleBuf(&pjc->hwc, cbX(pjc->hwc)); ScrambleBuf(&pjc->wszType, cbX(pjc->wszType)); } } CJoyCfg_LeaveCrit(this); } ExitBenignOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyCfg | UpdateGlobalGain | * * Create the device callback so we can talk to its driver and * tell it to change the gain value. * * This function must be called under the object critical section. * * @cwrap PJC | this * * @parm DWORD | idJoy | * * The joystick identifier. * * @parm DWORD | dwCplGain | * * New global gain. * * @returns * * Returns a COM error code. * *****************************************************************************/ STDMETHODIMP CJoyCfg_UpdateGlobalGain(PJC this, DWORD idJoy, DWORD dwCplGain) { HRESULT hres; EnterProcI(CJoyCfg_UpdateGlobalGain, (_ "puu", this, idJoy, dwCplGain)); AssertF(CJoyCfg_InCrit(this)); /* * Create the deviceeffect shepherd if we don't already have it. */ if(this->pes && idJoy == this->idJoyCache) { hres = S_OK; } else if(idJoy < cA(rgGUID_Joystick)) { PCGUID rguid; #ifdef DEBUG CREATEDCB CreateDcb; #endif IDirectInputDeviceCallback *pdcb; /* * Assume the creation will work. */ this->idJoyCache = idJoy; /* * Out with the old... */ Invoke_Release(&this->pes); /* * And in with the new... */ rguid = &rgGUID_Joystick[idJoy]; #ifdef DEBUG hres = hresFindInstanceGUID(rguid, &CreateDcb, 1); AssertF(SUCCEEDED(hres)); AssertF(CreateDcb == CJoy_New); #endif if(SUCCEEDED(hres = CJoy_New(0, rguid, &IID_IDirectInputDeviceCallback, (PPV)&pdcb))) { hres = pdcb->lpVtbl->CreateEffect(pdcb, &this->pes); Invoke_Release(&pdcb); } } else { hres = DIERR_DEVICENOTREG; } /* * If we have an effect shepherd, then tell it what the new * global gain is. */ if(SUCCEEDED(hres)) { AssertF(this->pes && idJoy == this->idJoyCache); hres = this->pes->lpVtbl->SetGlobalGain(this->pes, dwCplGain); } ExitOleProc(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | SetConfig | * * Create or redefine configuration information about a joystick. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm UINT | idJoy | * * Zero-based joystick identification number. * * @parm IN LPDIJOYCONFIG | pcfg | * * Contains information about the joystick. * * @parm DWORD | dwFlags | * * Zero or more flags * which specify which parts of the structure pointed * to by

contain information to be set. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can alter joystick configuration settings. * * = : One or more * parameters was invalid. * * @devnote * * This one is tricky. If the type begins with a sharp, then * it's an internal type. And if it is null, then it's a * custom type. * * *****************************************************************************/ STDMETHODIMP CJoyCfg_SetConfig(PDJC pdjc, UINT idJoy, LPCDIJOYCONFIG pcfg, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::SetConfig, (_ "pupx", pdjc, idJoy, pcfg, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && #if DIRECTINPUT_VERSION >= 0x05B2 SUCCEEDED(hres = hresFullValidReadPxCb2(pcfg, DIJOYCONFIG_DX6, DIJOYCONFIG_DX5, 2)) && #else SUCCEEDED(hres = hresFullValidReadPxCb(pcfg, DIJOYCONFIG, 2)) && #endif SUCCEEDED( (pcfg->dwSize == cbX(DIJOYCONFIG) ? ( hres = hresFullValidFl(fl, DIJC_SETVALID, 3) ) : ( hres = hresFullValidFl(fl, DIJC_SETVALID_DX5,3)) )) && fLimpFF(fl & DIJC_REGHWCONFIGTYPE, SUCCEEDED(hres = hresFullValidStructStr(pcfg, wszType, 2))) && fLimpFF(fl & DIJC_CALLOUT, SUCCEEDED(hres = hresFullValidStructStr(pcfg, wszCallout, 2))) && fLimpFF(fl & DIJC_WDMGAMEPORT, SUCCEEDED(hres = hresFullValidGuid(&pcfg->guidGameport, 2))) ) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this))) { JOYREGHWCONFIG jwc; if(fl & DIJC_REGHWCONFIGTYPE) { LPDWORD lpStart, lp; jwc = pcfg->hwc; /* * Need to check whether the whole jwc is zero. * If all are zero, we won't set it to JOY_HW_CUSTOM type. * See manbug: 39542. */ for( lpStart=(LPDWORD)&jwc, lp=(LPDWORD)&jwc.dwReserved; lp >= lpStart; lp-- ) { if( *lp ) { break; } } if( lp < lpStart ) { goto _CONTINUE_SET; } jwc.dwUsageSettings &= ~JOY_US_ISOEM; if(pcfg->wszType[0] == TEXT('\0')) { jwc.dwType = JOY_HW_CUSTOM; } else if(pcfg->wszType[0] == TEXT('#')) { jwc.dwType = CJoyCfg_TypeFromChar(pcfg->wszType[1]); if(fInOrder(JOY_HW_PREDEFMIN, jwc.dwType, JOY_HW_PREDEFMAX) && pcfg->wszType[2] == TEXT('\0')) { /* * If we want to use WDM for predefined devices, * then take away the comments. * fl |= DIJC_WDMGAMEPORT; */ } else { RPF("%s: Invalid predefined type \"%ls\"", s_szProc, pcfg->wszType); hres = E_INVALIDARG; goto done; } } else { HKEY hk; /* * The precise value of jwc.dwType is not relevant. * The Windows 95 joystick control panel sets the * value to JOY_HW_PREDEFMAX + id, so we will too. */ jwc.dwUsageSettings |= JOY_US_ISOEM; jwc.dwType = JOY_HW_PREDEFMAX + idJoy; if( !(fl & DIJC_WDMGAMEPORT) ) { hres = JoyReg_OpenTypeKey(pcfg->wszType, MAXIMUM_ALLOWED, REG_OPTION_NON_VOLATILE, &hk); if( SUCCEEDED( hres ) ) { hres = JoyReg_IsWdmGameport( hk ); if( SUCCEEDED(hres) ) { fl |= DIJC_WDMGAMEPORT; } } } } } _CONTINUE_SET: if( fWinnt ) { fl |= DIJC_WDMGAMEPORT; } if( (fl & DIJC_WDMGAMEPORT) && (cbX(*pcfg) >= cbX(DIJOYCONFIG_DX6)) ) { if( fWinnt || // in Winnt !(pcfg->hwc.hws.dwFlags & JOY_HWS_ISANALOGPORTDRIVER) || //USB joystick fVjoydDeviceNotExist ) // WDM gameport joystick and no VJOYD is used. { if( IsEqualGUID(&pcfg->guidInstance, &GUID_NULL) ) { hres = DIWdm_SetConfig(idJoy, &jwc, pcfg, fl ); if( SUCCEEDED(hres) ) { } goto done; } else { /* * Since pcfg is not null, we set it here to avoid calling * DIWdm_JoyHidMapping. Even if it fails, it doesn't hurt anything. */ hres = JoyReg_SetConfig(idJoy, &jwc, pcfg, fl); hres = DIWdm_SetJoyId(&pcfg->guidInstance, idJoy); } } else { /* * This is in Win9X, and VJOYD devices are being used. * We don't want to add WDM device at the same time. */ hres = E_FAIL; } } else { hres = JoyReg_SetConfig(idJoy, &jwc, pcfg, DIJC_UPDATEALIAS | fl); if (SUCCEEDED(hres)) { #ifdef WINNT PostMessage(HWND_BROADCAST, g_wmJoyChanged, idJoy+1, 0L); #else joyConfigChanged(0); #endif if( !fWinnt ) { fVjoydDeviceNotExist = FALSE; } } } } done:; CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | DeleteConfig | * * Delete configuration information about a joystick. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm UINT | idJoy | * * Zero-based joystick identification number. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can alter joystick configuration settings. * * = : One or more * parameters was invalid. * *****************************************************************************/ DIJOYCONFIG c_djcReset = { cbX(c_djcReset), /* dwSize */ { 0}, /* guidInstance */ { 0}, /* hwc */ DI_FFNOMINALMAX, /* dwGain */ { 0}, /* wszType */ { 0}, /* wszCallout */ }; STDMETHODIMP CJoyCfg_DeleteConfig(PDJC pdjc, UINT idJoy) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::DeleteConfig, (_ "pu", pdjc, idJoy)); if(SUCCEEDED(hres = hresPv(pdjc))) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this))) { HKEY hk; TCHAR tsz[MAX_JOYSTRING]; DIJOYCONFIG dijcfg; hres = DIWdm_DeleteConfig(idJoy); if( !fWinnt && hres == DIERR_DEVICENOTREG ) { fVjoydDeviceNotExist = TRUE; } /* * To delete it, set everything to the Reset values and * delete the configuration subkey. */ if( ( SUCCEEDED(hres) || hres == DIERR_DEVICENOTREG ) && SUCCEEDED(hres = JoyReg_SetConfig(idJoy, &c_djcReset.hwc, &c_djcReset, DIJC_SETVALID)) && SUCCEEDED(hres = JoyReg_OpenConfigKey(idJoy, MAXIMUM_ALLOWED, NULL, REG_OPTION_VOLATILE, &hk))) { wsprintf(tsz, TEXT("%u"), idJoy + 1); // DIWinnt_RegDeleteKey:: name is a mismomer, the function // recursively deletes the key and all subkeys. DIWinnt_RegDeleteKey(hk, tsz); RegCloseKey(hk); #ifndef WINNT joyConfigChanged(0); #endif hres = S_OK; } if( FAILED(hres) ) { if( FAILED( JoyReg_GetConfig(idJoy, NULL, &dijcfg, DIJC_REGHWCONFIGTYPE | DIJC_GUIDINSTANCE) ) ) { /* No config exists, so vacuous success on delete */ hres = S_FALSE; } } } CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | GetUserValues | * * Obtain information about user settings for the joystick. * * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm IN OUT LPDIJOYUSERVALUES | pjuv | * * Receives information about the user joystick configuration. * The caller "must" initialize the * field before calling this method. * * @parm DWORD | dwFlags | * * Zero or more flags specifying which parts * of the structure contain values * which are to be retrieved. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CJoyCfg_GetUserValues(PDJC pdjc, LPDIJOYUSERVALUES pjuv, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::GetUserValues, (_ "ppx", pdjc, pjuv, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidWritePxCb(pjuv, DIJOYUSERVALUES, 2)) && SUCCEEDED(hres = hresFullValidFl(fl, DIJU_GETVALID, 3))) { PJC this = _thisPvNm(pdjc, djc); hres = JoyReg_GetUserValues(pjuv, fl); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresFullValidUVStr | * * Validate a string field in a . * * @parm IN LPCWSTR | pwsz | * * String to be validated. * * @parm UINT | cwch | * * Maximum string length. * * @parm LPCSTR | pszName | * * Field name. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ #ifndef XDEBUG #define hresFullValidUVStr_(pwsz, cwch, pszName, z, i) \ _hresFullValidUVStr_(pwsz, cwch) \ #endif #define hresFullValidUVStr(pjuv, f, iarg) \ hresFullValidUVStr_(pjuv->f, cA(pjuv->f), #f, s_szProc,iarg)\ HRESULT INLINE hresFullValidUVStr_(LPCWSTR pwsz, UINT cwch, LPCSTR pszName, LPCSTR s_szProc, int iarg) { HRESULT hres; if(SUCCEEDED(hres = hresFullValidReadStrW(pwsz, cwch, iarg))) { } else { #ifdef XDEBUG RPF("%s: Invalid value for DIJOYUSERVALUES.%s", s_szProc, pszName); #endif } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | SetUserValues | * * Set the user settings for the joystick. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm IN LPCDIJOYUSERVALUES | pjuv | * * Contains information about the new user joystick settings. * * @parm DWORD | dwFlags | * * Zero or more flags specifying which parts * of the structure contain values * which are to be set. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can alter joystick configuration settings. * * = : One or more * parameters was invalid. * *****************************************************************************/ STDMETHODIMP CJoyCfg_SetUserValues(PDJC pdjc, LPCDIJOYUSERVALUES pjuv, DWORD fl) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::SetUserValues, (_ "pp", pdjc, pjuv, fl)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidReadPxCb(pjuv, DIJOYUSERVALUES, 2)) && fLimpFF(fl & DIJU_GLOBALDRIVER, SUCCEEDED(hres = hresFullValidUVStr(pjuv, wszGlobalDriver, 2))) && fLimpFF(fl & DIJU_GAMEPORTEMULATOR, SUCCEEDED(hres = hresFullValidUVStr(pjuv, wszGameportEmulator, 2))) && SUCCEEDED(hres = hresFullValidFl(fl, DIJU_SETVALID, 3))) { PJC this = _thisPvNm(pdjc, djc); CJoyCfg_EnterCrit(this); if(SUCCEEDED(hres = CJoyCfg_IsAcquired(this))) { hres = JoyReg_SetUserValues(pjuv, fl); } CJoyCfg_LeaveCrit(this); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | AddNewHardware | * * Displays the "Add New Hardware" dialog to * guide the user through installing * new game controller. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm HWND | hwndOwner | * * Window to act as owner window for UI. * * @parm REFGUID | rguidClass | * * which specifies the class of the hardware device * to be added. DirectInput comes with the following * class already defined: * * : Keyboard devices. * * : Mouse devices. * * : Media devices, including joysticks. * * : HID devices. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * * : The "media" class installer * could not be found or is invalid. * * : The user cancelled the operation. * * : The INF file for the device the user * selected could not be found or is invalid or is damaged. * * : DirectInput could not determine whether the * operation completed successfully. * *****************************************************************************/ STDMETHODIMP CJoyCfg_AddNewHardware(PDJC pdjc, HWND hwnd, REFGUID rguid) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::AddNewHardware, (_ "pxG", pdjc, hwnd, rguid)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidHwnd0(hwnd, 1)) && SUCCEEDED(hres = hresFullValidGuid(rguid, 2))) { PJC this = _thisPvNm(pdjc, djc); hres = AddNewHardware(hwnd, rguid); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | OpenTypeKey | * * Open the registry key associated with a joystick type. * * Control panel applications can use this key to store * per-type persistent information, such as global * configuration parameters. * * Such private information should be kept in a subkey * named "OEM"; do not store private information in the * main type key. * * Control panel applications can also use this key to * read configuration information, such as the strings * to use for device calibration prompts. * * The application should use to close * the registry key. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm LPCWSTR | pwszType | * * Points to the name of the type. The name of the type may * not exceed characters, including the terminating * null character. * * The name may not begin with * a "#" character. Types beginning with "#" are reserved * by DirectInput. * * @parm REGSAM | regsam | * * Registry security access mask. This can be any of the * values permitted by the function. * If write access is requested, then joystick * configuration must first have been acquired. * If only read access is requested, then acquisition is * not required. * * @parm PHKEY | phk | * * Receives the opened registry key on success. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can open a joystick type configuration key * for writing. * * = : One or more * parameters was invalid. * * : * A Win32 error code if access to the key is denied by * registry permissions or some other external factor. * *****************************************************************************/ STDMETHODIMP CJoyCfg_OpenTypeKey(PDJC pdjc, LPCWSTR pwszType, REGSAM sam, PHKEY phk) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::OpenTypeKey, (_ "pWx", pdjc, pwszType, sam)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidReadStrW(pwszType, MAX_JOYSTRING, 1)) && SUCCEEDED(hres = hresFullValidPcbOut(phk, cbX(*phk), 3))) { PJC this = _thisPvNm(pdjc, djc); if(pwszType[0] != TEXT('#')) { /* * Attempting to get write access requires acquisition. */ if(fLimpFF(IsWriteSam(sam), SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))) { hres = JoyReg_OpenTypeKey(pwszType, sam, REG_OPTION_NON_VOLATILE, phk); } } else { RPF("%s: Invalid pwszType (predefined)", s_szProc); hres = E_INVALIDARG; } } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | hresFullValidRegSam | * * Validate a registry access mask. The mask must be nonzero. * * @parm REGSAM | sam | * * Access mask, must not be zero. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * = : One or more * parameters was invalid. * *****************************************************************************/ #ifndef XDEBUG #define hresFullValidRegSam_(sam, z, i) \ _hresFullValidRegSam_(sam) \ #endif #define hresFullValidRegSam(sam, iarg) \ hresFullValidRegSam_(sam, s_szProc, iarg) \ HRESULT INLINE hresFullValidRegSam_(REGSAM sam, LPCSTR s_szProc, int iarg) { HRESULT hres; if(sam == 0) { #ifdef XDEBUG RPF("%s: Invalid registry access mask", s_szProc); #endif hres = E_INVALIDARG; } else { hres = S_OK; } return hres; } /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputJoyConfig | OpenConfigKey | * * Open the registry key associated with a * joystick configuration. * * Control panel applications can use this key to store * per-joystick persistent information, such as * button mappings. * * Such private information should be kept in a subkey * named "OEM"; do not store private information in the * main configuration key. * * The application should use to close * the registry key. * * @cwrap LPDIRECTINPUTJOYCONFIG | lpDirectInputJoyConfig * * @parm UINT | idJoy | * * Zero-based joystick identification number. * * @parm REGSAM | regsam | * * Registry security access mask. This can be any of the * values permitted by the function. * If write access is requested, then joystick * configuration must first have been acquired. * If only read access is requested, then acquisition is * not required. * * At least one access mask must be specified. * * @parm PHKEY | phk | * * Receives the opened registry key on success. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully. * * : Joystick configuration has not been * acquired. You must call * before you can open a joystick type configuration key * for writing. * * = : One or more * parameters was invalid. * * : The application attempted to open * the configuration key for reading, but no configuration * key for the joystick has yet been created. * Applications should proceed * as if the key were empty. * * : * A Win32 error code if access to the key is denied by * registry permissions or some other external factor. * *****************************************************************************/ STDMETHODIMP CJoyCfg_OpenConfigKey(PDJC pdjc, UINT idJoy, REGSAM sam, PHKEY phk) { HRESULT hres; EnterProcR(IDirectInputJoyConfig::OpenConfigKey, (_ "pux", pdjc, idJoy, sam)); if(SUCCEEDED(hres = hresPv(pdjc)) && SUCCEEDED(hres = hresFullValidRegSam(sam, 2)) && SUCCEEDED(hres = hresFullValidPcbOut(phk, cbX(*phk), 3))) { PJC this = _thisPvNm(pdjc, djc); /* * Attempting to get write access requires acquisition. */ if(fLimpFF(IsWriteSam(sam), SUCCEEDED(hres = CJoyCfg_IsAcquired(this)))) { // Since it always fails on Winnt, we don't need bother to run it at all. #ifndef WINNT if( idJoy < cA(rgGUID_Joystick) ) { HKEY hkMain; hres = JoyReg_OpenConfigKey(idJoy, sam, NULL, REG_OPTION_NON_VOLATILE, &hkMain); if(SUCCEEDED(hres)) { TCHAR tsz[20]; wsprintf(tsz, TEXT("%u"), idJoy + 1); hres = hresMumbleKeyEx(hkMain, tsz, sam, REG_OPTION_NON_VOLATILE, phk); RegCloseKey(hkMain); } } else #endif { hres = E_INVALIDARG; } } } ExitOleProcR(); return hres; } /***************************************************************************** * * CJoyCfg_New (constructor) * *****************************************************************************/ STDMETHODIMP CJoyCfg_New(PUNK punkOuter, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcI(IDirectInputJoyConfig8::, (_ "p", ppvObj)); if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 3))) { LPVOID pvTry = NULL; hres = Common_NewRiid(CJoyCfg, punkOuter, riid, &pvTry); if(SUCCEEDED(hres)) { /* Must use _thisPv in case of aggregation */ PJC this = _thisPv(pvTry); this->fCritInited = fInitializeCriticalSection(&this->crst); if( this->fCritInited ) { *ppvObj = pvTry; hres = S_OK; } else { Common_Unhold(this); *ppvObj = NULL; hres = E_OUTOFMEMORY; } } } ExitOleProcPpvR(ppvObj); return hres; } /***************************************************************************** * * The long-awaited vtbls and templates * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define CJoyCfg_Signature 0x6766434B /* "JCfg" */ Primary_Interface_Begin(CJoyCfg, IDirectInputJoyConfig) CJoyCfg_Acquire, CJoyCfg_Unacquire, CJoyCfg_SetCooperativeLevel, CJoyCfg_SendNotify, CJoyCfg_EnumTypes, CJoyCfg_GetTypeInfo, CJoyCfg_SetTypeInfo, CJoyCfg_DeleteType, CJoyCfg_GetConfig, CJoyCfg_SetConfig, CJoyCfg_DeleteConfig, CJoyCfg_GetUserValues, CJoyCfg_SetUserValues, CJoyCfg_AddNewHardware, CJoyCfg_OpenTypeKey, CJoyCfg_OpenConfigKey, Primary_Interface_End(CJoyCfg, IDirectInputJoyConfig) #endif /* DIRECTINPUT_VERSION > 0x0300 */