1362 lines
39 KiB
C
1362 lines
39 KiB
C
/*****************************************************************************
|
|
*
|
|
* 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;
|
|
}
|