windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/dx7/dll/diobj.c
2020-09-26 16:20:57 +08:00

1559 lines
46 KiB
C

/*****************************************************************************
*
* DIObj.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The IDirectInput main interface.
*
* Contents:
*
* CDIObj_New
*
*****************************************************************************/
#include "dinputpr.h"
#include "verinfo.h" /* For #ifdef FINAL */
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflDi
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct CDIObj |
*
* The <i IDirectInput> object, from which other things come.
*
* The A and W versions are simply alternate interfaces on the same
* underlying object.
*
* There really isn't anything interesting in the structure
* itself.
*
*
* @field IDirectInputA | diA |
*
* ANSI DirectInput object (containing vtbl).
*
* @field IDirectInputW | diW |
*
* UNICODE DirectInput object (containing vtbl).
*
* @field IDirectInputJoyConfig *| pdjc |
*
* Aggregated joystick configuration interface (if created).
*
* @field BOOL | fCritInited:1 |
*
* Set if the critical section has been initialized.
*
* @field CRITICAL_SECTION | crst |
*
* Critical section that guards thread-sensitive data.
*****************************************************************************/
typedef struct CDIObj {
/* Supported interfaces */
TFORM(IDirectInput) TFORM(di);
SFORM(IDirectInput) SFORM(di);
DWORD dwVersion;
IDirectInputJoyConfig *pdjc;
BOOL fCritInited:1;
CRITICAL_SECTION crst;
} CDIObj, DDI, *PDDI;
#define ThisClass CDIObj
#ifdef IDirectInput7Vtbl
#define ThisInterface TFORM(IDirectInput7)
#define ThisInterfaceA IDirectInput7A
#define ThisInterfaceW IDirectInput7W
#define ThisInterfaceT IDirectInput7
#else
#ifdef IDirectInput2Vtbl
#define ThisInterface TFORM(IDirectInput2)
#define ThisInterfaceA IDirectInput2A
#define ThisInterfaceW IDirectInput2W
#define ThisInterfaceT IDirectInput2
#else
#define ThisInterface TFORM(IDirectInput)
#define ThisInterfaceA IDirectInputA
#define ThisInterfaceW IDirectInputW
#define ThisInterfaceT IDirectInput
#endif
#endif
/*****************************************************************************
*
* Declare the interfaces we will be providing.
*
*****************************************************************************/
Primary_Interface(CDIObj, TFORM(ThisInterfaceT));
Secondary_Interface(CDIObj, SFORM(ThisInterfaceT));
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | 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 <mf IUnknown::QueryInterface>.
*
*//**************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | AddRef |
*
* Increments the reference count for the interface.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::AddRef>.
*
*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | 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 <mf IUnknown::Release>.
*
*****************************************************************************/
#ifdef DEBUG
Default_QueryInterface(CDIObj)
Default_AddRef(CDIObj)
Default_Release(CDIObj)
#else
#define CDIObj_QueryInterface Common_QueryInterface
#define CDIObj_AddRef Common_AddRef
#define CDIObj_Release Common_Release
#endif
#define CDIObj_AppFinalize Common_AppFinalize
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc void | CDIObj | EnterCrit |
*
* Enter the object critical section.
*
* @doc INTERNAL
*
* @mfunc void | CDIObj | LeaveCrit |
*
* Leave the object critical section.
*
*****************************************************************************/
void INLINE
CDIObj_EnterCrit(PDDI this)
{
EnterCriticalSection(&this->crst);
}
void INLINE
CDIObj_LeaveCrit(PDDI this)
{
LeaveCriticalSection(&this->crst);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc HRESULT | IDirectInput | QIHelper |
*
* We will dynamically create <i IDirectInputJoyConfig>
* and aggregate it with us.
*
#ifdef IDirectInput2Vtbl
* Support the original IDirectInput interfaces as well
* as the new IDirectInput2 interfaces.
#endif
* @parm IN REFIID | riid |
*
* The requested interface's IID.
*
* @parm OUT LPVOID * | ppvObj |
*
* Receives a pointer to the obtained interface.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_QIHelper(PDDI this, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcI(CDIObj_QIHelper, (_ "pG", this, riid));
if (IsEqualIID(riid, &IID_IDirectInputJoyConfig)) {
*ppvObj = 0; /* In case the New fails */
CDIObj_EnterCrit(this);
if (this->pdjc == 0) {
hres = CJoyCfg_New((PUNK)this, &IID_IUnknown, (PPV)&this->pdjc);
} else {
hres = S_OK;
}
CDIObj_LeaveCrit(this);
if (SUCCEEDED(hres)) {
/*
* This QI will addref us if it succeeds.
*/
hres = OLE_QueryInterface(this->pdjc, riid, ppvObj);
} else {
this->pdjc = 0;
}
#ifdef IDirectInput2Vtbl
} else if (IsEqualIID(riid, &IID_IDirectInputA)) {
*ppvObj = &this->diA;
OLE_AddRef(this);
hres = S_OK;
} else if (IsEqualIID(riid, &IID_IDirectInputW)) {
*ppvObj = &this->diW;
OLE_AddRef(this);
hres = S_OK;
#endif
#ifdef IDirectInput7Vtbl
} else if (IsEqualIID(riid, &IID_IDirectInput2A)) {
*ppvObj = &this->diA;
OLE_AddRef(this);
hres = S_OK;
} else if (IsEqualIID(riid, &IID_IDirectInput2W)) {
*ppvObj = &this->diW;
OLE_AddRef(this);
hres = S_OK;
#endif //IDirectInput7Vtbl
} else {
hres = Common_QIHelper(this, riid, ppvObj);
}
ExitOleProcPpv(ppvObj);
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CDIObj_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
CDIObj_Finalize(PV pvObj)
{
PDDI this = pvObj;
Invoke_Release(&this->pdjc);
if (this->fCritInited) {
DeleteCriticalSection(&this->crst);
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInput | CreateDeviceHelper |
*
* Creates and initializes an instance of a device which is
* specified by the GUID and IID.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm IN PCGUID | pguid |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm OUT PPV | ppvObj |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm IN LPUNKNOWN | punkOuter |
*
* See <mf IDirectInput::CreateDevice>.
*
* @parm IN RIID | riid |
*
* The interface the application wants to create. This will
* be either <i IDirectInputDeviceA> or <i IDirectInputDeviceW>.
* If the object is aggregated, then this parameter is ignored.
*
* @returns
*
* Returns a COM error code.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_CreateDeviceHelper(PDDI this, PCGUID pguid, PPV ppvObj,
PUNK punkOuter, RIID riid)
{
HRESULT hres;
EnterProc(CDIObj_CreateDeviceHelper,
(_ "pGxG", this, pguid, punkOuter, riid));
/*
* CDIDev_New will validate the punkOuter and ppvObj.
*
* IDirectInputDevice_Initialize will validate the pguid.
*
* riid is known good (since it came from CDIObj_CreateDeviceW
* or CDIObj_CreateDeviceA).
*/
hres = CDIDev_New(punkOuter, punkOuter ? &IID_IUnknown : riid, ppvObj);
if (SUCCEEDED(hres) && punkOuter == 0) {
PDID pdid = *ppvObj;
hres = IDirectInputDevice_Initialize(pdid, g_hinst,
this->dwVersion, pguid);
if (SUCCEEDED(hres)) {
} else {
Invoke_Release(ppvObj);
}
}
ExitOleProcPpv(ppvObj);
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | CreateDevice |
*
* Creates and initializes an instance of a device which is
* specified by the GUID and IID.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm REFGUID | rguid |
* Identifies the instance of the
* device for which the indicated interface
* is requested. The <mf IDirectInput::EnumDevices> method
* can be used to determine which instance GUIDs are supported by
* the system.
*
* @parm OUT LPDIRECTINPUTDEVICE * | lplpDirectInputDevice |
* Points to where to return
* the pointer to the <i IDirectInputDevice> 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.
*
* @comm Calling this function with <p punkOuter> = NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, NULL,
* CLSCTX_INPROC_SERVER, <p riid>, <p lplpDirectInputDevice>);
* then initializing it with <f Initialize>.
*
* Calling this function with <p punkOuter> != NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, <p punkOuter>,
* CLSCTX_INPROC_SERVER, &IID_IUnknown, <p lplpDirectInputDevice>).
* The aggregated object must be initialized manually.
*
* @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 ppvOut> parameter is not a valid pointer.
*
* <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>:
* Out of memory.
*
* <c DIERR_NOINTERFACE> = <c E_NOINTERFACE>
* The specified interface is not supported by the object.
*
* <c DIERR_DEVICENOTREG> = The device instance does not
* correspond to a device that is registered with DirectInput.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_CreateDeviceW(PV pdiW, REFGUID rguid, PPDIDW ppdidW, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput::CreateDevice,
(_ "pGp", pdiW, rguid, punkOuter));
if (SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW))) {
PDDI this = _thisPvNm(pdiW, diW);
hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidW,
punkOuter, &IID_IDirectInputDeviceW);
}
ExitOleProcPpv(ppdidW);
return hres;
}
STDMETHODIMP
CDIObj_CreateDeviceA(PV pdiA, REFGUID rguid, PPDIDA ppdidA, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput::CreateDevice,
(_ "pGp", pdiA, rguid, punkOuter));
if (SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA))) {
PDDI this = _thisPvNm(pdiA, diA);
hres = CDIObj_CreateDeviceHelper(this, rguid, (PPV)ppdidA,
punkOuter, &IID_IDirectInputDeviceA);
}
ExitOleProcPpv(ppdidA);
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | CreateDeviceEx |
*
* Creates and initializes an instance of a device which is
* specified by the GUID. CreateDeviceEx allows an app to
* directly create a IID_IDirectInputDevice7 interface without
* going through a CreateDevice() and QI the interface
* for an IID_IDirectInput7.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm REFGUID | rguid |
* Identifies the instance of the
* device for which the indicated interface
* is requested. The <mf IDirectInput::EnumDevices> method
* can be used to determine which instance GUIDs are supported by
* the system.
*
* @parm REFIID | riid |
* Identifies the REFIID for the interface. Currently accepted values
* are IID_IDirectInputDevice, IID_IDirectInputDevice2, IID_IDirectInputDevice7.
*
*
* @parm OUT LPVOID * | pvOut |
* Points to where to return
* the pointer to the <i IDirectInputDevice#> 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.
*
* @comm Calling this function with <p punkOuter> = NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, NULL,
* CLSCTX_INPROC_SERVER, <p riid>, <p lplpDirectInputDevice>);
* then initializing it with <f Initialize>.
*
* Calling this function with <p punkOuter> != NULL
* is equivalent to creating the object via
* <f CoCreateInstance>(&CLSID_DirectInputDevice, <p punkOuter>,
* CLSCTX_INPROC_SERVER, &IID_IUnknown, <p lplpDirectInputDevice>).
* The aggregated object must be initialized manually.
*
* @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 ppvOut> parameter is not a valid pointer.
*
* <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>:
* Out of memory.
*
* <c DIERR_NOINTERFACE> = <c E_NOINTERFACE>
* The specified interface is not supported by the object.
*
* <c DIERR_DEVICENOTREG> = The device instance does not
* correspond to a device that is registered with DirectInput.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_CreateDeviceExW(PV pdiW, REFGUID rguid, REFIID riid, LPVOID * pvOut, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput::CreateDeviceEx,
(_ "pGGp", pdiW, rguid, riid, punkOuter));
if(SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)))
{
PDDI this = _thisPvNm(pdiW, diW);
hres = CDIObj_CreateDeviceHelper(this, rguid, pvOut,
punkOuter, riid);
}
ExitOleProcPpv(pvOut);
return hres;
}
STDMETHODIMP
CDIObj_CreateDeviceExA(PV pdiA, REFGUID rguid, REFIID riid, LPVOID * pvOut, PUNK punkOuter)
{
HRESULT hres;
EnterProcR(IDirectInput::CreateDevice,
(_ "pGp", pdiA, rguid, riid, punkOuter));
if(SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)))
{
PDDI this = _thisPvNm(pdiA, diA);
hres = CDIObj_CreateDeviceHelper(this, rguid, pvOut,
punkOuter, riid);
}
ExitOleProcPpv(pvOut);
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIObj_TestDeviceFlags |
*
* Determines whether the device matches the specified flags.
* Phantom devices are treated as not really there.
*
* @parm PDIDW | pdidW |
*
* Device to be queried.
*
* @parm DWORD | edfl |
*
* Enumeration flags. It is one or more <c DIEDFL_*> values.
*
* The bits in the enumeration flags are in two categories.*
*
* Normal flags are the ones whose presence requires that
* the corresponding bit in the device flags also be set.
*
* Inverted flags (<c DIEDFL_INCLUDEMASK>) are the ones whose
* absence requires that the corresponding bit in the device
* flags also be absent.
*
* By inverting the inclusion flags in both the enumeration
* flags and the actual device flags, and then treating the
* whole thing as a bunch of normal flags, we get the desired
* behavior for the inclusion flags.
*
* @returns
*
* <c S_OK> if the device meets the criteria.
*
* <c S_FALSE> if the device does not meet the criteria.
* Note that <mf DirectInput::GetDeviceStatus> relies on
* this specific return value.
*
* Other error code as appropriate.
*
*****************************************************************************/
HRESULT EXTERNAL
CDIObj_TestDeviceFlags(PDIDW pdidW, DWORD edfl)
{
HRESULT hres;
DIDEVCAPS_DX3 dc;
EnterProcI(CDIObj_TestDeviceFlags, (_ "px", pdidW, edfl));
/*
* We intentionally use a DIDEVCAPS_DX3 because going for
* a full DIDEVCAPS_DX5 requires us to load the force
* feedback driver which is pointless for our current
* goal.
*/
dc.dwSize = cbX(dc);
hres = IDirectInputDevice_GetCapabilities(pdidW, (PV)&dc);
AssertF(dc.dwSize == cbX(dc));
CAssertF(DIEDFL_ATTACHEDONLY == DIDC_ATTACHED);
CAssertF(DIEDFL_FORCEFEEDBACK == DIDC_FORCEFEEDBACK);
CAssertF(DIEDFL_INCLUDEALIASES == DIDC_ALIAS);
CAssertF(DIEDFL_INCLUDEPHANTOMS == DIDC_PHANTOM);
if (SUCCEEDED(hres)) {
if (fHasAllBitsFlFl(dc.dwFlags ^ DIEDFL_INCLUDEMASK,
edfl ^ DIEDFL_INCLUDEMASK)) {
hres = S_OK;
} else {
/*
* Note: DX3 and DX5 returned E_DEVICENOTREG for
* phantom devices. Now we return S_FALSE. Let's
* hope nobody gets upset.
*/
hres = S_FALSE;
}
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | EnumDevices |
*
* Enumerates the DirectInput devices that are attached to
* or could be attached to the computer.
*
* For example, an external game port may support a joystick
* or a steering wheel, but only one can be plugged in at a
* time. <mf IDirectInput::EnumDevices> will enumerate both
* devices.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm DWORD | dwDevType |
*
* Device type filter. If 0, then all device types are
* enumerated. Otherwise, it is a <c DIDEVTYPE_*> value,
* indicating the device type that should be enumerated.
*
* @parm LPDIENUMDEVICESCALLBACK | lpCallback |
* Points to an application-defined callback function.
* For more information, see the description of the
* <f DIEnumDevicesProc> callback function.
*
* @parm IN LPVOID | pvRef |
* Specifies a 32-bit application-defined
* value to be passed to the callback function. This value
* may be any 32-bit value; it is prototyped as an <t LPVOID>
* for convenience.
*
* @parm DWORD | fl |
* Optional flags which control the enumeration. The
* following flags are defined and may be combined.
*
* <c DIEDFL_ATTACHEDONLY>: Enumerate only attached devices.
*
* <c DIEDFL_FORCEFEEDBACK>: Enumerate only devices which
* support force feedback. This flag is new for DirectX 5.0.
*
* <c DIEDFL_INCLUDEALIASES>: Include alias devices in the
* enumeration. If this flag is not specified, then devices
* which are aliases of other devices (indicated by the
* <c DIDC_ALIAS> flag in the <e DIDEVCAPS.dwFlags> field
* of the <t DIDEVCAPS> structure) will be excluded from
* the enumeration. This flag is new for DirectX 5.0a.
*
* <c DIEDFL_INCLUDEPHANTOMS>: Include phantom devices in the
* enumeration. If this flag is not specified, then devices
* which are phantoms (indicated by the
* <c DIDC_PHANTOM> flag in the <e DIDEVCAPS.dwFlags> field
* of the <t DIDEVCAPS> structure) will be excluded from
* the enumeration. This flag is new for DirectX 5.0a.
*
* The default is
* <c DIEDFL_ALLDEVICES>: Enumerate all installed devices.
*
* @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.
* Note that if the callback stops the enumeration prematurely,
* the enumeration is considered to have succeeded.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p fl> parameter contains invalid flags, or the callback
* procedure returned an invalid status code.
*
* @cb BOOL CALLBACK | DIEnumDevicesProc |
*
* An application-defined callback function that receives
* DirectInput devices as a result of a call to the
* <om IDirectInput::EnumDevices> method.
*
* @parm IN LPDIDEVICEINSTANCE | lpddi |
*
* Structure that describes the device instance.
*
*
* @parm IN OUT LPVOID | pvRef |
* Specifies the application-defined value given in the
* <mf IDirectInput::EnumDevices> function.
*
* @returns
*
* Returns <c DIENUM_CONTINUE> to continue the enumeration
* or <c DIENUM_STOP> to stop the enumeration.
*
*//**************************************************************************
*
* In DEBUG/RDEBUG, if the callback returns a bogus value, raise
* a validation exception.
*
*****************************************************************************/
HRESULT INLINE
CDIObj_EnumDevices_IsValidTypeFilter(DWORD dwDevType)
{
HRESULT hres;
/*
* First make sure the type mask is okay.
*/
if ((dwDevType & DIDEVTYPE_TYPEMASK) < DIDEVTYPE_MAX) {
/*
* Now make sure attribute masks are okay.
*/
if (dwDevType & DIDEVTYPE_ENUMMASK & ~DIDEVTYPE_ENUMVALID) {
RPF("IDirectInput::EnumDevices: Invalid dwDevType");
hres = E_INVALIDARG;
} else {
hres = S_OK;
}
} else {
RPF("IDirectInput::EnumDevices: Invalid dwDevType");
hres = E_INVALIDARG;
}
return hres;
}
STDMETHODIMP
CDIObj_EnumDevicesW(PV pdiW, DWORD dwDevType,
LPDIENUMDEVICESCALLBACKW pec, LPVOID pvRef, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInput::EnumDevices,
(_ "pxppx", pdiW, dwDevType, pec, pvRef, fl));
if (SUCCEEDED(hres = hresPvI(pdiW, ThisInterfaceW)) &&
SUCCEEDED(hres = hresFullValidPfn(pec, 2)) &&
SUCCEEDED(hres = CDIObj_EnumDevices_IsValidTypeFilter(dwDevType)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIEDFL_VALID, 4))) {
PDDI this = _thisPvNm(pdiW, diW);
if(SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion))) {
CDIDEnum *pde;
hres = CDIDEnum_New(&this->diW, dwDevType, fl, this->dwVersion, &pde);
if (SUCCEEDED(hres)) {
DIDEVICEINSTANCEW ddiW;
ddiW.dwSize = cbX(ddiW);
while ((hres = CDIDEnum_Next(pde, &ddiW)) == S_OK) {
BOOL fRc;
/*
* WARNING! "goto" here! Make sure that nothing
* is held while we call the callback.
*/
fRc = Callback(pec, &ddiW, pvRef);
switch (fRc) {
case DIENUM_STOP: goto enumdoneok;
case DIENUM_CONTINUE: break;
default:
RPF("%s: Invalid return value from callback", s_szProc);
ValidationException();
break;
}
}
AssertF(hres == S_FALSE);
enumdoneok:;
CDIDEnum_Release(pde);
hres = S_OK;
}
}
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIObj_EnumDevicesCallbackA |
*
* Wrapper function for <mf IDirectInput::EnumDevices>
* which translates the UNICODE parameters to ANSI.
*
* @parm IN LPCDIDECICEINSTANCEW | pdiW |
*
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm IN OUT PV | pvRef |
*
* Pointer to <t struct ENUMDEVICESINFO> which describes
* the original callback.
*
* @returns
*
* Returns whatever the original callback returned.
*
*****************************************************************************/
typedef struct ENUMDEVICESINFO {
LPDIENUMDEVICESCALLBACKA pecA;
PV pvRef;
} ENUMDEVICESINFO, *PENUMDEVICESINFO;
BOOL CALLBACK
CDIObj_EnumDevicesCallback(LPCDIDEVICEINSTANCEW pdiW, PV pvRef)
{
PENUMDEVICESINFO pedi = pvRef;
BOOL fRc;
DIDEVICEINSTANCEA diA;
EnterProc(CDIObj_EnumDevicesCallback,
(_ "GGxWWp", &pdiW->guidInstance, &pdiW->guidProduct,
&pdiW->dwDevType,
pdiW->tszProductName, pdiW->tszInstanceName,
pvRef));
diA.dwSize = cbX(diA);
DeviceInfoWToA(&diA, pdiW);
fRc = pedi->pecA(&diA, pedi->pvRef);
ExitProcX(fRc);
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputA | EnumDevices |
*
* ANSI version of <mf IDirectInput::EnumDevices>.
* We wrap the operation.
*
* @parm IN LPGUID | lpGUIDDeviceType |
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm LPDIENUMDEVICESCALLBACKA | lpCallbackA |
* Same as <mf IDirectInput::EnumDevices>, except ANSI.
*
* @parm IN LPVOID | pvRef |
* Same as <mf IDirectInput::EnumDevices>.
*
* @parm DWORD | fl |
* Same as <mf IDirectInput::EnumDevices>.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_EnumDevicesA(PV pdiA, DWORD dwDevType,
LPDIENUMDEVICESCALLBACKA pec, LPVOID pvRef, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInput::EnumDevices,
(_ "pxppx", pdiA, dwDevType, pec, pvRef, fl));
/*
* EnumDevicesW will validate the rest.
*/
if (SUCCEEDED(hres = hresPvI(pdiA, ThisInterfaceA)) &&
SUCCEEDED(hres = hresFullValidPfn(pec, 1))) {
ENUMDEVICESINFO edi = { pec, pvRef };
PDDI this = _thisPvNm(pdiA, diA);
hres = CDIObj_EnumDevicesW(&this->diW, dwDevType,
CDIObj_EnumDevicesCallback, &edi, fl);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | GetDeviceStatus |
*
* Determine whether a device is currently attached.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm REFGUID | rguid |
*
* Identifies the instance of the
* device whose status is being checked.
*
* @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 device is attached.
*
* <c DI_NOTATTACHED> = <c S_FALSE>: The device is not
* attached.
*
* <c E_FAIL>: DirectInput could not determine
* whether the device is attached.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* device does not exist.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_GetDeviceStatus(PV pdi, REFGUID rguid _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput::GetDeviceStatus, (_ "pG", pdi, rguid));
if (SUCCEEDED(hres = hresPvT(pdi))) {
PDDI this = _thisPv(pdi);
PDIDW pdidW;
hres = IDirectInput_CreateDevice(&this->diW, rguid, (PV)&pdidW, 0);
if (SUCCEEDED(hres)) {
hres = CDIObj_TestDeviceFlags(pdidW, DIEDFL_ATTACHEDONLY);
OLE_Release(pdidW);
}
}
ExitOleProc();
return hres;
}
#ifdef XDEBUG
CSET_STUBS(GetDeviceStatus, (PV pdi, REFGUID rguid), (pdi, rguid THAT_))
#else
#define CDIObj_GetDeviceStatusA CDIObj_GetDeviceStatus
#define CDIObj_GetDeviceStatusW CDIObj_GetDeviceStatus
#endif
#ifdef DO_THE_IMPOSSIBLE
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | SetAttachedDevice |
*
* Informs DirectInput that a new device has been attached
* to the system by the user. This is useful when an application
* asks the user to attach a currently installed device but does
* not want to launch the DirectInput control panel.
*
* DirectInput needs to be informed that the device has
* been attached for internal bookkeeping purposes.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm IN LPDIRECTINPUTDEVICE | lpDIDevice |
*
* Identifies the device which has been attached.
*
* @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 device is attached.
*
* @devnote
*
* This method is not implemented in the current release
* of DirectInput.
*
* This won't work. We need to receive a port, too.
* And how can the app create a <p lpDIDevice> in the
* first place for a device that does not exist?
* I guess I just don't understand.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_SetAttachedDevice(PV pdi, PV pdid _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput::SetAttachedDevice, (_ "pp", pdi, pdid));
if (SUCCEEDED(hres = hresPvT(pdi))) {
PDDI this = _thisPv(pdi);
hres = E_NOTIMPL;
}
ExitOleProc();
return hres;
}
#ifdef XDEBUG
CSET_STUBS(SetAttachedDevice, (PV pdi, PV pdid), (pdi, pdid THAT_))
#else
#define CDIObj_SetAttachedDeviceA CDIObj_SetAttachedDevice
#define CDIObj_SetAttachedDeviceW CDIObj_SetAttachedDevice
#endif
#endif
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | RunControlPanel |
*
* Run the DirectInput control panel so that the user can
* install a new input device or modify the setup.
*
* This function will not run third-party control panels.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @parm IN HWND | hwndOwner |
*
* Identifies the window handle that will be used as the
* parent window for subsequent UI. NULL is a valid parameter,
* indicating that there is no parent window.
*
* @parm DWORD | dwFlags |
*
* No flags are currently defined. This parameter "must" be
* zero.
*
* @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 device is attached.
*
* @devnote
*
* The <p dwFlags> is eventually going to allow
* <c DIRCP_MODAL> to request a modal control panel.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
STDMETHODIMP
CDIObj_RunControlPanel(PV pdi, HWND hwndOwner, DWORD fl _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput::RunControlPanel, (_ "pxx", pdi, hwndOwner, fl));
if (SUCCEEDED(hres = hresPvT(pdi)) &&
SUCCEEDED(hres = hresFullValidHwnd0(hwndOwner, 1)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIRCP_VALID, 2)) ) {
PDDI this = _thisPv(pdi);
if(SUCCEEDED(hres = hresValidInstanceVer(g_hinst, this->dwVersion))) {
/*
* We used to run "directx.cpl,@0,3" but directx.cpl is not
* redistributable; it comes only with the SDK. So we just
* run the system control panel.
*/
hres = hresRunControlPanel(TEXT(""));
}
}
ExitOleProc();
return hres;
}
#ifdef XDEBUG
CSET_STUBS(RunControlPanel, (PV pdi, HWND hwndOwner, DWORD fl),
(pdi, hwndOwner, fl THAT_))
#else
#define CDIObj_RunControlPanelA CDIObj_RunControlPanel
#define CDIObj_RunControlPanelW CDIObj_RunControlPanel
#endif
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput | Initialize |
*
* Initialize a DirectInput object.
*
* The <f DirectInputCreate> method automatically
* initializes the DirectInput object device after creating it.
* Applications normally do not need to call this function.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @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.
*
* @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 device is attached.
*
* <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.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_Initialize(PV pdi, HINSTANCE hinst, DWORD dwVersion _THAT)
{
HRESULT hres;
EnterProcR(IDirectInput::Initialize, (_ "pxx", pdi, hinst, dwVersion));
AhAppRegister(dwVersion);
if (SUCCEEDED(hres = hresPvT(pdi))) {
PDDI this = _thisPv(pdi);
if (SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion))) {
this->dwVersion = dwVersion;
}
}
#ifndef DX_FINAL_RELEASE
{
#pragma message("BETA EXPIRATION TIME BOMB! Remove for final build!")
SYSTEMTIME st;
GetSystemTime(&st);
if ( st.wYear > DX_EXPIRE_YEAR ||
((st.wYear == DX_EXPIRE_YEAR) && (MAKELONG(st.wDay, st.wMonth) > MAKELONG(DX_EXPIRE_DAY, DX_EXPIRE_MONTH)))
) {
MessageBox(0, DX_EXPIRE_TEXT,
TEXT("Microsoft DirectInput"), MB_OK);
}
}
#endif
ExitOleProc();
return hres;
}
#ifdef XDEBUG
CSET_STUBS(Initialize, (PV pdi, HINSTANCE hinst, DWORD dwVersion),
(pdi, hinst, dwVersion THAT_))
#else
#define CDIObj_InitializeA CDIObj_Initialize
#define CDIObj_InitializeW CDIObj_Initialize
#endif
#ifdef IDirectInput2Vtbl
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIObj_FindDeviceInternal |
*
* The worker function for
* <mf IDirectInput2::FindDevice> which works only for HID devices.
*
* For more details, see <mf IDirectInput2::FindDevice>.
*
* @parm LPCTSTR | ptszName |
*
* The name of the device relative to the class <t GUID>.
*
* @parm OUT LPGUID | pguidOut |
*
* Pointer to a <t GUID> which receives the instance
* <t GUID> for the device, if the device is found.
*
*****************************************************************************/
HRESULT EXTERNAL
CDIObj_FindDeviceInternal(LPCTSTR ptszName, LPGUID pguidOut)
{
HRESULT hres;
/*
* Look twice. If it's not found the first time,
* then refresh the cache and try again in case
* it was for a device that was recently added.
* (In fact, it will likely be a device that was
* recently added, because FindDevice is usually
* called in response to a Plug and Play event.)
*/
hres = hresFindHIDDeviceInterface(ptszName, pguidOut);
if (FAILED(hres)) {
DIHid_BuildHidList(TRUE);
hres = hresFindHIDDeviceInterface(ptszName, pguidOut);
}
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInput2 | FindDevice |
*
* Obtain the instance <t GUID> for a device given
* its class <t GUID> and an opaque name.
*
* This method can be used by applications which register
* for Plug and Play notifications and are notified by
* Plug and Play that a new device has been added
* to the system. The Plug and Play notification will
* be in the form of a class <t GUID> and a device name.
* The application can pass the <t GUID> and name to
* this method to obtain the instance <t GUID> for
* the device, which can then be passed to
* <mf IDirectInput::CreateDevice> or
* <mf IDirectInput::GetDeviceStatus>.
*
* @cwrap LPDIRECTINPUT2 | lpDirectInput2
*
* @parm REFGUID | rguidClass |
*
* Class <t GUID> identifying the device class
* for the device the application wishes to locate.
*
* The application obtains the class <t GUID> from the
* Plug and Play device arrival notification.
*
* @parm LPCTSTR | ptszName |
*
* The name of the device relative to the class <t GUID>.
*
* The application obtains the class name from the
* Plug and Play device arrival notification.
*
* @parm OUT LPGUID | pguidInstance |
*
* Pointer to a <t GUID> which receives the instance
* <t GUID> for the device, if the device is found.
*
* @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 device was found, and its
* instance <t GUID> has been stored in <p pguidInstance>.
*
* <c DIERR_DEVICENOTREG> = The <t GUID> and name do not
* correspond to a device that is registered with DirectInput.
* For example, they may refer to a storage device rather
* than an input device.
*
*****************************************************************************/
#define cchNameMax MAX_PATH
STDMETHODIMP
TFORM(CDIObj_FindDevice)(PV pdiT, REFGUID rguid,
LPCTSTR ptszName, LPGUID pguidOut)
{
HRESULT hres;
EnterProcR(IDirectInput2::FindDevice,
(_ "pGs", pdiT, rguid, ptszName));
if (SUCCEEDED(hres = TFORM(hresPv)(pdiT)) &&
SUCCEEDED(hres = hresFullValidGuid(rguid, 1)) &&
SUCCEEDED(hres = TFORM(hresFullValidReadStr)(ptszName,
cchNameMax, 2)) &&
SUCCEEDED(hres = hresFullValidWritePvCb(pguidOut, cbX(GUID), 3))) {
if (IsEqualIID(rguid, &GUID_HIDClass)) {
hres = CDIObj_FindDeviceInternal(ptszName, pguidOut);
} else {
hres = DIERR_DEVICENOTREG;
}
}
ExitOleProc();
return hres;
}
STDMETHODIMP
SFORM(CDIObj_FindDevice)(PV pdiS, REFGUID rguid,
LPCSSTR psszName, LPGUID pguidOut)
{
HRESULT hres;
TCHAR tsz[cchNameMax];
EnterProcR(IDirectInput2::FindDevice,
(_ "pGS", pdiS, rguid, psszName));
/*
* TFORM(CDIObj_FindDevice) will validate the rguid and pguidOut.
*/
if (SUCCEEDED(hres = SFORM(hresPv)(pdiS)) &&
SUCCEEDED(hres = SFORM(hresFullValidReadStr)(psszName, cA(tsz), 2))) {
PDDI this = _thisPvNm(pdiS, SFORM(di));
SToT(tsz, cA(tsz), psszName);
hres = TFORM(CDIObj_FindDevice)(&this->TFORM(di), rguid, tsz, pguidOut);
}
ExitOleProc();
return hres;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @mfunc HRESULT | IDirectInput | New |
*
* Create a new instance of an IDirectInput object.
*
* @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 <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP
CDIObj_New(PUNK punkOuter, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcR(IDirectInput::CreateInstance, (_ "Gp", riid, ppvObj));
hres = Excl_Init();
if (SUCCEEDED(hres)) {
/*
* Note that we cannot use Common_NewRiid for an object
* that aggregates other interfaces!
*
* The reason is that Common_NewRiid will perform
* a QI as part of the initialization, but we cannot handle
* the QI until after we've been initialized and are
* ready to mess with aggregated goo.
*/
if (SUCCEEDED(hres = hresFullValidRiid(riid, 2))) {
if (fLimpFF(punkOuter, IsEqualIID(riid, &IID_IUnknown))) {
hres = Common_New(CDIObj, punkOuter, ppvObj);
if (SUCCEEDED(hres)) {
PDDI this = _thisPv(*ppvObj);
this->fCritInited = fInitializeCriticalSection(&this->crst);
if( this->fCritInited )
{
/*
* Only after the object is ready do we QI for the
* requested interface. And the reason is that the
* QI might cause us to create an aggregated buddy,
* which we can't do until we've been initialized.
*
* Don't do this extra QI if we are ourselves aggregated,
* or we will end up giving the wrong punk to the caller!
*/
if (punkOuter == 0) {
hres = OLE_QueryInterface(this, riid, ppvObj);
OLE_Release(this);
}
if (FAILED(hres)) {
Invoke_Release(ppvObj);
}
}
else
{
Common_Unhold(this);
*ppvObj = NULL;
hres = E_OUTOFMEMORY;
}
}
} else {
RPF("CreateDevice: IID must be IID_IUnknown if created for aggregation");
*ppvObj = 0;
hres = CLASS_E_NOAGGREGATION;
}
}
}
ExitOleProcPpvR(ppvObj);
return hres;
}
/*****************************************************************************
*
* The long-awaited vtbls and templates
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
#define CDIObj_Signature 0x504E4944 /* "DINP" */
Interface_Template_Begin(CDIObj)
Primary_Interface_Template(CDIObj, TFORM(ThisInterfaceT))
Secondary_Interface_Template(CDIObj, SFORM(ThisInterfaceT))
Interface_Template_End(CDIObj)
Primary_Interface_Begin(CDIObj, TFORM(ThisInterfaceT))
TFORM(CDIObj_CreateDevice),
TFORM(CDIObj_EnumDevices),
TFORM(CDIObj_GetDeviceStatus),
TFORM(CDIObj_RunControlPanel),
TFORM(CDIObj_Initialize),
#ifdef IDirectInput2Vtbl
TFORM(CDIObj_FindDevice),
#ifdef IDirectInput7Vtbl
TFORM(CDIObj_CreateDeviceEx),
#endif
#endif
Primary_Interface_End(CDIObj, TFORM(ThisInterfaceT))
Secondary_Interface_Begin(CDIObj, SFORM(ThisInterfaceT), SFORM(di))
SFORM(CDIObj_CreateDevice),
SFORM(CDIObj_EnumDevices),
SFORM(CDIObj_GetDeviceStatus),
SFORM(CDIObj_RunControlPanel),
SFORM(CDIObj_Initialize),
#ifdef IDirectInput2Vtbl
SFORM(CDIObj_FindDevice),
#ifdef IDirectInput7Vtbl
SFORM(CDIObj_CreateDeviceEx),
#endif
#endif
Secondary_Interface_End(CDIObj, SFORM(ThisInterfaceT), SFORM(di))