247 lines
5.3 KiB
C++
247 lines
5.3 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
BattleZone.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This app is a really good example of things not to do:
|
||
|
|
||
|
1. Infinite loop on mciSendString('play...'). In order to fix this we
|
||
|
return a failure case if the same play string is sent twice and the
|
||
|
device is playing already. Note that the behaviour of the
|
||
|
mciSendString API is consistent with win9x. Someone managed to repro
|
||
|
this hang on win9x, but it's more difficult.
|
||
|
|
||
|
2. They call SetCooperativeLevel(DDSCL_NORMAL) between a Begin/End
|
||
|
Scene. On NT, this causes the Z-Buffer to be lost which means that
|
||
|
when EndScene is called, it returns D3DERR_SURFACESLOST which causes
|
||
|
the app to AV.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
This is an app specific shim.
|
||
|
|
||
|
History:
|
||
|
|
||
|
02/10/2000 linstev Created
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include <mmsystem.h>
|
||
|
|
||
|
IMPLEMENT_SHIM_BEGIN(BattleZone)
|
||
|
#include "ShimHookMacro.h"
|
||
|
|
||
|
APIHOOK_ENUM_BEGIN
|
||
|
APIHOOK_ENUM_ENTRY_DIRECTX_COMSERVER()
|
||
|
APIHOOK_ENUM_ENTRY(mciSendCommandA)
|
||
|
APIHOOK_ENUM_ENTRY(mciSendStringA)
|
||
|
APIHOOK_ENUM_END
|
||
|
|
||
|
IMPLEMENT_DIRECTX_COMSERVER_HOOKS()
|
||
|
|
||
|
CString * g_csLastCommand = NULL;
|
||
|
MCIDEVICEID g_wDeviceID = 0;
|
||
|
LPDIRECTDRAWSURFACE g_lpZBuffer = NULL;
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Store the DeviceId.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
MCIERROR
|
||
|
APIHOOK(mciSendCommandA)(
|
||
|
MCIDEVICEID IDDevice,
|
||
|
UINT uMsg,
|
||
|
DWORD fdwCommand,
|
||
|
DWORD dwParam
|
||
|
)
|
||
|
{
|
||
|
MCIERROR mErr = ORIGINAL_API(mciSendCommandA)(
|
||
|
IDDevice,
|
||
|
uMsg,
|
||
|
fdwCommand,
|
||
|
dwParam);
|
||
|
|
||
|
if ((mErr == 0) && (uMsg == MCI_OPEN))
|
||
|
{
|
||
|
g_wDeviceID = ((LPMCI_OPEN_PARMS)dwParam)->wDeviceID;
|
||
|
}
|
||
|
|
||
|
return mErr;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Prevent looping.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
MCIERROR
|
||
|
APIHOOK(mciSendStringA)(
|
||
|
LPCSTR lpszCommand,
|
||
|
LPSTR lpszReturnString,
|
||
|
UINT cchReturn,
|
||
|
HANDLE hwndCallback
|
||
|
)
|
||
|
{
|
||
|
DPFN( eDbgLevelInfo, "mciSendStringA: %s", lpszCommand);
|
||
|
|
||
|
CSTRING_TRY
|
||
|
{
|
||
|
CString csCommand(lpszCommand);
|
||
|
if (csCommand.Compare(*g_csLastCommand) == 0)
|
||
|
{
|
||
|
MCI_STATUS_PARMS mciStatus;
|
||
|
ZeroMemory(&mciStatus, sizeof(mciStatus));
|
||
|
mciStatus.dwItem = MCI_STATUS_MODE;
|
||
|
|
||
|
if (0 == ORIGINAL_API(mciSendCommandA)(
|
||
|
g_wDeviceID,
|
||
|
MCI_STATUS,
|
||
|
MCI_STATUS_ITEM,
|
||
|
(DWORD_PTR)&mciStatus))
|
||
|
{
|
||
|
if (mciStatus.dwReturn == MCI_MODE_PLAY)
|
||
|
{
|
||
|
DPFN( eDbgLevelWarning, "Device still playing, returning busy");
|
||
|
return MCIERR_DEVICE_NOT_READY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*g_csLastCommand = csCommand;
|
||
|
}
|
||
|
}
|
||
|
CSTRING_CATCH
|
||
|
{
|
||
|
// Do nothing
|
||
|
}
|
||
|
|
||
|
return ORIGINAL_API(mciSendStringA)(
|
||
|
lpszCommand,
|
||
|
lpszReturnString,
|
||
|
cchReturn,
|
||
|
hwndCallback);
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Hook create surface to find the zbuffer we'll need to restore later. Note that
|
||
|
we use HookObject to get the surface release notification.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
HRESULT
|
||
|
COMHOOK(IDirectDraw, CreateSurface)(
|
||
|
PVOID pThis,
|
||
|
LPDDSURFACEDESC lpDDSurfaceDesc,
|
||
|
LPDIRECTDRAWSURFACE *lplpDDSurface,
|
||
|
IUnknown* pUnkOuter
|
||
|
)
|
||
|
{
|
||
|
HRESULT hReturn;
|
||
|
|
||
|
_pfn_IDirectDraw_CreateSurface pfnOld =
|
||
|
ORIGINAL_COM(IDirectDraw, CreateSurface, pThis);
|
||
|
|
||
|
if (SUCCEEDED(hReturn = (*pfnOld)(
|
||
|
pThis,
|
||
|
lpDDSurfaceDesc,
|
||
|
lplpDDSurface,
|
||
|
pUnkOuter)))
|
||
|
{
|
||
|
if (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
|
||
|
{
|
||
|
g_lpZBuffer = *lplpDDSurface;
|
||
|
DPFN( eDbgLevelInfo, "Found ZBuffer", g_lpZBuffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hReturn;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Use SetCooperativeLevel to keep track of who the exclusive mode owner is.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
HRESULT
|
||
|
COMHOOK(IDirectDraw, SetCooperativeLevel)(
|
||
|
PVOID pThis,
|
||
|
HWND hWnd,
|
||
|
DWORD dwFlags
|
||
|
)
|
||
|
{
|
||
|
HRESULT hReturn;
|
||
|
|
||
|
// Original SetCooperativeLevel
|
||
|
_pfn_IDirectDraw_SetCooperativeLevel pfnOld =
|
||
|
ORIGINAL_COM(IDirectDraw, SetCooperativeLevel, pThis);
|
||
|
|
||
|
hReturn = (*pfnOld)(pThis, hWnd, dwFlags);
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
if (g_lpZBuffer && (g_lpZBuffer->IsLost() == DDERR_SURFACELOST))
|
||
|
{
|
||
|
g_lpZBuffer->Restore();
|
||
|
DPFN( eDbgLevelInfo, "Restoring lost ZBuffer");
|
||
|
}
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
return hReturn;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Allocate global variables.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
BOOL
|
||
|
NOTIFY_FUNCTION(
|
||
|
DWORD fdwReason)
|
||
|
{
|
||
|
if (fdwReason == DLL_PROCESS_ATTACH) {
|
||
|
CSTRING_TRY
|
||
|
{
|
||
|
g_csLastCommand = new CString;
|
||
|
return g_csLastCommand != NULL;
|
||
|
}
|
||
|
CSTRING_CATCH
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
HOOK_BEGIN
|
||
|
|
||
|
CALL_NOTIFY_FUNCTION
|
||
|
|
||
|
APIHOOK_ENTRY_DIRECTX_COMSERVER()
|
||
|
|
||
|
COMHOOK_ENTRY(DirectDraw, IDirectDraw, CreateSurface, 6)
|
||
|
COMHOOK_ENTRY(DirectDraw, IDirectDraw, SetCooperativeLevel, 20)
|
||
|
|
||
|
APIHOOK_ENTRY(WINMM.DLL, mciSendCommandA)
|
||
|
APIHOOK_ENTRY(WINMM.DLL, mciSendStringA)
|
||
|
HOOK_END
|
||
|
|
||
|
IMPLEMENT_SHIM_END
|
||
|
|