|
|
/*****************************************************************************
* * DInput.c * * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved. * * Abstract: * * * * Contents: * * DirectInput8Create() * DllGetClassObject() * DllCanUnloadNow() * DllMain() * *****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
* * The sqiffle for this file. * *****************************************************************************/
#define sqfl sqflDll
/***************************************************************************
* * @doc INTERNAL * * @topic The DirectInput synchronization hierarchy | * * Extreme caution must be exercised to ensure that the synchronization * hierarchy is preserved. Failure to observe these rules will result * in a deadlock. * * @ex * * In the following list, locks must be taken in the order specified. * Furthermore, the Dll critical section and the cross-process mutexes * may never be taken simultaneously. (They should be considered * to be at the bottom of the hierarchy.) * * | * * DirectInputEffect * DirectInputDevice * Dll critical section * The cross-process global mutex * The cross-process joystick mutex * ***************************************************************************/
/*****************************************************************************
* * @doc INTERNAL * * @global DWORD | g_cRef | * * DLL reference count. * * @global HINSTANCE | g_hinst | * * DLL instance handle. * * @global LONG | g_lLoadLibrary | * * Number of times we have been artificially <f LoadLibrary>'d * to prevent ourselves from being unloaded by a non-OLE * application. Actually, it's the number of times minus one, * so we can use the interlocked functions to tell whether * the first <f LoadLibrary> is happening or the last * <f FreeLibrary> is happening. * * We perform a physical <f LoadLibrary> or <f FreeLibrary> * only on the transition, so as to avoid overflowing the * counter in KERNEL32. * * @global HANDLE | g_hVxD | * * Handle to VxD, if available. Win9x only! * * @global OPENVXDHANDLE | OpenVxDHandle | * * Address of Win9x-only KERNEL32 entry point to convert * a process handle into a VxD handle. Win9x only! * * @global DWORD | g_dwLastBonusPoll | * * Last time a bonus poll was made to tickle the SideWinder * drivers. Win9x only! * * @global CRITICAL_SECTION | g_crstDll | * * Per-process critical section to protect process-global * variables. * * @global DWORD | g_flEmulation | * * Flags that describe what levels of forced emulation are * active. * * @global HHOOK | g_hhkLLHookCheck | * * Used only temporarily to test whether low-level hooks * are supported on the system. * * @global HANDLE | g_hmtxGlobal | * * System-global mutex that protects shared memory blocks * which describe device exclusive acquisition information. * * @global HANDLE | g_hfm | * * Handle to the file mapping that describes the shared * memory block. NT requires us to keep the handle open * so that the associated name remains in the namespace. * * @global PSHAREDOBJECTPAGE | g_psop | * * Pointer to the shared memory block itself. * * @global HANDLE | g_hmtxJoy | * * System-global mutex that protects shared memory blocks * which describe joystick effects. * * @global UINT | g_wmJoyChanged | * * Registered window message which is broadcast when joysticks * are reconfigured. * * @global LONG | g_lWheelGranularity | * * The wheel granularity. One hardware "click" of the mouse * wheel results in this much reported motion. * * @global int | g_cdtoMax | * * Total number of elements in the g_pdto array. * * @global int | g_cdto | * * Number of occupied elements in the g_pdto array. * * @global PDWORD | g_rgdwCRCTable | * * Pointer to the array of values used for CRC generation. * * @global PDEVICETOUSER | g_pdto | * * Pointer to the device to owner array. * * @global BOOL | g_fCritInited | * * True if g_crstDll has been initialized. * * @global DIAPPHACKS | g_AppHacks | * * Application hacks. Initialized to: * fReacquire FALSE * fNoSubClass FALSE * nMaxDeviceNameLength MAX_PATH * * @global DWORD | g_dwAppDate | * * Application link date stamp, used in generating application ID * * @global DWORD | g_dwAppFileLen | * * Application file length, used in generating application ID * * @global DWORD | g_dwLastMsgSent | * * Last message WPARAM broadcast in a g_wmDInputNotify message. * This is one of the DIMSGWP_* values or zero if no message has yet * been broadcast. This value only ever increases as applications * cannot become less identifed as they proceed. This value is used * as a test of * * @global UINT | g_wmDInputNotify | * * Registered window message which is broadcast when a change of * application status is detected. * * *****************************************************************************/
DWORD g_cRef; HINSTANCE g_hinst; HINSTANCE g_hinstDbg; LONG g_lLoadLibrary = -1; #ifndef WINNT
HANDLE g_hVxD = INVALID_HANDLE_VALUE; OPENVXDHANDLE _OpenVxDHandle; DWORD g_dwLastBonusPoll; #endif
CRITICAL_SECTION g_crstDll; DWORD g_flEmulation; LPDWORD g_pdwSequence;
#ifdef USE_SLOW_LL_HOOKS
HHOOK g_hhkLLHookCheck; #endif
HANDLE g_hmtxGlobal; HANDLE g_hfm; struct SHAREDOBJECTPAGE *g_psop; HANDLE g_hmtxJoy; UINT g_wmJoyChanged; LONG g_lWheelGranularity;
int g_cdtoMax; int g_cdto; struct _DEVICETOUSER *g_pdto;
PDWORD g_rgdwCRCTable;
BOOL g_fCritInited;
#ifdef WORKER_THREAD
MSGWAITFORMULTIPLEOBJECTSEX _MsgWaitForMultipleObjectsEx = FakeMsgWaitForMultipleObjectsEx; #endif
CANCELIO _CancelIO = FakeCancelIO;
#ifdef XDEBUG
TRYENTERCRITICALSECTION _TryEnterCritSec = FakeTryEnterCriticalSection; int g_cCrit = -1; UINT g_thidCrit; HANDLE g_thhandleCrit; #endif
#ifdef DEBUG
TCHAR g_tszLogFile[MAX_PATH]; #endif
DIAPPHACKS g_AppHacks = {FALSE,FALSE,MAX_PATH}; DWORD g_dwAppDate; DWORD g_dwAppFileLen; DWORD g_dwLastMsgSent; UINT g_wmDInputNotify;
BOOL g_fRawInput;
#ifdef USE_WM_INPUT
HWND g_hwndThread; HANDLE g_hEventAcquire; HANDLE g_hEventThread; HANDLE g_hEventHid; #endif
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllEnterCrit | * * Take the DLL critical section. * * The DLL critical section is the lowest level critical section. * You may not attempt to acquire any other critical sections or * yield while the DLL critical section is held. Failure to * comply is a violation of the semaphore hierarchy and will * lead to deadlocks. * *****************************************************************************/
void EXTERNAL DllEnterCrit_(LPCTSTR lptszFile, UINT line) {
#ifdef XDEBUG
if ( ! _TryEnterCritSec(&g_crstDll) ) { SquirtSqflPtszV(sqflCrit, TEXT("Dll CritSec blocked @%s,%d"), lptszFile, line); EnterCriticalSection(&g_crstDll); }
if (++g_cCrit == 0) { g_thidCrit = GetCurrentThreadId(); g_thhandleCrit = GetCurrentThread();
SquirtSqflPtszV(sqflCrit, TEXT("Dll CritSec Entered @%s,%d"), lptszFile, line); } AssertF(g_thidCrit == GetCurrentThreadId()); #else
EnterCriticalSection(&g_crstDll); #endif
}
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | IsThreadActive | * * Check if the thread is still active. * *****************************************************************************/
BOOL IsThreadActive( HANDLE hThread ) { DWORD dwExitCode = 0;
return (NULL != hThread && GetExitCodeThread(hThread, &dwExitCode) && STILL_ACTIVE == dwExitCode ); }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllLeaveCrit | * * Leave the DLL critical section. * *****************************************************************************/
void EXTERNAL DllLeaveCrit_(LPCTSTR lptszFile, UINT line) { #ifdef XDEBUG
if( IsThreadActive(g_thhandleCrit) ) { AssertF(g_thidCrit == GetCurrentThreadId()); } else { SquirtSqflPtszV(sqflCrit, TEXT("Current thread has died.")); } AssertF(g_cCrit >= 0); if (--g_cCrit < 0) { g_thidCrit = 0; } SquirtSqflPtszV(sqflCrit, TEXT("Dll CritSec Leaving @%s,%d"), lptszFile, line); #endif
LeaveCriticalSection(&g_crstDll); }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllInCrit | * * Nonzero if we are in the DLL critical section. * *****************************************************************************/
#ifdef XDEBUG
BOOL INTERNAL DllInCrit(void) { return g_cCrit >= 0 && g_thidCrit == GetCurrentThreadId(); }
#endif
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllAddRef | * * Increment the reference count on the DLL. * *****************************************************************************/
void EXTERNAL DllAddRef(void) { InterlockedIncrement((LPLONG)&g_cRef); SquirtSqflPtszV(sqfl, TEXT("DllAddRef -> %d"), g_cRef); }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllRelease | * * Decrement the reference count on the DLL. * *****************************************************************************/
void EXTERNAL DllRelease(void) { InterlockedDecrement((LPLONG)&g_cRef); SquirtSqflPtszV(sqfl, TEXT("DllRelease -> %d"), g_cRef); }
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | DllLoadLibrary | * * Increment the DLL load count. * * This is to prevent a non-OLE application from unloading us * while we still have windows subclassed. * *****************************************************************************/
BOOL EXTERNAL DllLoadLibrary(void) { BOOL fRes = TRUE;
AssertF(InCrit());
if (InterlockedIncrement(&g_lLoadLibrary) == 0) { TCHAR tsz[MAX_PATH - 1];
/*
* See hresValidInstanceVer_ for an explanation of why * we need to pass cA() - 1 instead of cA(). */ if ( !GetModuleFileName(g_hinst, tsz, cA(tsz) - 1) || !LoadLibrary(tsz) ) { /*
* Restore the refcount. * There is no race condition here because we are always called * InCrit so the refcount after the restore can only be the same * as it was when we entered. If this should change, we would * need to take extra precautions as two threads could try to * LoadLibrary at the same time. As only one would do the * physical load, there is no way to let the other know if the * load failed. */ InterlockedDecrement(&g_lLoadLibrary); fRes = FALSE; RPF( "LoadLibrary( self ) failed!, le = %d", GetLastError() ); } } SquirtSqflPtszV(sqfl, TEXT("DllLoadLibrary -> %d"), g_lLoadLibrary);
return fRes; }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllFreeLibraryAndExitThread | * * Worker thread which frees the library in a less dangerous * (I'm loathe to say "safe") manner. * * ThreadProcs are prototyped to return a void but since the return * would follow some form of ExitThread, it will never be reached so * this function is declared to return void and cast. * * @parm LPVOID | pvContext | * * Unused context information. * *****************************************************************************/
void INTERNAL DllFreeLibraryAndExitThread(LPVOID pvContext) { /*
* Sleep for one extra second to make extra sure that the * DllFreeLibrary thread is out and gone. */ SleepEx(1000, FALSE);
FreeLibraryAndExitThread(g_hinst, 0);
/*NOTREACHED*/ }
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllFreeLibrary | * * Decrement the DLL load count. * * This undoes a previous <f DllLoadLibrary>. * * We can't blindly do a <f FreeLibrary>, because we might * be freeing our last reference, and then we will die because * we won't exist when the <f FreeLibrary> returns. * * If we are in the wacky case, then we spin a low-priority * thread whose job is to free us. We create it at low priority * so it will lose the race with this thread, which is busy * getting out of the way. * *****************************************************************************/
void EXTERNAL DllFreeLibrary(void) { if (InterlockedDecrement(&g_lLoadLibrary) < 0) { if (g_cRef) { /*
* There are other references to us, so we can just * go away quietly. */ FreeLibrary(g_hinst); } else { /*
* This is the last reference, so we need to create a * worker thread which will call <f FreeLibraryAndExitThread>. */ DWORD thid; HANDLE hth;
hth = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)DllFreeLibraryAndExitThread, 0, CREATE_SUSPENDED, &thid); if (hth) { SetThreadPriority(hth, THREAD_PRIORITY_IDLE); ResumeThread(hth); CloseHandle(hth); } } } SquirtSqflPtszV(sqfl, TEXT("DllFreeLibrary -> %d"), g_lLoadLibrary); }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | DllGetClassObject | * * Create an <i IClassFactory> instance for this DLL. * * @parm REFCLSID | rclsid | * * The object being requested. * * @parm RIID | riid | * * The desired interface on the object. * * @parm PPV | ppvOut | * * Output pointer. * * @comm * The artificial refcount inside <f DllClassObject> helps * to avoid the race condition described in <f DllCanUnloadNow>. * It's not perfect, but it makes the race window smaller. * *****************************************************************************/
#pragma BEGIN_CONST_DATA
#ifdef DEMONSTRATION_FFDRIVER
/*
* Build the fake force feedback driver for internal testing. */
GUID CLSID_EffectDriver = { 0x25E609E2,0xB259,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00 };
#endif
CLSIDMAP c_rgclsidmap[cclsidmap] = { { &CLSID_DirectInput8, CDIObj_New, IDS_DIRECTINPUT8,}, { &CLSID_DirectInputDevice8, CDIDev_New, IDS_DIRECTINPUTDEVICE8,}, #ifdef DEMONSTRATION_FFDRIVER
{ &CLSID_EffectDriver, CJoyEff_New, 0,}, #endif
};
#pragma END_CONST_DATA
STDAPI DllGetClassObject(REFCLSID rclsid, RIID riid, PPV ppvObj) { HRESULT hres; UINT iclsidmap; EnterProcR(DllGetClassObject, (_ "G", rclsid));
if ( g_fCritInited ) { DllAddRef(); for (iclsidmap = 0; iclsidmap < cA(c_rgclsidmap); iclsidmap++) { if (IsEqualIID(rclsid, c_rgclsidmap[iclsidmap].rclsid)) { hres = CDIFactory_New(c_rgclsidmap[iclsidmap].pfnCreate, riid, ppvObj); goto done; } } SquirtSqflPtszV(sqfl | sqflError, TEXT("%S: Wrong CLSID"), s_szProc); *ppvObj = 0; hres = CLASS_E_CLASSNOTAVAILABLE;
done:;
ExitOleProcPpv(ppvObj); DllRelease(); } else { hres = E_OUTOFMEMORY; RPF( "Failing DllGetClassObject due to lack of DLL critical section" ); } return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | DllCanUnloadNow | * * Determine whether the DLL has any outstanding interfaces. * * There is an unavoidable race condition between * <f DllCanUnloadNow> and the creation of a new * <i IClassFactory>: Between the time we return from * <f DllCanUnloaDNow> and the caller inspects the value, * another thread in the same process may decide to call * <f DllGetClassObject>, thus suddenly creating an object * in this DLL when there previously was none. * * It is the caller's responsibility to prepare for this * possibility; there is nothing we can do about it. * * @returns * * Returns <c S_OK> if the DLL can unload, <c S_FALSE> if * it is not safe to unload. * *****************************************************************************/
STDMETHODIMP DllCanUnloadNow(void) { HRESULT hres; #ifdef DEBUG
if (IsSqflSet(sqfl)) { SquirtSqflPtszV(sqfl, TEXT("DllCanUnloadNow() - g_cRef = %d"), g_cRef); Common_DumpObjects(); } #endif
hres = g_cRef ? S_FALSE : S_OK; return hres; }
#ifdef USE_SLOW_LL_HOOKS
/*****************************************************************************
* * @doc INTERNAL * * @func LRESULT | DllLlHookTest | * * Tiny hook procedure used to test whether LL hooks are * supported by the operating system. * * This function is almost never called. We install the * hook and immediately remove it. The only time it * manages to get called is if the user moves the mouse * or presses a key during the microsecond that we exist. * * Wait! In fact, this function is *never* called. We * do not process messages at any point the hook is installed, * so in fact nothing happens at all. * * @parm int | nCode | * * Hook code. * * @parm WPARAM | wp | * * Hook-specific code. * * @parm LPARAM | lp | * * Hook-specific code. * * @returns * * Always chains to previous hook. * *****************************************************************************/
LRESULT CALLBACK DllLlHookTest(int nCode, WPARAM wp, LPARAM lp) { /*
* Note that there is not actually anything wrong here, * but it is a theoretically impossible condition, so I want to * know when it happens. */ AssertF(!TEXT("DllLlHookTest - Unexpected hook")); return CallNextHookEx(g_hhkLLHookCheck, nCode, wp, lp); }
#endif
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllProcessAttach | * * Called when the DLL is loaded. * * We are not interested in thread attaches and detaches, * so we disable thread notifications for performance reasons. * *****************************************************************************/
#pragma BEGIN_CONST_DATA
TCHAR c_tszKernel32[] = TEXT("KERNEL32");
#ifndef WINNT
char c_szOpenVxDHandle[] = "OpenVxDHandle"; #endif
void INTERNAL DllProcessAttach(void) { HINSTANCE hinstK32; #ifdef DEBUG
WriteProfileString(0, 0, 0); /* Flush the win.ini cache */ Sqfl_Init(); GetProfileString(TEXT("DEBUG"), TEXT("LogFile"), TEXT(""), g_tszLogFile, cA(g_tszLogFile)); SquirtSqflPtszV(sqfl, TEXT("LoadDll - DInput")); SquirtSqflPtszV(sqfl, TEXT("Version %x"), DIRECTINPUT_VERSION ); SquirtSqflPtszV(sqfl, TEXT("Built %s at %s\n"), TEXT(__DATE__), TEXT(__TIME__) ); #endif
/*
* Disabling thread library calls is important so that * we don't deadlock with ourselves over the critical * section when we spin up the worker thread to handle * low-level hooks. */ DisableThreadLibraryCalls(g_hinst);
g_fCritInited = fInitializeCriticalSection(&g_crstDll); if ( !g_fCritInited ) { RPF( "Failed to initialize DLL critical section" ); }
hinstK32 = GetModuleHandle( c_tszKernel32 );
{ CANCELIO tmp;
tmp = (CANCELIO)GetProcAddress(hinstK32, "CancelIo"); if (tmp) { _CancelIO = tmp; } else { AssertF(_CancelIO == FakeCancelIO); } }
#ifdef WINNT
/*
* For now, only look for TryEnterCriticalSection on NT as it is not * implemented on 9x but the stub is annoying on 98 with dbg kernels. */ { #ifdef XDEBUG
TRYENTERCRITICALSECTION tmpCrt;
tmpCrt = (TRYENTERCRITICALSECTION)GetProcAddress(hinstK32, "TryEnterCriticalSection"); if (tmpCrt) { _TryEnterCritSec = tmpCrt; } else { AssertF(_TryEnterCritSec == FakeTryEnterCriticalSection); } #endif
}
#else
_OpenVxDHandle = (OPENVXDHANDLE)GetProcAddress(hinstK32, c_szOpenVxDHandle); #endif
#ifdef WORKER_THREAD
{ MSGWAITFORMULTIPLEOBJECTSEX tmp;
tmp = (MSGWAITFORMULTIPLEOBJECTSEX) GetProcAddress(GetModuleHandle(TEXT("USER32")), "MsgWaitForMultipleObjectsEx"); if (tmp) { _MsgWaitForMultipleObjectsEx = tmp; } else { AssertF(_MsgWaitForMultipleObjectsEx == FakeMsgWaitForMultipleObjectsEx); } }
/*
* We cannot initialize g_hmtxGlobal here, because we * have no way to report the error back to the caller. */ #endif
#ifdef USE_SLOW_LL_HOOKS
/*
* Determine whether low-level input hooks are supported. */ g_hhkLLHookCheck = SetWindowsHookEx(WH_MOUSE_LL, DllLlHookTest, g_hinst, 0); if (g_hhkLLHookCheck) { UnhookWindowsHookEx(g_hhkLLHookCheck); }
#endif
/*
* Warning! Do not call ExtDll_Init during PROCESS_ATTACH! */ g_wmJoyChanged = RegisterWindowMessage(MSGSTR_JOYCHANGED); g_wmDInputNotify = RegisterWindowMessage(DIRECTINPUT_NOTIFICATION_MSGSTRING);
#ifdef USE_WM_INPUT
g_fRawInput = (DIGetOSVersion() == WINWH_OS); if( g_fRawInput ) { g_hEventAcquire = CreateEvent(0x0, 0, 0, 0x0); g_hEventThread = CreateEvent(0x0, 0, 0, 0x0); g_hEventHid = CreateEvent(0x0, 0, 0, 0x0); } #endif
}
/*****************************************************************************
* * @doc INTERNAL * * @func void | DllProcessDetach | * * Called when the DLL is unloaded. * *****************************************************************************/
void INTERNAL DllProcessDetach(void) { extern PLLTHREADSTATE g_plts;
SquirtSqflPtszV(sqfl | sqflMajor, TEXT("DINPUT8: ProcessDetach. PID:%08x, TID:%08x."), GetCurrentProcessId(), GetCurrentThreadId() );
#ifdef USE_WM_INPUT
if (g_hEventAcquire) { CloseHandle(g_hEventAcquire); }
if (g_hEventThread) { CloseHandle(g_hEventThread); }
if (g_hEventHid) { CloseHandle(g_hEventHid); } #endif
#ifndef WINNT
if (g_hVxD != INVALID_HANDLE_VALUE) { CloseHandle(g_hVxD); } #endif
if (g_psop) { UnmapViewOfFile(g_psop); }
if (g_hfm) { CloseHandle(g_hfm); }
if (g_hmtxGlobal) { CloseHandle(g_hmtxGlobal); }
if (g_hmtxJoy) { CloseHandle(g_hmtxJoy); }
FreePpv( &g_pdto ); FreePpv( &g_rgdwCRCTable );
ExtDll_Term();
if ( g_fCritInited ) { DeleteCriticalSection(&g_crstDll); }
if ( g_hinstDbg ) { FreeLibrary(g_hinstDbg); } /*
* Output message last so that anything that follows is known to be bad. */ if (g_cRef ) { RPF("unloaded before all objects released. (cRef:%d)\r\n", g_cRef); }
}
/*****************************************************************************
* * @doc INTERNAL * * @func BOOL | DllMain | * * Called to notify the DLL about various things that can happen. * * @parm HINSTANCE | hinst | * * The instance handle of this DLL. * * @parm DWORD | dwReason | * * Notification code. * * @parm LPVOID | lpReserved | * * Not used. * * @returns * * Returns <c TRUE> to allow the DLL to load. * *****************************************************************************/
BOOL APIENTRY DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: g_hinst = hinst; DllProcessAttach(); SquirtSqflPtszV(sqfl | sqflMajor, TEXT("DINPUT8: DLL_PROCESS_ATTACH hinst=0x%p, lpReserved=0x%p"), hinst, lpReserved ); break;
case DLL_PROCESS_DETACH: DllProcessDetach(); SquirtSqflPtszV(sqfl | sqflMajor, TEXT("DINPUT8: DLL_PROCESS_DETACH hinst=0x%p, lpReserved=0x%p"), hinst, lpReserved ); break; } return 1; }
/*****************************************************************************
* * @doc EXTERNAL * * @topic Definitions and Ground Rules | * * The phrase "undefined behavior" refers to behavior which is not * covered by this specification due to violations of a constraint. * No constraint is imposed by the specification as to the result of * undefined behavior. It may range from silently ignoring the * situation to a complete system crash. * * If this specification does not prescribe a behavior for a particular * situation, the behavior is "undefined". * * The phrase "It is an error" indicates that failure to comply * is a violation of the DirectInput specification and results * in "undefined behavior". * * The word "shall" is to be interpreted as a * requirement on an application; conversely, "shall not" is to be * interpreted as a prohibition. Violation of a requirement or * prohibition "is an error". * * The word "may" indicates that the indicated behavior is possible * but is not required. * * The word "should" indicates a strong suggestion. * If the application violates a "should" requirement, then DirectInput * "may" fail the operation. * * Pointer parameters to functions "shall not" be NULL unless explicitly * documented as OPTIONAL. "It is an error" to pass a pointer to an object * of the wrong type, to an object which is not allocated, or to an * object which has been freed or <f Release>d. * * Unless indicated otherwise, * an object pointed to by a pointer parameter documented as an * IN parameter "shall not" be modified by the called procedure. * Conversely, a pointer parameter documented * as an OUT parameter "shall" point to a modifiable object. * * When a bitmask of flags is defined, all bits not defined by this * specification are reserved. Applications "shall not" set reserved * bits and "shall" ignore reserved bits should they be received. * *****************************************************************************/
/*****************************************************************************
* * @doc EXTERNAL * * @topic Initialization and Versions | * * In several places, DirectInput requires you to pass an instance * handle and a version number. * * The instance handle must correspond to the application or * DLL that is initializing the DirectInput object. * * DirectInput uses this value to determine whether the * application or DLL has been certified and to establish * any special behaviors that may be necessary for * backwards-compatibility. * * It is an error for a DLL to pass the handle of the * parent application. For example, an ActiveX control * embedded in a web page which uses DirectInput must * pass its own instance handle and not the handle of the * web browser. This ensures that DirectInput recognizes * the control and can enable any special behaviors * for the control the behave properly. * * The version number parameter specifies which version of * DirectInput the DirectInput subsystem should emulate. * * Applications designed for the latest version of DirectInput * should pass the value <c DIRECTINPUT_VERSION> as defined * in dinput.h. * * Applications designed for previous versions of DirectInput * should pass a value corresponding to the version of * DirectInput they were designed for. For example, an * application that was designed to run on DirectInput 3.0 * should pass a value of 0x0300. * * If you #define <c DIRECTINPUT_VERSION> to 0x0300 before * including the dinput.h header file, then the dinput.h * header file will generate DirectInput 3.0-compatible * structure definitions. * *****************************************************************************/
/*****************************************************************************
* * @doc INTERNAL * * @func HRESULT | DirectInputCreateHelper | * * This function creates a new DirectInput object * which supports the <i IDirectInput> COM interface. * * On success, the function returns a pointer to the new object in * *<p lplpDirectInput>. * * @parm IN HINSTANCE | hinst | * * Instance handle of the application or DLL that is creating * the DirectInput object. * * @parm DWORD | dwVersion | * * Version number of the dinput.h header file that was used. * * @parm OUT PPV | ppvObj | * Points to where to return * the pointer to the <i IDirectInput> interface, if successful. * * @parm IN LPUNKNOWN | punkOuter | Pointer to controlling unknown. * * @parm RIID | riid | * * The interface the application wants to create. This will * be either <i IDirectInputA> or <i IDirectInputW>. * If the object is aggregated, then this parameter is ignored. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> * * <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: * Out of memory. * *****************************************************************************/
STDMETHODIMP DirectInputCreateHelper(HINSTANCE hinst, DWORD dwVer, PPV ppvObj, PUNK punkOuter, RIID riid) { HRESULT hres; EnterProc(DirectInputCreateHelper, (_ "xxxG", hinst, dwVer, punkOuter, riid));
if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 3))) { if( g_fCritInited ) { LPVOID pvTry = NULL; hres = CDIObj_New(punkOuter, punkOuter ? &IID_IUnknown : riid, &pvTry);
if( SUCCEEDED( hres ) ) { AhAppRegister( dwVer, 0x0 ); } if (SUCCEEDED(hres) && punkOuter == 0) { LPDIRECTINPUT pdi = pvTry; hres = pdi->lpVtbl->Initialize(pdi, hinst, dwVer); if (SUCCEEDED(hres)) { *ppvObj = pvTry; } else { Invoke_Release(&pvTry); *ppvObj = NULL; } } } else { RPF( "Failing DirectInputCreate due to lack of DLL critical section" ); hres = E_OUTOFMEMORY; } }
ExitOleProcPpv(ppvObj); return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @func HRESULT | DirectInput8Create | * * <bnew> This function creates a new DirectInput8 object * which supports the <i IDirectInput8> COM interfaces. This function * allows the app to pass an IID so it does not have to do an extra * QI off some initial interface in order to obtain the one it * really wanted. * * On success, the function returns a pointer to the new object in * *<p ppvOut>. * <enew> * * @parm IN HINSTANCE | hinst | * * Instance handle of the application or DLL that is creating * the DirectInput object. * * See the section titled "Initialization and Versions" * for more information. * * @parm DWORD | dwVersion | * * Version number of the dinput.h header file that was used. * * See the section titled "Initialization and Versions" * for more information. * * @parm REFIID | riidtlf | * * The desired interface interface. Currently, valid values are * IID_IDirectInput8A and IID_IDirectInput8W. * * @parm OUT LPVOID | *ppvOut | * * Points to where to return * the pointer to the <i IDirectInput8> interface, if successful. * * @parm IN LPUNKNOWN | punkOuter | Pointer to controlling unknown * for OLE aggregation, or 0 if the interface is not aggregated. * Most callers will pass 0. * * Note that if aggregation is requested, the object returned * in *<p lplpDirectInput> will be a pointer to an * <i IUnknown> rather than an <i IDirectInput8>, as required * by OLE aggregation. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The * <p lplpDirectInput> parameter is not a valid pointer. * * <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: * Out of memory. * * <c DIERR_DIERR_OLDDIRECTINPUTVERSION>: The application * requires a newer version of DirectInput. * * <c DIERR_DIERR_BETADIRECTINPUTVERSION>: The application * was written for an unsupported prerelease version * of DirectInput. * * @comm Calling this function with <p punkOuter> = NULL * is equivalent to creating the object via * <f CoCreateInstance>(&CLSID_DirectInput8, <p punkOuter>, * CLSCTX_INPROC_SERVER, <p refiid>, <p lplpDirectInput>); * then initializing it with <f Initialize>. * * Calling this function with <p punkOuter> != NULL * is equivalent to creating the object via * <f CoCreateInstance>(&CLSID_DirectInput8, <p punkOuter>, * CLSCTX_INPROC_SERVER, &IID_IUnknown, <p lplpDirectInput>). * The aggregated object must be initialized manually. * * Since the IID is passed, there is no need for separate ANSI and * UNICODE versions of this service however as with other system * services which are sensitive to character set issues, * macros in the header file map a generic version of the IID name * to the appropriate IID for the character set variation. * *****************************************************************************/
STDMETHODIMP DirectInput8Create(HINSTANCE hinst, DWORD dwVer, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter) { HRESULT hres = E_NOINTERFACE;
#ifndef XDEBUG
TCHAR tszGuid[ctchNameGuid]; TCHAR tszKey[ctchGuid + 40]; /* 40 is more than enough */ ULONG lRc; HKEY hk; #endif
EnterProc(DirectInput8Create, (_ "xxGx", hinst, dwVer, riidltf, ppvOut, punkOuter));
/* Need to maintain a refcount to keep the Dll Around */ DllAddRef();
#ifndef XDEBUG
/*
* To provide the Dx CPL with a mechanism to allow developers to hot swap dinput8 * retail and debug bits * In the retail version, we will check the CLSID//"CLSID_DirectInput8"/InProcServer32
* If it is found that the last n characters of the Dll name matches "Dinput8D.dll" then * we will LoadLibrary Dinput8.dll and call the DirectInput8Create function in dinput8d.dll * */ // Convert guid to name
NameFromGUID(tszGuid, &CLSID_DirectInput8); // The NameFromGUID function adds a prefix, which is easily gotten rid of
wsprintf(tszKey, TEXT("CLSID\\%s\\InProcServer32"), tszGuid+ctchNamePrefix); // Open the CLSID registry key
lRc = RegOpenKeyEx(HKEY_CLASSES_ROOT, tszKey, 0, KEY_QUERY_VALUE, &hk); if (lRc == ERROR_SUCCESS) { TCHAR tszDll[MAX_PATH]; int cb; cb = cbX(tszDll); lRc = RegQueryValue(hk, 0, tszDll, &cb); if (lRc == ERROR_SUCCESS) { // We are only interested in the last few TCHARs for our comparison
cb = lstrlen(tszDll ) - lstrlen(TEXT("dinput8d.dll")); if ( ( cb >= 0x0 ) && ( 0x0 == lstrcmpi(TEXT("dinput8d.dll"), &tszDll[cb])) ) { // We should use the debug binary
g_hinstDbg = LoadLibrary(tszDll); if (g_hinstDbg) { typedef HRESULT ( WINAPI * DIRECTINPUTCREATE8) ( HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter); DIRECTINPUTCREATE8 _DirectInputCreate8;
AssertF(g_hinstDbg != g_hinst ); _DirectInputCreate8 = (DIRECTINPUTCREATE8)GetProcAddress( g_hinstDbg, "DirectInput8Create"); if ( _DirectInputCreate8 && g_hinstDbg != g_hinst ) { hres = _DirectInputCreate8(hinst, dwVer, riidltf, ppvOut, punkOuter); } } } } RegCloseKey(hk); }
if( hres == E_NOINTERFACE) #endif /* XDEBUG */
//Can't create an IDirectInputJoyConfig8 interface through this mechanism, only IDirectInput8 interface!
if (!IsEqualIID(riidltf, &IID_IDirectInputJoyConfig8)) { hres = DirectInputCreateHelper(hinst, dwVer, ppvOut, punkOuter, riidltf); }
DllRelease();
ExitOleProcPpv(ppvOut); return hres; }
|