Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1937 lines
53 KiB

/*****************************************************************************
*
* DIEm.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* DirectInput VxD emulation layer. (I.e., do the things that
* dinput.vxd normally does.) You may find large chunks of this
* code familiar: It's exactly the same thing that happens in
* the VxD.
*
* Contents:
*
* CEm_AcquireInstance
* CEm_UnacquireInstance
* CEm_SetBufferSize
* CEm_DestroyInstance
* CEm_SetDataFormat
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflEm
#define ThisClass CEm
#define CEM_SIGNATURE 0x4D4D4545 /* "EEMM" */
PEM g_pemFirst;
#ifdef WORKER_THREAD
PLLTHREADSTATE g_plts; /* The currently active input thread */
#ifdef USE_WM_INPUT
BOOL g_fFromKbdMse;
#endif
#endif /* WORKER_THREAD */
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_FreeInstance |
*
* It's really gone now.
*
* @parm PEM | this |
*
* The victim.
*
*****************************************************************************/
void EXTERNAL
CEm_FreeInstance(PEM this)
{
PEM *ppem;
EnterProc(CEm_FreeInstance, (_ "p", this));
AssertF(this->dwSignature == CEM_SIGNATURE);
AssertF(this->cRef == 0);
/*
* It is the owner's responsibility to unacquire before releasing.
*/
AssertF(!(this->vi.fl & VIFL_ACQUIRED));
/*
* If this device has a reference to a hook, then remove
* the reference.
*/
#ifdef WORKER_THREAD
if (this->fWorkerThread) {
PLLTHREADSTATE plts;
DWORD idThread;
/*
* Protect test and access of g_plts with DLLCrit
*/
DllEnterCrit();
plts = g_plts;
if (plts ) {
AssertF(plts->cRef);
/*
* Note that we need to keep the thread ID because
* the InterlockedDecrement might cause us to lose
* the object.
*
* Note that this opens a race condition where the
* thread might decide to kill itself before we
* post it the nudge message. That's okay, because
* even if the thread ID gets recycled, the message
* that appears is a dummy WM_NULL message that
* causes no harm.
*/
idThread = plts->idThread; /* Must save before we dec */
if( InterlockedDecrement(&plts->cRef) == 0 ) {
g_plts = 0;
}
}
DllLeaveCrit();
if( plts )
{
NudgeWorkerThread(idThread);
}
}
#endif
/*
* Unlink the node from the master list.
*/
DllEnterCrit();
for (ppem = &g_pemFirst; *ppem; ppem = &(*ppem)->pemNext) {
AssertF((*ppem)->dwSignature == CEM_SIGNATURE);
if (*ppem == this) {
*ppem = (*ppem)->pemNext;
break;
}
}
AssertF(ppem);
DllLeaveCrit();
FreePpv(&this->rgdwDf);
FreePpv(&this->vi.pBuffer);
if( InterlockedDecrement(&this->ped->cRef) == 0x0 )
{
FreePpv(&this->ped->pDevType);
}
D(this->dwSignature++);
FreePv(this);
ExitProc();
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_CreateInstance |
*
* Create a device thing.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* What the object should look like.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* The answer goes here.
*
* @parm PED | ped |
*
* Descriptor.
*
*****************************************************************************/
HRESULT EXTERNAL
CEm_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut, PED ped)
{
HRESULT hres;
EnterProc(CEm_CreateInstance, (_ "pp", pdevf, ped));
AssertF(pdevf->cbData == ped->cbData);
CAssertF(FIELD_OFFSET(CEm, vi) == 0);
hres = AllocCbPpv(cbX(CEm), ppviOut);
if (SUCCEEDED(hres)) {
PEM pem = (PV)*ppviOut;
D(pem->dwSignature = CEM_SIGNATURE);
pem->dwExtra = pdevf->dwExtra;
pem->ped = ped;
pem->cAcquire = -1;
/*
* Make sure these functions are inverses.
*/
AssertF(DIGETEMFL(DIMAKEEMFL(pdevf->dwEmulation)) ==
pdevf->dwEmulation);
pem->vi.fl = VIFL_EMULATED | DIMAKEEMFL(pdevf->dwEmulation);
pem->vi.pState = ped->pState;
CEm_AddRef(pem);
DllEnterCrit();
/*
* Build the devtype array. This consists of one dword
* for each byte in the data format.
*
* Someday: Do the button thing too.
*/
if (ped->pDevType == 0) {
hres = ReallocCbPpv(cbCdw(pdevf->cbData), &ped->pDevType);
if (SUCCEEDED(hres)) {
UINT iobj;
/*
* If HID is messed up, we will end up with
* entries whose dwType is zero (because HID
* said they existed, but when we went around
* enumerating, they never showed up).
*
* And don't put no-data items into the array!
*/
for (iobj = 0; iobj < pdevf->cObj; iobj++) {
if (pdevf->rgodf[iobj].dwType &&
!(pdevf->rgodf[iobj].dwType & DIDFT_NODATA)) {
ped->pDevType[pdevf->rgodf[iobj].dwOfs] =
pdevf->rgodf[iobj].dwType;
}
}
}
} else {
hres = S_OK;
}
if (SUCCEEDED(hres)) {
/*
* Link this node into the list. This must be done
* under the critical section.
*/
pem->pemNext = g_pemFirst;
g_pemFirst = pem;
InterlockedIncrement(&ped->cRef);
*ppviOut = &pem->vi;
} else {
FreePpv(ppviOut);
}
DllLeaveCrit();
}
ExitOleProcPpv(ppviOut);
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | CEm_NextSequence |
*
* Increment the sequence number wherever it may be.
*
*****************************************************************************/
DWORD INTERNAL
CEm_NextSequence(void)
{
/*
* Stashing the value into a local tells the compiler that
* the value can be cached. Otherwise, the compiler has
* to assume that InterlockedIncrement can modify g_pdwSequence
* so it keeps reloading it.
*/
LPDWORD pdwSequence = g_pdwSequence;
AssertF(pdwSequence);
/*
* Increment through zero.
*/
if (InterlockedIncrement((LPLONG)pdwSequence) == 0) {
InterlockedIncrement((LPLONG)pdwSequence);
}
return *pdwSequence;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PEM | CEm_BufferEvent |
*
* Add a single event to the device, returning the next device
* on the global list.
*
* This routine is entered with the global critical section
* taken exactly once.
*
*****************************************************************************/
PEM INTERNAL
CEm_BufferEvent(PEM pem, DWORD dwData, DWORD dwOfs, DWORD tm, DWORD dwSeq)
{
PEM pemNext;
/*
* We must release the global critical section in order to take
* the device critical section.
*/
CEm_AddRef(pem); /* Make sure it doesn't vanish */
DllLeaveCrit();
AssertF(!InCrit());
/*
* ---Windows Bug 238305---
* Run the buffering code in __try block so that if an
* input is receive after the device is released, we can
* catch the AV and clean up from there.
*/
__try
{
CDIDev_EnterCrit(pem->vi.pdd);
AssertF(dwOfs < pem->ped->cbData);
AssertF(pem->rgdwDf);
/*
* If the user cares about the object...
*/
if (pem->rgdwDf[dwOfs] != 0xFFFFFFFF) {
LPDIDEVICEOBJECTDATA_DX3 pdod = pem->vi.pHead;
/*
* Set the node value.
*/
pdod->dwOfs = pem->rgdwDf[dwOfs];
pdod->dwData = dwData;
pdod->dwTimeStamp = tm;
pdod->dwSequence = dwSeq;
/*
* Append the node to the list if there is room.
* Note that we rely above on the fact that the list is
* never totally full.
*/
pdod++;
AssertF(pdod <= pem->vi.pEnd);
if (pdod >= pem->vi.pEnd) {
pdod = pem->vi.pBuffer;
}
/*
* always keep the new data
*/
pem->vi.pHead = pdod;
if (pdod == pem->vi.pTail) {
if (!pem->vi.fOverflow) {
RPF("Buffer overflow; discard old data");
}
pem->vi.pTail++;
if (pem->vi.pTail == pem->vi.pEnd) {
pem->vi.pTail = pem->vi.pBuffer;
}
pem->vi.fOverflow = 1;
}
}
CDIDev_LeaveCrit(pem->vi.pdd);
}
/*
* If we get an AV, most likely input is received after the device has
* been released. In this case, we clean up the thread and exit as
* soon as possible.
*/
__except( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH )
{
/* Do nothing here, so we clean up the thread and exit below. */
RPF("CEm_BufferEvent: Access Violation catched! Most likely the device has been released");
}
DllEnterCrit();
pemNext = pem->pemNext;
AssertF(fLimpFF(pemNext, pemNext->dwSignature == CEM_SIGNATURE));
CEm_Release(pem);
return pemNext;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func HRESULT | CEm_ContinueEvent |
*
* Add a single event to the queues of all acquired devices
* of the indicated type.
*
* @returns
*
* TRUE if someone is interested in this data (even if they are not
* buffered).
*
*****************************************************************************/
BOOL EXTERNAL
CEm_ContinueEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm, DWORD dwSeq)
{
DWORD ddwData; /* delta in dwData */
BOOL fRtn = FALSE;
AssertF(!InCrit());
/* Sanity check: Make sure the ped has been initialized */
if (ped->pDevType) {
PEM pem, pemNext;
if (ped->pDevType[dwOfs] & DIDFT_DWORDOBJS) {
DWORD UNALIGNED *pdw = pvAddPvCb(ped->pState, dwOfs);
if (*pdw != dwData) {
if (ped->pDevType[dwOfs] & DIDFT_POV ) {
ddwData = dwData; /* Don't do deltas for POV */
} else {
ddwData = dwData - *pdw;
}
*pdw = dwData;
} else {
goto nop;
}
} else {
LPBYTE pb = pvAddPvCb(ped->pState, dwOfs);
AssertF((dwData & ~0x80) == 0);
if (*pb != (BYTE)dwData) {
*pb = (BYTE)dwData;
ddwData = dwData; /* Don't do deltas for buttons */
/* Someday: Button sequences go here */
} else {
goto nop;
}
}
AssertF(!InCrit()); /* You can never be too paranoid */
DllEnterCrit();
for (pem = g_pemFirst; pem; pem = pemNext) {
AssertF(pem->dwSignature == CEM_SIGNATURE);
if ((pem->vi.fl & (VIFL_ACQUIRED|VIFL_INITIALIZE)) && pem->ped == ped) {
if (pem->vi.pBuffer) {
if( pem->vi.fl & VIFL_RELATIVE )
{
pemNext = CEm_BufferEvent(pem, ddwData, dwOfs, tm, dwSeq);
}
else
{
pemNext = CEm_BufferEvent(pem, dwData, dwOfs, tm, dwSeq);
}
AssertF(fLimpFF(pemNext,
pemNext->dwSignature == CEM_SIGNATURE));
} else {
pemNext = pem->pemNext;
AssertF(fLimpFF(pemNext,
pemNext->dwSignature == CEM_SIGNATURE));
}
/*
* It would be easy to avoid setting the event if nothing
* was buffered but somebody would rely on getting them
* without setting a buffer.
*/
fRtn = TRUE;
} else {
pemNext = pem->pemNext;
AssertF(fLimpFF(pemNext,
pemNext->dwSignature == CEM_SIGNATURE));
}
}
DllLeaveCrit();
}
nop:;
return fRtn;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | CEm_AddEvent |
*
* Increment the DirectInput sequence number, then
* add a single event to the queues of all acquired devices
* of the indicated type.
*
* @parm PED | ped |
*
* Device which is adding the event.
*
* @parm DWORD | dwData |
*
* The event data.
*
* @parm DWORD | dwOfs |
*
* Device data format-relative offset for <p dwData>.
*
* @parm DWORD | tm |
*
* Time the event was generated.
*
* @returns
*
* Returns the sequence number added, so that it may be
* continued.
*
*****************************************************************************/
DWORD EXTERNAL
CEm_AddEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm)
{
PEM pem, pemNext;
DWORD dwSeq = CEm_NextSequence();
AssertF(!InCrit()); /* You can never be too paranoid */
if( CEm_ContinueEvent(ped, dwData, dwOfs, tm, dwSeq) )
{
DllEnterCrit();
for (pem = g_pemFirst; pem; pem = pemNext) {
AssertF(pem->dwSignature == CEM_SIGNATURE);
if ((pem->vi.fl & VIFL_ACQUIRED) && pem->ped == ped) {
CDIDev_SetNotifyEvent(pem->vi.pdd);
}
pemNext = pem->pemNext;
AssertF(fLimpFF(pemNext,
pemNext->dwSignature == CEM_SIGNATURE));
}
DllLeaveCrit();
}
return dwSeq;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_AddState |
*
* Record a brand new device state.
*
* @parm PED | ped |
*
* Device which has changed state.
*
* @parm DWORD | dwData |
*
* The value to record.
*
* @parm DWORD | tm |
*
* Time the state change was generated.
*
*****************************************************************************/
void EXTERNAL
CEm_AddState(PED ped, LPVOID pvData, DWORD tm)
{
DWORD dwSeq = CEm_NextSequence();
/* Sanity check: Make sure the ped has been initialized */
if (ped->pDevType) {
DWORD dwOfs;
BOOL fEvent = FALSE;
/*
* Note, it is too late to improve performance by only doing events
* if somebody is listening.
*/
dwOfs = 0;
while (dwOfs < ped->cbData) {
/*
* There shouldn't be any no-data items.
*/
AssertF(!(ped->pDevType[dwOfs] & DIDFT_NODATA));
if (ped->pDevType[dwOfs] & DIDFT_DWORDOBJS) {
DWORD UNALIGNED *pdw = pvAddPvCb(pvData, dwOfs);
if( CEm_ContinueEvent(ped, *pdw, dwOfs, tm, dwSeq) ){
fEvent = TRUE;
}
dwOfs += cbX(DWORD);
} else {
LPBYTE pb = pvAddPvCb(pvData, dwOfs);
if( CEm_ContinueEvent(ped, *pb, dwOfs, tm, dwSeq) ) {
fEvent = TRUE;
}
dwOfs++;
}
}
if( fEvent ) {
PEM pem, pemNext;
AssertF(!InCrit()); /* You can never be too paranoid */
DllEnterCrit();
for (pem = g_pemFirst; pem; pem = pemNext) {
AssertF(pem->dwSignature == CEM_SIGNATURE);
if ((pem->vi.fl & VIFL_ACQUIRED) && pem->ped == ped) {
CDIDev_SetNotifyEvent(pem->vi.pdd);
}
pemNext = pem->pemNext;
AssertF(fLimpFF(pemNext,
pemNext->dwSignature == CEM_SIGNATURE));
}
DllLeaveCrit();
}
}
}
#if 0
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_InputLost |
*
* Remove global hooks because something weird happened.
*
* We don't need to do anything because our hooks are local.
*
*****************************************************************************/
HRESULT INLINE
CEm_InputLost(LPVOID pvIn, LPVOID pvOut)
{
return S_OK;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_UnacquirePem |
*
* Unacquire the device in the device-specific way.
*
* @parm PEM | pem |
*
* Information about the gizmo being mangled.
*
* @parm UINT | fdufl |
*
* Assorted flags describing why we are being unacquired.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_UnacquirePem(PEM this, UINT fdufl)
{
HRESULT hres;
#ifdef DEBUG
EnterProcR(CEm_UnacquirePem, (_ "px", this, fdufl));
#else
EnterProcR(IDirectInputDevice8::Unacquire, (_ "p", this));
#endif
AssertF(this->dwSignature == CEM_SIGNATURE);
AssertF((fdufl & ~FDUFL_UNPLUGGED) == 0);
CAssertF(FDUFL_UNPLUGGED == VIFL_UNPLUGGED);
if (this->vi.fl & VIFL_ACQUIRED) {
this->vi.fl &= ~VIFL_ACQUIRED;
this->vi.fl |= fdufl;
if (InterlockedDecrement(&this->cAcquire) < 0) {
InterlockedDecrement(&this->ped->cAcquire);
hres = this->ped->Acquire(this, 0);
} else {
SquirtSqflPtszV(sqfl, TEXT("%S: Still acquired %d"),
s_szProc, this->cAcquire);
hres = S_OK;
}
} else {
SquirtSqflPtszV(sqfl, TEXT("%S: Not acquired %d"),
s_szProc, this->cAcquire);
hres = S_OK;
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_ForceDeviceUnacquire |
*
* Force all users of a device to unacquire.
*
* @parm PEM | pem |
*
* Information about the gizmo being mangled.
*
* @parm UINT | fdufl |
*
* Assorted flags describing why we are being unacquired.
*
*****************************************************************************/
void EXTERNAL
CEm_ForceDeviceUnacquire(PED ped, UINT fdufl)
{
PEM pem, pemNext;
AssertF((fdufl & ~FDUFL_UNPLUGGED) == 0);
AssertF(!DllInCrit());
DllEnterCrit();
for (pem = g_pemFirst; pem; pem = pemNext) {
AssertF(pem->dwSignature == CEM_SIGNATURE);
if (pem->ped == ped && (pem->vi.fl & VIFL_ACQUIRED)) {
CEm_AddRef(pem);
DllLeaveCrit();
CEm_UnacquirePem(pem, fdufl);
CDIDev_SetForcedUnacquiredFlag(pem->vi.pdd);
/*
* Since this happens only when the device is acquired,
* we don't need to worry about the notify event changing
* asynchronously.
*/
CDIDev_SetNotifyEvent(pem->vi.pdd);
DllEnterCrit();
pemNext = pem->pemNext;
AssertF(pem->dwSignature == CEM_SIGNATURE);
CEm_Release(pem);
} else {
pemNext = pem->pemNext;
AssertF(pem->dwSignature == CEM_SIGNATURE);
}
}
DllLeaveCrit();
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_DestroyInstance |
*
* Clean up an instance.
*
*****************************************************************************/
HRESULT EXTERNAL
CEm_DestroyInstance(PVXDINSTANCE *ppvi)
{
HRESULT hres;
PEM this = _thisPvNm(*ppvi, vi);
EnterProc(CEm_DestroyInstance, (_ "p", *ppvi));
AssertF(this->dwSignature == CEM_SIGNATURE);
AssertF((PV)this == (PV)*ppvi);
if (this) {
CEm_Release(this);
}
hres = S_OK;
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_SetDataFormat |
*
* Record the application data format in the device so that
* we can translate it for buffering purposes.
*
* @parm PVXDDATAFORMAT | pvdf |
*
* Information about the gizmo being mangled.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_SetDataFormat(PVXDDATAFORMAT pvdf)
{
HRESULT hres;
PEM this = _thisPvNm(pvdf->pvi, vi);
EnterProc(CEm_SetDataFormat, (_ "p", pvdf->pvi));
AssertF(this->dwSignature == CEM_SIGNATURE);
hres = ReallocCbPpv( cbCdw(pvdf->cbData), &this->rgdwDf);
if (SUCCEEDED(hres)) {
AssertF(pvdf->cbData == this->ped->cbData);
memcpy(this->rgdwDf, pvdf->pDfOfs, cbCdw(pvdf->cbData) );
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_AcquireInstance |
*
* Acquire the device in the device-specific way.
*
* @parm PVXDINSTANCE * | ppvi |
*
* The instance to acquire.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_AcquireInstance(PVXDINSTANCE *ppvi)
{
HRESULT hres;
PEM this = _thisPvNm(*ppvi, vi);
#ifdef DEBUG
EnterProc(CEm_AcquireInstance, (_ "p", *ppvi));
#else
EnterProcR(IDirectInputDevice8::Acquire, (_ "p", *ppvi));
#endif
AssertF(this->dwSignature == CEM_SIGNATURE);
this->vi.fl |= VIFL_ACQUIRED;
if (InterlockedIncrement(&this->cAcquire) == 0) {
InterlockedIncrement(&this->ped->cAcquire);
hres = this->ped->Acquire(this, 1);
if (FAILED(hres)) {
this->vi.fl &= ~VIFL_ACQUIRED;
InterlockedDecrement(&this->cAcquire);
}
} else {
SquirtSqflPtszV(sqfl, TEXT("%S: Already acquired %d"),
s_szProc, this->cAcquire);
hres = S_OK;
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_UnacquireInstance |
*
* Unacquire the device in the device-specific way.
*
* @parm PVXDINSTANCE * | ppvi |
*
* Information about the gizmo being mangled.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_UnacquireInstance(PVXDINSTANCE *ppvi)
{
HRESULT hres;
PEM this = _thisPvNm(*ppvi, vi);
EnterProc(CEm_UnacquireInstance, (_ "p", *ppvi));
hres = CEm_UnacquirePem(this, FDUFL_NORMAL);
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_SetBufferSize |
*
* Allocate a buffer of the appropriate size.
*
* @parm PVXDDWORDDATA | pvdd |
*
* The <p dwData> is the buffer size.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_SetBufferSize(PVXDDWORDDATA pvdd)
{
HRESULT hres;
PEM this = _thisPvNm(pvdd->pvi, vi);
EnterProc(CEm_SetBufferSize, (_ "px", pvdd->pvi, pvdd->dw));
AssertF(this->dwSignature == CEM_SIGNATURE);
hres = ReallocCbPpv(cbCxX(pvdd->dw, DIDEVICEOBJECTDATA),
&this->vi.pBuffer);
if (SUCCEEDED(hres)) {
this->vi.pHead = this->vi.pBuffer;
this->vi.pTail = this->vi.pBuffer;
this->vi.pEnd = &this->vi.pBuffer[pvdd->dw];
}
ExitOleProc();
return hres;
}
#ifdef USE_SLOW_LL_HOOKS
/*****************************************************************************
*
* @struct LLHOOKINFO |
*
* Information about how to install a low-level hook.
*
* @field int | idHook |
*
* The Windows hook identifier.
*
* @field HOOKPROC | hp |
*
* The hook procedure itself.
*
*****************************************************************************/
typedef struct LLHOOKINFO {
int idHook;
HOOKPROC hp;
} LLHOOKINFO, *PLLHOOKINFO;
typedef const LLHOOKINFO *PCLLHOOKINFO;
#pragma BEGIN_CONST_DATA
const LLHOOKINFO c_rgllhi[] = {
{ WH_KEYBOARD_LL, CEm_LL_KbdHook }, /* LLTS_KBD */
{ WH_MOUSE_LL, CEm_LL_MseHook }, /* LLTS_MSE */
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_LL_SyncHook |
*
* Install or remove a hook as needed.
*
* @parm UINT | ilts |
*
* Which hook is being handled?
*
* @parm PLLTHREADSTATE | plts |
*
* Thread hook state containing hook information to synchronize.
*
*****************************************************************************/
void INTERNAL
CEm_LL_SyncHook(PLLTHREADSTATE plts, UINT ilts)
{
PLLHOOKSTATE plhs = &plts->rglhs[ilts];
if (!fLeqvFF(plhs->cHook, plhs->hhk)) {
if (plhs->hhk) {
UnhookWindowsHookEx(plhs->hhk);
plhs->hhk = 0;
} else {
PCLLHOOKINFO pllhi = &c_rgllhi[ilts];
plhs->hhk = SetWindowsHookEx(pllhi->idHook, pllhi->hp, g_hinst, 0);
}
}
}
#endif /* USE_SLOW_LL_HOOKS */
#ifdef WORKER_THREAD
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | FakeMsgWaitForMultipleObjectsEx |
*
* Stub function which emulates
* <f MsgWaitForMultipleObjectsEx>
* on platforms that do not support it.
*
* Such platforms (namely, Windows 95) do not support HID
* and therefore the inability to go into an alertable
* wait state constitutes no loss of amenity.
*
* @parm DWORD | nCount |
*
* Number of handles in handle array.
*
* @parm LPHANDLE | pHandles |
*
* Pointer to an object-handle array.
*
* @parm DWORD | ms |
*
* Time-out interval in milliseconds.
*
* @parm DWORD | dwWakeMask |
*
* Type of input events to wait for.
*
* @parm DWORD | dwFlags |
*
* Wait flags.
*
* @returns
*
* Same as <f MsgWaitForMultipleObjectsEx>.
*
*****************************************************************************/
DWORD WINAPI
FakeMsgWaitForMultipleObjectsEx(
DWORD nCount,
LPHANDLE pHandles,
DWORD ms,
DWORD dwWakeMask,
DWORD dwFlags)
{
/*
* We merely call the normal MsgWaitForMultipleObjects because
* the only way we can get here is on a platform that doesn't
* support HID.
*/
return MsgWaitForMultipleObjects(nCount, pHandles,
dwFlags & MWMO_WAITALL, ms, dwWakeMask);
}
#ifdef WINNT
// On win2k non-exclusive mode user thinks the Dinput thread is hung.
// In order to fix this we set a TimerEvent and wake up every so
// often and execute the FakeTimerProc. This keeps user happy and
// keeps dinput thread from being marked as hung and we can get
// events to our low level hooks
VOID CALLBACK FakeTimerProc(
HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
)
{
}
#endif
#ifdef USE_WM_INPUT
#pragma BEGIN_CONST_DATA
TCHAR c_szEmClassName[] = TEXT("DIEmWin");
#pragma END_CONST_DATA
/****************************************************************************
*
* CEm_WndProc
*
* Window procedure for simple sample.
*
****************************************************************************/
LRESULT CALLBACK
CEm_WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch (msg) {
//case WM_INPUT:
// RPF("in WM_INPUT message");
// break;
default:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HWND
CEm_InitWindow(void)
{
HWND hwnd;
WNDCLASS wc;
static BOOL fFirstTime = TRUE;
if( fFirstTime ) {
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szEmClassName;
wc.hbrBackground = 0;
wc.hInstance = g_hinst;
wc.style = 0;
wc.lpfnWndProc = CEm_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
if (!RegisterClass(&wc)) {
return NULL;
}
fFirstTime = FALSE;
}
hwnd = CreateWindow(
c_szEmClassName, // Class name
TEXT("DIEmWin"), // Caption
WS_OVERLAPPEDWINDOW, // Style
-1, -1, // Position
1, 1, // Size
NULL, //parent
NULL, // No menu
g_hinst, // inst handle
0 // no params
);
if( !hwnd ) {
RPF("CreateWindow failed.");
}
return hwnd;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func VOID | CEm_LL_ThreadProc |
*
* The thread that manages our low-level hooks.
*
* ThreadProcs are prototyped to return a DWORD 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.
*
* When we get started, and whenever we receive any message
* whatsoever, re-check to see which hooks should be installed
* and re-synchronize ourselves with them.
*
* Note that restarting can be slow, since it happens only
* when we get nudged by a client.
*
* @parm PLLTHREADSTATE | plts |
*
* The thread state to use.
*
*****************************************************************************/
VOID INTERNAL
CEm_LL_ThreadProc(PLLTHREADSTATE plts)
{
MSG msg;
DWORD dwRc;
#ifdef USE_WM_INPUT
HWND hwnd = NULL;
#endif
AssertF(plts->idThread == GetCurrentThreadId());
SquirtSqflPtszV(sqflLl, TEXT("CEm_LL_ThreadProc: Thread started"));
#ifdef USE_SLOW_LL_HOOKS
/*
* Refresh the mouse acceleration values.
*
* ISSUE-2001/03/29-timgill Need a window to listen for WM_SETTINGCHANGE
* We need to create a window to listen for
* WM_SETTINGCHANGE so we can refresh the mouse acceleration
* as needed.
*/
CEm_Mouse_OnMouseChange();
#endif
/*
* Create ourselves a queue before we go into our "hey what happened
* before I got here?" phase. The thread that created us is waiting on
* the thread event, holding DLLCrit, so let it go as soon as the queue
* is ready. We create the queue by calling a function that requires a
* queue. We use this very simple one.
*/
GetInputState();
#ifdef WINNT
// Look at comment block in FakeTimerProc
SetTimer(NULL, 0, 2 * 1000 /*2 seconds*/, FakeTimerProc);
#endif
SetEvent(plts->hEvent);
#ifdef USE_WM_INPUT
ResetEvent(g_hEventThread);
if( g_fRawInput ) {
hwnd = CEm_InitWindow();
if (!hwnd) {
g_fRawInput = FALSE;
}
}
g_hwndThread = hwnd;
// Tell CEm_LL_Acquire that windows has been created.
SetEvent( g_hEventAcquire );
if( g_fFromKbdMse ) {
DWORD rc;
rc = WaitForSingleObject(g_hEventThread, INFINITE);
g_fFromKbdMse = FALSE;
}
#endif
#ifdef USE_SLOW_LL_HOOKS
/*
* Note carefully that we sync the hooks before entering our
* fake GetMessage loop. This is necessary to avoid the race
* condition when CEm_LL_Acquire posts us a thread message
* before our thread gets a queue. By sync'ing the hooks
* first, we do what the lost message would've had us do
* anyway.
* ISSUE-2001/03/29-timgill Following branch should be no longer necessary
* This is should not be needed now that CEm_GetWorkerThread waits for
* this thread to respond before continuing on to post any messages.
*/
#endif /* USE_SLOW_LL_HOOKS */
do {
#ifdef USE_SLOW_LL_HOOKS
if( !g_fRawInput ) {
CEm_LL_SyncHook(plts, LLTS_KBD);
CEm_LL_SyncHook(plts, LLTS_MSE);
}
#endif
/*
* We can wake up for three reasons.
*
* 1. We received an APC due to an I/o completion.
* Just go back to sleep.
*
* 2. We need to call Peek/GetMessage so that
* USER can dispatch a low-level hook or SendMessage.
* Go into a PeekMessage loop to let that happen.
*
* 3. A message got posted to us.
* Go into a PeekMessage loop to process it.
*/
do {
dwRc = _MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT,
MWMO_ALERTABLE);
} while (dwRc == WAIT_IO_COMPLETION);
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.hwnd == 0 && msg.message == WM_NULL && msg.lParam)
{
/*
* See if maybe the lParam is a valid PEM that we're
* processing.
*/
PEM pem = (PEM)msg.lParam;
if( pem && pem == plts->pemCheck )
{
AssertF(GPA_FindPtr(&plts->gpaHid, pem));
CEm_HID_Sync(plts, pem);
plts->pemCheck = NULL;
SetEvent(plts->hEvent);
#ifdef USE_WM_INPUT
if( g_fRawInput ) {
SetEvent(g_hEventHid);
}
#endif
continue;
}
}
#ifdef USE_WM_INPUT
else if ( g_fRawInput && msg.message == WM_INPUT &&
(msg.wParam == RIM_INPUT || msg.wParam == RIM_INPUTSINK) )
{
CDIRaw_OnInput(&msg);
}
#endif
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while (plts->cRef);
#ifdef USE_SLOW_LL_HOOKS
/*
* Remove our hooks before we go.
*
* It is possible that there was a huge flurry of disconnects,
* causing us to notice that our refcount disappeared before
* we got a chance to remove the hooks in our message loop.
*/
AssertF(plts->rglhs[LLTS_KBD].cHook == 0);
AssertF(plts->rglhs[LLTS_KBD].cExcl == 0);
AssertF(plts->rglhs[LLTS_MSE].cHook == 0);
AssertF(plts->rglhs[LLTS_MSE].cExcl == 0);
if( !g_fRawInput ) {
if (plts->rglhs[LLTS_KBD].hhk) {
UnhookWindowsHookEx(plts->rglhs[LLTS_KBD].hhk);
}
if (plts->rglhs[LLTS_MSE].hhk) {
UnhookWindowsHookEx(plts->rglhs[LLTS_MSE].hhk);
}
}
#endif /* USE_SLOW_LL_HOOKS */
#ifdef USE_WM_INPUT
if( g_hwndThread ) {
DestroyWindow( g_hwndThread );
g_hwndThread = NULL;
}
ResetEvent( g_hEventAcquire );
ResetEvent( g_hEventHid );
#endif
if( plts->gpaHid.rgpv ) {
FreePpv(&plts->gpaHid.rgpv);
}
if( plts->hEvent ) {
CloseHandle( plts->hEvent );
}
if( plts->hThread) {
CloseHandle(plts->hThread);
}
FreePpv( &plts );
SquirtSqflPtszV(sqflLl, TEXT("CEm_LL_ThreadProc: Thread terminating"));
FreeLibraryAndExitThread(g_hinst, 0);
/*NOTREACHED*/
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_GetWorkerThread |
*
* Piggyback off the existing worker thread if possible;
* else create a new one.
*
* @parm PEM | pem |
*
* Emulation state which requires a worker thread.
*
* @parm PLLTHREADSTATE * | pplts |
*
* Receives thread state for worker thread.
*
*****************************************************************************/
STDMETHODIMP
CEm_GetWorkerThread(PEM pem, PLLTHREADSTATE *pplts)
{
PLLTHREADSTATE plts;
HRESULT hres;
DllEnterCrit();
/*
* Normally, we can piggyback off the one we already have.
*/
plts = g_plts;
/*
* If we already have a ref to a worker thread, then use it.
*/
if (pem->fWorkerThread) {
/*
* The reference we created when we created the worker thread
* ensures that g_plts is valid.
*/
AssertF(plts);
AssertF(plts->cRef);
if (plts) {
hres = S_OK;
} else {
AssertF(0); /* Can't happen */
hres = E_FAIL;
}
} else
if (plts) {
/*
* Create a reference to the existing thread.
*/
pem->fWorkerThread = TRUE;
InterlockedIncrement(&plts->cRef);
hres = S_OK;
} else {
/*
* There is no worker thread (or it is irretrievably
* on its way out) so create a new one.
*/
hres = AllocCbPpv(cbX(LLTHREADSTATE), &plts);
if (SUCCEEDED(hres)) {
DWORD dwRc = 0;
TCHAR tsz[MAX_PATH];
/*
* Assume the worst unless we find otherwise
*/
hres = E_FAIL;
if( GetModuleFileName(g_hinst, tsz, cA(tsz))
&& ( LoadLibrary(tsz) == g_hinst ) )
{
/*
* Must set up everything to avoid racing with
* the incoming thread.
*/
g_plts = plts;
InterlockedIncrement(&plts->cRef);
plts->hEvent = CreateEvent(0x0, 0, 0, 0x0);
if( plts->hEvent )
{
plts->hThread= CreateThread(0, 0, (LPTHREAD_START_ROUTINE)CEm_LL_ThreadProc, plts,
0, &plts->idThread);
if( plts->hThread )
{
/*
* Boost our priority to make sure we
* can handle the messages.
*
* RaymondC commented this out saying that it does not
* help but we're hoping that it may on Win2k.
*/
SetThreadPriority(plts->hThread, THREAD_PRIORITY_HIGHEST);
/*
* Wait for the thread to signal that it is up and running
* or for it to terminate.
* This means that we don't have to consider the
* possibility that the thread is not yet running in
* NotifyWorkerThreadPem so we know a failure there is
* terminal and don't retry.
*
* Assert that the handle fields make a two handle array.
*/
CAssertF( FIELD_OFFSET( LLTHREADSTATE, hThread) + sizeof(plts->hThread)
== FIELD_OFFSET( LLTHREADSTATE, hEvent) );
/*
* According to a comment in CEm_LL_ThreadProc Win95 may
* fail with an invalid parameter error, so if it does,
* keep trying. (Assume no valid case will occur.)
*
* ISSUE-2001/03/29-timgill Need to minimise waits while holding sync. objects
* Waiting whilst holding DLLCrit is bad.
*/
do
{
dwRc = WaitForMultipleObjects( 2, &plts->hThread, FALSE, INFINITE);
} while ( ( dwRc == WAIT_FAILED ) && ( GetLastError() == ERROR_INVALID_PARAMETER ) );
if( dwRc == WAIT_OBJECT_0 ) {
SquirtSqflPtszV(sqfl | sqflError,
TEXT("CEm_GetWorkerThread: Created Thread terminated on first wait") );
} else {
pem->fWorkerThread = TRUE;
hres = S_OK;
if( dwRc != WAIT_OBJECT_0 + 1 )
{
/*
* This would be a bad thing if it ever happened
* but we have to assume that the thread is still
* running so we return a success anyway.
*/
SquirtSqflPtszV(sqfl | sqflError,
TEXT("CEm_GetWorkerThread: First wait returned 0x%08x with LastError %d"),
dwRc, GetLastError() );
}
}
}
else
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("CEm_GetWorkerThread: CreateThread failed with error %d"),
GetLastError() );
}
}
else
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("CEm_GetWorkerThread: CreateEvent failed with error %d"),
GetLastError() );
}
if( FAILED( hres ) )
{
if( plts->hEvent ) {
CloseHandle( plts->hEvent );
}
FreeLibrary(g_hinst);
}
}
else
{
RPF( "CEm_GetWorkerThread: failed to LoadLibrary( self ), le = %d", GetLastError() );
}
if( FAILED( hres ) )
{
FreePv(plts);
g_plts = 0;
}
}
}
DllLeaveCrit();
*pplts = plts;
return hres;
}
#endif /* WORKER_THREAD */
#ifdef USE_SLOW_LL_HOOKS
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_LL_Acquire |
*
* Acquire/unacquire a mouse or keyboard via low-level hooks.
*
* @parm PEM | pem |
*
* Device being acquired.
*
* @parm BOOL | fAcquire |
*
* Whether the device is being acquired or unacquired.
*
* @parm ULONG | fl |
*
* Flags in VXDINSTANCE (vi.fl).
*
* @parm UINT | ilts |
*
* LLTS_KBD or LLTS_MSE, depending on which is happening.
*
*****************************************************************************/
STDMETHODIMP
CEm_LL_Acquire(PEM this, BOOL fAcquire, ULONG fl, UINT ilts)
{
PLLTHREADSTATE plts;
BOOL fExclusive = fl & VIFL_CAPTURED;
BOOL fNoWinkey = fl & VIFL_NOWINKEY;
HRESULT hres = S_OK;
EnterProc(CEm_LL_Acquire, (_ "puuu", this, fAcquire, fExclusive, ilts));
AssertF(this->dwSignature == CEM_SIGNATURE);
AssertF(ilts==LLTS_KBD || ilts==LLTS_MSE);
#ifdef USE_WM_INPUT
g_fFromKbdMse = fAcquire ? TRUE : FALSE;
ResetEvent( g_hEventAcquire );
#endif
hres = CEm_GetWorkerThread(this, &plts);
if (SUCCEEDED(hres)) {
AssertF( plts->rglhs[ilts].cHook >= plts->rglhs[ilts].cExcl );
#ifdef USE_WM_INPUT
if( g_fRawInput && !g_hwndThread) {
DWORD dwRc;
dwRc = WaitForSingleObject(g_hEventAcquire, INFINITE);
}
#endif
if (fAcquire) {
InterlockedIncrement(&plts->rglhs[ilts].cHook);
if (fExclusive) {
InterlockedIncrement(&plts->rglhs[ilts].cExcl);
}
#ifdef USE_WM_INPUT
if( g_hwndThread ) {
if( fExclusive ) {
hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_EXCL, g_hwndThread);
}
else if( fNoWinkey ) {
AssertF( ilts == 0 );
if( ilts == 0 ) {
hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_NOHOTKEYS, g_hwndThread);
} else {
hres = E_FAIL;
}
}
else {
hres = CDIRaw_RegisterRawInputDevice(1-ilts, DIRAW_NONEXCL, g_hwndThread);
}
if(FAILED(hres)) {
hres = S_FALSE;
g_fRawInput = FALSE;
RPF("CEm_LL_Acquire: RegisterRawInput failed. LL will be used.");
}
}
#endif
} else { /* Remove the hook */
AssertF(plts->cRef);
if (fExclusive) {
InterlockedDecrement(&plts->rglhs[ilts].cExcl);
}
InterlockedDecrement(&plts->rglhs[ilts].cHook);
#ifdef USE_WM_INPUT
if( g_fRawInput ) {
CDIRaw_UnregisterRawInputDevice(1-ilts, g_hwndThread);
if( plts->rglhs[ilts].cHook ) {
CDIRaw_RegisterRawInputDevice(1-ilts, 0, g_hwndThread);
}
}
#endif
}
NudgeWorkerThread(plts->idThread);
#ifdef USE_WM_INPUT
// tell CEm_LL_ThreadProc that acquire finished.
SetEvent( g_hEventThread );
#endif
}
ExitOleProc();
return hres;
}
#endif /* USE_SLOW_LL_HOOKS */
/*****************************************************************************
*
* Joystick emulation
*
*****************************************************************************/
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_Joy_Acquire |
*
* Acquire a joystick. Nothing happens.
*
* @parm PEM | pem |
*
* Device being acquired.
*
*****************************************************************************/
STDMETHODIMP
CEm_Joy_Acquire(PEM this, BOOL fAcquire)
{
AssertF(this->dwSignature == CEM_SIGNATURE);
return S_OK;
}
/*****************************************************************************
*
* Joystick globals
*
* Since we don't use joystick emulation by default, we allocate
* the emulation variables dynamically so we don't blow a page
* of memory on them.
*
*****************************************************************************/
typedef struct JOYEMVARS {
ED rged[cJoyMax];
DIJOYSTATE2 rgjs2[cJoyMax];
} JOYEMVARS, *PJOYEMVARS;
static PJOYEMVARS s_pjev;
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_Joy_CreateInstance |
*
* Create a joystick thing.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* What the object should look like.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* The answer goes here.
*
*****************************************************************************/
#define OBJAT(T, v) (*(T *)(v))
#define PUN(T, v) OBJAT(T, &(v))
HRESULT INTERNAL
CEm_Joy_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
HRESULT hres;
DllEnterCrit();
if (s_pjev == 0) {
DWORD uiJoy;
hres = AllocCbPpv(cbX(JOYEMVARS), &s_pjev);
if (SUCCEEDED(hres)) {
for (uiJoy = 0; uiJoy < cJoyMax; uiJoy++) {
PUN(PV, s_pjev->rged[uiJoy].pState) = &s_pjev->rgjs2[uiJoy];
s_pjev->rged[uiJoy].Acquire = CEm_Joy_Acquire;
s_pjev->rged[uiJoy].cbData = cbX(s_pjev->rgjs2[uiJoy]);
s_pjev->rged[uiJoy].cRef = 0x0;
}
}
} else {
hres = S_OK;
}
DllLeaveCrit();
if (SUCCEEDED(hres)) {
hres = CEm_CreateInstance(pdevf, ppviOut,
&s_pjev->rged[pdevf->dwExtra]);
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_Joy_Ping |
*
* Read data from the joystick.
*
* @parm PVXDINSTANCE * | ppvi |
*
* Information about the gizmo being mangled.
*
*****************************************************************************/
HRESULT INTERNAL
CEm_Joy_Ping(PVXDINSTANCE *ppvi)
{
HRESULT hres;
JOYINFOEX ji;
MMRESULT mmrc;
PEM this = _thisPvNm(*ppvi, vi);
AssertF(this->dwSignature == CEM_SIGNATURE);
ji.dwSize = cbX(ji);
ji.dwFlags = JOY_RETURNALL + JOY_RETURNRAWDATA;
ji.dwPOV = JOY_POVCENTERED; /* joyGetPosEx forgets to set this */
mmrc = joyGetPosEx((DWORD)(UINT_PTR)this->dwExtra, &ji);
if (mmrc == JOYERR_NOERROR) {
DIJOYSTATE2 js;
UINT uiButtons;
ZeroX(js); /* Wipe out the bogus things */
js.lX = ji.dwXpos;
js.lY = ji.dwYpos;
js.lZ = ji.dwZpos;
js.lRz = ji.dwRpos;
js.rglSlider[0] = ji.dwUpos;
js.rglSlider[1] = ji.dwVpos;
js.rgdwPOV[0] = ji.dwPOV;
js.rgdwPOV[1] = JOY_POVCENTERED;
js.rgdwPOV[2] = JOY_POVCENTERED;
js.rgdwPOV[3] = JOY_POVCENTERED;
for (uiButtons = 0; uiButtons < 32; uiButtons++) {
if (ji.dwButtons & (1 << uiButtons)) {
js.rgbButtons[uiButtons] = 0x80;
}
}
CEm_AddState(&s_pjev->rged[this->dwExtra], &js, GetTickCount());
hres = S_OK;
} else {
/*
* dinput.dll forces the device unacquired here
* in DX8 we just return an error
*/
hres = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32,
ERROR_DEV_NOT_EXIST);
}
return hres;
}
HRESULT EXTERNAL
NotifyWorkerThreadPem(DWORD idThread, PEM pem)
{
PLLTHREADSTATE plts;
HRESULT hres;
hres = CEm_GetWorkerThread(pem, &plts);
if( SUCCEEDED(hres) )
{
AssertF(plts->idThread == idThread);
hres = NudgeWorkerThreadPem( plts, pem );
}
return hres;
}
HRESULT EXTERNAL
NudgeWorkerThreadPem( PLLTHREADSTATE plts, PEM pem )
{
HRESULT hres = S_FALSE;
plts->pemCheck = pem;
if( !PostWorkerMessage(plts->idThread, pem))
{
SquirtSqflPtszV(sqfl | sqflBenign,
TEXT("NudgeWorkerThreadPem: PostThreadMessage Failed with error %d"),
GetLastError() );
}
else if( pem )
{
DWORD dwRc;
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("NudgeWorkerThreadPem: PostThreadMessage SUCCEEDED, waiting for event ... "));
/*
* According to a comment in CEm_LL_ThreadProc Win95 may
* fail with an invalid parameter error, so if it does,
* keep trying. (Assume no valid case will occur.)
*/
do
{
dwRc = WaitForMultipleObjects( 2, &plts->hThread, FALSE, INFINITE);
} while ( ( dwRc == WAIT_FAILED ) && ( GetLastError() == ERROR_INVALID_PARAMETER ) );
switch( dwRc )
{
case WAIT_OBJECT_0:
SquirtSqflPtszV(sqfl | sqflBenign,
TEXT("NotifyWorkerThreadPem: Not expecting response from dead worker thread") );
break;
case WAIT_OBJECT_0 + 1:
/*
* The worker thread responded OK
*/
hres = S_OK;
AssertF(plts->pemCheck == NULL );
break;
default:
SquirtSqflPtszV(sqfl | sqflError,
TEXT("NotifyWorkerThreadPem: WaitForMultipleObjects returned 0x%08x with LastError %d"),
dwRc, GetLastError() );
hres = E_FAIL;
break;
}
}
return hres;
}