/***************************************************************************** * * DIEShep.c * * Copyright (c) 1997 Microsoft Corporation. All Rights Reserved. * * Abstract: * * The IDirectInputEffectDriver shepherd. * * The shepherd does the annoying work of babysitting the * external IDirectInputDriver. * * It makes sure nobody parties on bad handles. * * It handles cross-process (or even intra-process) effect * management. * * It caches the joystick ID so you don't have to. * * Contents: * * CEShep_New * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflEShep #pragma BEGIN_CONST_DATA #ifdef IDirectInputDevice2Vtbl /***************************************************************************** * * Declare the interfaces we will be providing. * *****************************************************************************/ Primary_Interface(CEShep, IDirectInputEffectShepherd); /***************************************************************************** * * @doc INTERNAL * * @struct CEShep | * * The object, which * babysits an . * * @field IDirectInputEffectShepherd | des | * * DirectInputEffectShepherd object (containing vtbl). * * @field IDirectInputEffectDriver * | pdrv | * * Delegated effect driver interface. * * @field UINT | idJoy | * * Joystick identification number. * * @field HINSTANCE | hinst | * * The instance handle of the DLL that contains the effect * driver. * *****************************************************************************/ typedef struct CEShep { /* Supported interfaces */ IDirectInputEffectShepherd des; IDirectInputEffectDriver *pdrv; UINT idJoy; HINSTANCE hinst; } CEShep, ES, *PES; typedef IDirectInputEffectShepherd DES, *PDES; #define ThisClass CEShep #define ThisInterface IDirectInputEffectShepherd /***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputEffectShepherd | QueryInterface | * * Gives a client access to other interfaces on an object. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @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 EXTERNAL * * @method HRESULT | IDirectInputEffectShepherd | AddRef | * * Increments the reference count for the interface. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * ***************************************************************************** * * @doc EXTERNAL * * @method HRESULT | IDirectInputEffectShepherd | Release | * * Decrements the reference count for the interface. * If the reference count on the object falls to zero, * the object is freed from memory. * * @cwrap LPDIRECTINPUT | lpDirectInput * * @returns * * Returns the object reference count. * * @xref OLE documentation for . * *//************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | 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 | IDirectInputEffectShepherd | 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(CEShep) Default_AddRef(CEShep) Default_Release(CEShep) #else #define CEShep_QueryInterface Common_QueryInterface #define CEShep_AddRef Common_AddRef #define CEShep_Release Common_Release #endif #define CEShep_QIHelper Common_QIHelper #define CEShep_AppFinalize Common_AppFinalize /***************************************************************************** * * @doc INTERNAL * * @func void | CEShep_Finalize | * * Clean up our instance data. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/ void INTERNAL CEShep_Finalize(PV pvObj) { PES this = pvObj; Invoke_Release(&this->pdrv); if (this->hinst) { FreeLibrary(this->hinst); } } /***************************************************************************** * * @doc INTERNAL * * @method void | CEShep | UnlockDevice | * * Unlock the joystick table after we are finished messing * with the device. * *****************************************************************************/ void INLINE CEShep_UnlockDevice(void) { ReleaseMutex(g_hmtxJoy); } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CEShep | LockDevice | * * Validate that the the device access token is still valid. * * If so, then take the joystick mutex to prevent someone * from dorking with the device while we're using it. * Call when done. * * If not, then try to steal ownership if requested. * * Else, fail. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Handle to lock. * * @parm DWORD | dwAccess | * * If , then force ownership of the device. * This is done as part of device acquisition to kick out the * previous owner. * * Otherwise, if the device belongs to somebody else, then * leave it alone. * * * @returns * * if the operation completed successfully. * * if the lock failed. * Note that * * and * * are particularly keen on this error code. * *****************************************************************************/ STDMETHODIMP CEShep_LockDevice(PES this, PSHEPHANDLE psh, DWORD dwAccess) { HRESULT hres; EnterProc(CEShep_LockDevice, (_ "puu", this, psh->dwTag, dwAccess)); WaitForSingleObject(g_hmtxJoy, INFINITE); /* * Note that DISFFC_FORCERESET allows unconditional access. * DISFFC_FORCERESET is used when we perform the initial reset * after acquiring, so we can legitimately steal the device * from the previous owner. */ if (dwAccess & DISFFC_FORCERESET) { hres = S_OK; } else if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) { hres = S_OK; } else { ReleaseMutex(g_hmtxJoy); hres = DIERR_NOTEXCLUSIVEACQUIRED; } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method void | CEShep | UnlockEffect | * * Unlock the joystick table after we are finished messing * with an effect. * *****************************************************************************/ void INLINE CEShep_UnlockEffect(void) { ReleaseMutex(g_hmtxJoy); } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CEShep | LockEffect | * * Validate that the the effect handle is still valid. * * If so, then take the joystick mutex to prevent someone * from dorking with the device while we're using the handle. * Call when done. * * If not, then set the effect handle to zero to indicate * that it's bogus. The * * method relies on the zero-ness. * It is also asserted in to make * sure we don't accidentally leave effects on the device * when we leave. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Handle to lock. * * @returns * * if the operation completed successfully. * * if the lock failed. * Note that * and * assume * that this is the only possible error code. * *****************************************************************************/ STDMETHODIMP CEShep_LockEffect(PES this, PSHEPHANDLE psh) { HRESULT hres; EnterProc(CEShep_LockEffect, (_ "pux", this, psh->dwTag, psh->dwEffect)); WaitForSingleObject(g_hmtxJoy, INFINITE); if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag && psh->dwEffect) { hres = S_OK; } else { psh->dwEffect = 0; ReleaseMutex(g_hmtxJoy); hres = DIERR_NOTDOWNLOADED; } ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | DeviceID | * * Inform the driver of the identity of the device. * * For example, if a device driver is passed *

= 2 and

= 1, * then this means that unit 1 on the device * corresponds to joystick ID number 2. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm DWORD | dwExternalID | * * The joystick ID number being used. * The Windows joystick subsystem allocates external IDs. * * @parm DWORD | fBegin | * * Nonzero if access to the device is begining. * Zero if the access to the device is ending. * * @parm LPVOID | lpReserved | * * Reserved for future use (HID). * * @returns * * if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/ STDMETHODIMP CEShep_DeviceID(PDES pdes, DWORD dwExternalID, DWORD fBegin, LPVOID pvReserved) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::DeviceID, (_ "puu", pdes, dwExternalID, fBegin)); this = _thisPvNm(pdes, des); AssertF(dwExternalID < cJoyMax); if (dwExternalID < cJoyMax) { VXDINITPARMS vip; /* * If this device has never been used before, * go grab its global gain. */ WaitForSingleObject(g_hmtxJoy, INFINITE); if (g_psoh->rggjs[dwExternalID].dwTag == 0) { DIJOYCONFIG cfg; JOYCAPS caps; g_psoh->rggjs[dwExternalID].dwTag = 1; hres = JoyReg_GetConfig(dwExternalID, &caps, &cfg, DIJC_GAIN); if (SUCCEEDED(hres)) { SquirtSqflPtszV(sqfl, TEXT("Joystick %d global gain = %d"), dwExternalID, cfg.dwGain); g_psoh->rggjs[dwExternalID].dwCplGain = cfg.dwGain; } else { g_psoh->rggjs[dwExternalID].dwCplGain = DI_FFNOMINALMAX; } /* * Set to DI_FFNOMINALMAX until we learn better. */ g_psoh->rggjs[dwExternalID].dwDevGain = DI_FFNOMINALMAX; } ReleaseMutex(g_hmtxJoy); /* * Ask the HEL for the internal ID. */ hres = Hel_Joy_GetInitParms(dwExternalID, &vip); if (SUCCEEDED(hres)) { this->idJoy = dwExternalID; hres = this->pdrv->lpVtbl->DeviceID(this->pdrv, DIRECTINPUT_VERSION, dwExternalID, fBegin, vip.dwId, pvReserved); } } else { hres = E_FAIL; } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | Escape | * * Escape to the driver. This method is called * in response to an application invoking the * method. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about the effect at which the command is directed. * * @parm LPDIEFFESCAPE | pesc | * * Command block. * * @returns * * if the operation completed successfully. * * if the effect is not downloaded. * *****************************************************************************/ STDMETHODIMP CEShep_Escape(PDES pdes, PSHEPHANDLE psh, LPDIEFFESCAPE pesc) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::Escape, (_ "puxx", pdes, psh->dwTag, psh->dwEffect, pesc->dwCommand)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) { if (psh->dwEffect) { hres = this->pdrv->lpVtbl->Escape(this->pdrv, this->idJoy, psh->dwEffect, pesc); } else { hres = DIERR_NOTDOWNLOADED; } CEShep_UnlockEffect(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | DeviceEscape | * * Escape to the driver. This method is called * in response to an application invoking the * method. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about the ownership of the device. * * @parm LPDIEFFESCAPE | pesc | * * Command block. * * @returns * * if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/ STDMETHODIMP CEShep_DeviceEscape(PDES pdes, PSHEPHANDLE psh, LPDIEFFESCAPE pesc) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::DeviceEscape, (_ "pux", pdes, psh->dwTag, pesc->dwCommand)); this = _thisPvNm(pdes, des); AssertF(psh->dwEffect == 0); WaitForSingleObject(g_hmtxJoy, INFINITE); if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) { hres = this->pdrv->lpVtbl->Escape(this->pdrv, this->idJoy, 0, pesc); } else { hres = DIERR_NOTEXCLUSIVEACQUIRED; } ReleaseMutex(g_hmtxJoy); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CEShep | SetPhysGain | * * Set the physical gain based on the global gain * and the local gain. * * The caller must already have the global joystick lock. * * * @cwrap PES | this * * @returns * * if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/ STDMETHODIMP CEShep_SetPhysGain(PES this) { HRESULT hres; hres = this->pdrv->lpVtbl->SetGain( this->pdrv, this->idJoy, MulDiv(g_psoh->rggjs[this->idJoy].dwDevGain, g_psoh->rggjs[this->idJoy].dwCplGain, DI_FFNOMINALMAX)); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | SetGlobalGain | * * Set the global gain. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm DWORD | dwCplGain | * * The new global gain value. * * @returns * * if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/ STDMETHODIMP CEShep_SetGlobalGain(PDES pdes, DWORD dwCplGain) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::SetGlobalGain, (_ "pu", pdes, dwCplGain)); this = _thisPvNm(pdes, des); WaitForSingleObject(g_hmtxJoy, INFINITE); g_psoh->rggjs[this->idJoy].dwCplGain = dwCplGain; hres = CEShep_SetPhysGain(this); ReleaseMutex(g_hmtxJoy); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | SetGain | * * Set the overall device gain. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about device ownership. * * @parm DWORD | dwDevGain | * * The new local gain value. * * @returns * * if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/ STDMETHODIMP CEShep_SetGain(PDES pdes, PSHEPHANDLE psh, DWORD dwDevGain) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::SetGain, (_ "puu", pdes, psh->dwTag, dwDevGain)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, DISFFC_NULL))) { g_psoh->rggjs[this->idJoy].dwDevGain = dwDevGain; hres = CEShep_SetPhysGain(this); CEShep_UnlockDevice(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | SendForceFeedbackCommand | * * Send a command to the device. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about device ownership. * * @parm DWORD | dwCmd | * * Command, one of the values. * * @returns * on success. * * @devnote * * Semantics unclear. * *****************************************************************************/ STDMETHODIMP CEShep_SetForceFeedbackState(PDES pdes, PSHEPHANDLE psh, DWORD dwCmd) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::SetForceFeedbackState, (_ "pux", pdes, psh->dwTag, dwCmd)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, dwCmd))) { if (dwCmd & DISFFC_FORCERESET) { dwCmd &= ~DISFFC_FORCERESET; dwCmd |= DISFFC_RESET; } hres = this->pdrv->lpVtbl->SendForceFeedbackCommand( this->pdrv, this->idJoy, dwCmd); if (SUCCEEDED(hres) && (dwCmd & DISFFC_RESET)) { psh->dwTag = ++g_psoh->rggjs[this->idJoy].dwTag; } CEShep_UnlockDevice(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | GetForceFeedbackState | * * Retrieve the force feedback state for the device. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about device ownership. * * @parm LPDIDEVICESTATE | pds | * * Receives device state. * * @returns * on success. * * @devnote * * Semantics unclear. * *****************************************************************************/ STDMETHODIMP CEShep_GetForceFeedbackState(PDES pdes, PSHEPHANDLE psh, LPDIDEVICESTATE pds) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::GetForceFeedbackState, (_ "pup", pdes, psh->dwTag, pds)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, DISFFC_NULL))) { hres = this->pdrv->lpVtbl->GetForceFeedbackState( this->pdrv, this->idJoy, pds); CEShep_UnlockDevice(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | DownloadEffect | * * Send an effect to the device. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm DWORD | dwEffectId | * * Magic cookie dword that identifies the effect. * * @parm IN OUT PSHEPHANDLE | psh | * * On entry, contains the handle of the effect being * downloaded. If the value is zero, then a new effect * is downloaded. If the value is nonzero, then an * existing effect is modified. * * On exit, contains the new effect handle. * * @parm LPCDIEFFECT | peff | * * The new parameters for the effect. The axis and button * values have been converted to axis/button indexes. * * @returns * on success. * * if no change was made. * *****************************************************************************/ STDMETHODIMP CEShep_DownloadEffect(PDES pdes, DWORD dwEffectId, PSHEPHANDLE psh, LPCDIEFFECT peff, DWORD fl) { PES this; HRESULT hres = S_OK; EnterProcI(IDirectInputEffectShepherd::DownloadEffect, (_ "pxuppx", pdes, dwEffectId, psh->dwTag, psh->dwEffect, peff, fl)); this = _thisPvNm(pdes, des); /* * Downloading an effect is sufficiently different from all * other methods that we do the locking manually. */ WaitForSingleObject(g_hmtxJoy, INFINITE); /* * If not downloading, then it doesn't matter whether or not * the tag matches. However, if the tag doesn't match, then * we must wipe out the download handle because it's dead. */ if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) { } else { psh->dwEffect = 0; if (fl & DIEP_NODOWNLOAD) { /* It's okay if not downloading */ } else { hres = DIERR_NOTEXCLUSIVEACQUIRED; goto done; } } /* * If downloading and creating a new effect, * then all parameters need to be downloaded. */ if (!(fl & DIEP_NODOWNLOAD) && psh->dwEffect == 0) { fl |= DIEP_ALLPARAMS; } if (fl) { hres = this->pdrv->lpVtbl->DownloadEffect( this->pdrv, this->idJoy, dwEffectId, &psh->dwEffect, peff, fl); } else { hres = S_FALSE; } done:; ReleaseMutex(g_hmtxJoy); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | DestroyEffect | * * Remove an effect from the device. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about the effect to be destroyed. On exit, * the is zero'd so nobody will use * it any more. * * @returns * on success. * * if the effect was already destroyed. * *****************************************************************************/ STDMETHODIMP CEShep_DestroyEffect(PDES pdes, PSHEPHANDLE psh) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::DestroyEffect, (_ "pux", pdes, psh->dwTag, psh->dwEffect)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) { DWORD dwEffect = psh->dwEffect; psh->dwEffect = 0; hres = this->pdrv->lpVtbl->DestroyEffect( this->pdrv, this->idJoy, dwEffect); CEShep_UnlockEffect(); } else { hres = S_FALSE; } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | StartEffect | * * Begin playback of an effect. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm PSHEPHANDLE | psh | * * Information about the effect to be played. * * @parm DWORD | dwMode | * * How the effect is to affect other effects. * * @parm DWORD | dwCount | * * Number of times the effect is to be played. * * @returns * on success. * *****************************************************************************/ STDMETHODIMP CEShep_StartEffect(PDES pdes, PSHEPHANDLE psh, DWORD dwMode, DWORD dwCount) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::StartEffect, (_ "puxxu", pdes, psh->dwTag, psh->dwEffect, dwMode, dwCount)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) { hres = this->pdrv->lpVtbl->StartEffect(this->pdrv, this->idJoy, psh->dwEffect, dwMode, dwCount); CEShep_UnlockEffect(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | StopEffect | * * Halt playback of an effect. * * ISSUE-2001/03/29-timgill There is no way to pause an effect * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm PSHEPHANDLE | psh | * * Information about the effect to be stopped. * * @returns * on success. * *****************************************************************************/ STDMETHODIMP CEShep_StopEffect(PDES pdes, PSHEPHANDLE psh) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::StopEffect, (_ "pux", pdes, psh->dwTag, psh->dwEffect)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) { hres = this->pdrv->lpVtbl->StopEffect(this->pdrv, this->idJoy, psh->dwEffect); CEShep_UnlockEffect(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | GetEffectStatus | * * Obtain information about an effect. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm PSHEPHANDLE | psh | * * Information about the effect to be queried. * * @parm LPDWORD | pdwStatus | * * Receives the effect status. * * @returns * on success. * *****************************************************************************/ STDMETHODIMP CEShep_GetEffectStatus(PDES pdes, PSHEPHANDLE psh, LPDWORD pdwStatus) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::GetEffectStatus, (_ "pux", pdes, psh->dwTag, psh->dwEffect)); this = _thisPvNm(pdes, des); if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) { hres = this->pdrv->lpVtbl->GetEffectStatus(this->pdrv, this->idJoy, psh->dwEffect, pdwStatus); CEShep_UnlockEffect(); } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | GetVersions | * * Obtain version information about the force feedback * hardware and driver. * * @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd * * @parm LPDIDRIVERVERSIONS | pvers | * * A structure which will be filled in with version information * describing the hardware, firmware, and driver. * * @returns * on success. * *****************************************************************************/ STDMETHODIMP CEShep_GetVersions(PDES pdes, LPDIDRIVERVERSIONS pvers) { PES this; HRESULT hres; EnterProcI(IDirectInputEffectShepherd::GetVersions, (_ "p", pdes)); this = _thisPvNm(pdes, des); AssertF(pvers->dwSize == cbX(*pvers)); hres = this->pdrv->lpVtbl->GetVersions(this->pdrv, pvers); ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CEShep | InitInstance | * * Initialize a new instance of * an IDirectInputEffectShepherd object. * * If an in-proc OLE server is needed, then load it. * * Otherwise, use our private interface that goes down * to our helper driver. * * @parm IN HKEY | hkFF | * * Force feedback registry key. * * @returns * * Standard OLE . * *****************************************************************************/ STDMETHODIMP CEShep_InitInstance(PES this, HKEY hkFF) { LONG lRc; HRESULT hres; TCHAR tszClsid[ctchGuid]; EnterProcI(IDirectInputEffectShepherd::InitInstance, (_ "x", hkFF)); if( hkFF == 0x0 ) { TCHAR tszName[ctchNameGuid]; NameFromGUID(tszName, &IID_IDirectInputPIDDriver ); memcpy(tszClsid, &tszName[ctchNamePrefix], cbX(tszClsid) ); lRc = ERROR_SUCCESS; }else { lRc = RegQueryString(hkFF, TEXT("CLSID"), tszClsid, cA(tszClsid)); } if (lRc == ERROR_SUCCESS) { hres = DICoCreateInstance(tszClsid, 0, &IID_IDirectInputEffectDriver, &this->pdrv, &this->hinst); /* * If anything went wrong, change the error to * E_NOTIMPL so the app won't see a wacky CoCreateInstance * error code. */ if (FAILED(hres)) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("Substituting E_NOTIMPL for FF driver CoCreateInstance error 0x%08x"), hres ); hres = E_NOTIMPL; } } else { #ifdef WINNT hres = E_NOTIMPL; #else DWORD cb = 0; lRc = RegQueryValueEx(hkFF, TEXT("VJoyD"), 0, 0, 0, &cb); if (lRc == ERROR_SUCCESS || lRc == ERROR_MORE_DATA) { hres = CEffVxd_New(0, &IID_IDirectInputEffectDriver, &this->pdrv); } else { hres = E_NOTIMPL; } #endif } ExitOleProcR(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @method HRESULT | IDirectInputEffectShepherd | New | * * Create a new instance of an IDirectInputEffectShepherd object. * * @parm IN HKEY | hkFF | * * Force feedback registry key. * * @parm IN PUNK | punkOuter | * * Controlling unknown for aggregation. * * @parm IN RIID | riid | * * Desired interface to new object. * * @parm OUT PPV | ppvObj | * * Output pointer for new object. * * @returns * * Standard OLE . * *****************************************************************************/ STDMETHODIMP CEShep_New(HKEY hkFF, PUNK punkOuter, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcR(IDirectInputEffectShepherd::, (_ "G", riid)); AssertF(g_hmtxJoy); hres = Common_NewRiid(CEShep, punkOuter, riid, ppvObj); if (SUCCEEDED(hres)) { /* Must use _thisPv in case of aggregation */ PES this = _thisPv(*ppvObj); if (SUCCEEDED(hres = CEShep_InitInstance(this, hkFF))) { } else { Invoke_Release(ppvObj); } } ExitOleProcPpvR(ppvObj); return hres; } /***************************************************************************** * * The long-awaited vtbls and templates * *****************************************************************************/ #pragma BEGIN_CONST_DATA #define CEShep_Signature 0x50454853 /* "SHEP" */ Interface_Template_Begin(CEShep) Primary_Interface_Template(CEShep, IDirectInputEffectShepherd) Interface_Template_End(CEShep) Primary_Interface_Begin(CEShep, IDirectInputEffectShepherd) CEShep_DeviceID, CEShep_GetVersions, CEShep_Escape, CEShep_DeviceEscape, CEShep_SetGain, CEShep_SetForceFeedbackState, CEShep_GetForceFeedbackState, CEShep_DownloadEffect, CEShep_DestroyEffect, CEShep_StartEffect, CEShep_StopEffect, CEShep_GetEffectStatus, CEShep_SetGlobalGain, Primary_Interface_End(CEShep, IDirectInputEffectShepherd) #endif