|
|
/****************************************************************************
debug.c
winmm debug support module
Copyright (c) Microsoft Corporation 1990 - 1995. All rights reserved
History 10/1/92 Updated for NT by Robin Speed (RobinSp) ****************************************************************************/
#include "winmmi.h"
//#include <wchar.h>
#include <tchar.h>
#include <stdarg.h>
// BUGBUG - no REAL logging for now ! - NT doesn't have Dr Watson !
#define LogParamError(a, b)
//String constants.
#ifdef DEBUG
DBGPARAM dpCurSettings = { TEXT("mmio.lib"), { TEXT("Function"),TEXT("Warning"),TEXT("Error"),TEXT("Verbose"), TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"), TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"), TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a") }, 0x00000006 }; #endif //DEBUG
HANDLE hHeap; PHNDL pHandleList; CRITICAL_SECTION HandleListCritSec; /***************************************************************************
* @doc INTERNAL * * @func HANDLE | NewHandle | allocate a fixed handle in MMSYSTEM's local heap * * @parm UINT | uType | unique id describing handle type * @parm UINT | uSize | size in bytes to be allocated * * @rdesc Returns pointer/handle to memory object * * @comm a standard handle header (HNDL) will be added to the object, * and it will be linked into the list of MMSYSTEM handles. * ***************************************************************************/ HANDLE NewHandle(UINT uType, UINT uSize) { PHNDL pHandle; hHeap = GetProcessHeap();
pHandle = (PHNDL)HeapAlloc(hHeap, 0, sizeof(HNDL) + uSize);
if (pHandle == NULL) { UINT err = GetLastError(); return pHandle; } else { memset(pHandle, 0, sizeof(HNDL) + uSize); EnterCriticalSection(&HandleListCritSec); pHandle->pNext = pHandleList; pHandle->hThread = (HANDLE)GetCurrentThreadId(); // For WOW validation
pHandle->uType = uType; InitializeCriticalSection(&pHandle->CritSec); pHandleList = pHandle; LeaveCriticalSection(&HandleListCritSec); } return PHtoH(pHandle); }
/***************************************************************************
* @doc INTERNAL * * @func HANDLE | FreeHandle | free handle allocated with NewHandle * * @parm HANDLE | hUser | handle returned from NewHandle * * @comm handle will be unlinked from list, and memory will be freed * * ***************************************************************************/
void FreeHandle(HANDLE hUser) { /* Find handle and free from list */
PHNDL pHandle; PHNDL *pSearch;
if (hUser == NULL) { return; }
//
// Point to our handle data
//
pHandle = HtoPH(hUser);
EnterCriticalSection(&HandleListCritSec);
pSearch = &pHandleList;
while (*pSearch != NULL) { if (*pSearch == pHandle) { //
// Found it
// Remove it from the list
//
*pSearch = pHandle->pNext; LeaveCriticalSection(&HandleListCritSec); DeleteCriticalSection(&pHandle->CritSec); pHandle->uType = 0; pHandle->hThread = NULL; pHandle->pNext = NULL; HeapFree(hHeap, 0, (LPTSTR)pHandle); return; } else { pSearch = &(*pSearch)->pNext; } }
DEBUGMSG(1, (TEXT("Freeing handle which is not in the list !"))); DEBUGCHK(FALSE); LeaveCriticalSection(&HandleListCritSec); }
#ifndef UNDER_CExxx
int winmmDebugLevel = 0; /**************************************************************************
@doc INTERNAL
@api void | winmmSetDebugLevel | Set the current debug level
@parm int | iLevel | The new level to set
@rdesc There is no return value
**************************************************************************/
void winmmSetDebugLevel(int level) { #ifdef DEBUG
winmmDebugLevel = level; DEBUGMSG(1, (TEXT("debug level set to %d"), winmmDebugLevel)); #endif
}
UINT inited = 0;
#ifdef DEBUG
extern int mciDebugLevel; #endif
#ifdef DEBUG
void InitDebugLevel(void) { if (!inited) { inited = 1; } DEBUGMSG(2, (TEXT("Starting, debug level=%d"), winmmDebugLevel)); } #endif
#ifdef DEBUG_RETAIL
/***************************************************************************
* @doc INTERNAL WAVE MIDI * * @func BOOL | ValidateHeader | validates a wave or midi date header * * @parm LPVOID | lpHeader| pointer to wave/midi header * @parm UINT | wSize | size of header passed by app * @parm UINT | wType | unique id describing header/handle type * * @rdesc Returns TRUE if <p> is non NULL and <wSize> is the correct size * Returns FALSE otherwise * * @comm if the header is invalid an error will be generated. * ***************************************************************************/
BOOL ValidateHeader(PVOID pHdr, UINT uSize, UINT uType) { // Detect bad header
if (!ValidateWritePointer(pHdr, uSize)) { DebugErr(DBF_ERROR, "Invalid header pointer"); return FALSE; }
// Check type
switch (uType) { case TYPE_MIDIIN: case TYPE_MIDIOUT: case TYPE_MIDISTRM: { PMIDIHDR pHeader = pHdr;
if ((TYPE_MIDISTRM == uType) && uSize < sizeof(MIDIHDR)) { DebugErr(DBF_ERROR, "Invalid header size"); LogParamError(ERR_BAD_VALUE, uSize); return FALSE; } if (pHeader->dwFlags & ~MHDR_VALID) { LogParamError(ERR_BAD_FLAGS, ((PMIDIHDR)pHeader)->dwFlags); return FALSE; }
/* NOTE: For Dreamcast, because we don't actually require a valid lpData
in some cases, don't bother validating */
} break;
default: DEBUGCHK(FALSE); break; }
return TRUE; }
#ifndef USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateReadPointer | validates that a pointer is valid to * read from. * * @parm LPVOID | lpPoint| pointer to validate * @parm DWORD | dLen | supposed length of said pointer * * @rdesc Returns TRUE if <p> is a valid pointer * Returns FALSE if <p> is not a valid pointer * * @comm will generate error if the pointer is invalid * ***************************************************************************/
BOOL ValidateReadPointer(PVOID pPoint, ULONG Len) { // For now just check access to first and last byte
try { volatile BYTE b; b = ((PBYTE)pPoint)[0]; b = ((PBYTE)pPoint)[Len - 1]; } except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_PTR, pPoint); return FALSE; } return TRUE; }
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateWritePointer | validates that a pointer is valid to * write to. * * @parm LPVOID | lpPoint| pointer to validate * @parm DWORD | dLen | supposed length of said pointer * * @rdesc Returns TRUE if <p> is a valid pointer * Returns FALSE if <p> is not a valid pointer * * @comm will generate error if the pointer is invalid * ***************************************************************************/ BOOL ValidateWritePointer(PVOID pPoint, ULONG Len) { // For now just check read and write access to first and last byte
try { volatile BYTE b; b = ((PBYTE)pPoint)[0]; ((PBYTE)pPoint)[0] = b; b = ((PBYTE)pPoint)[Len - 1]; ((PBYTE)pPoint)[Len - 1] = b;
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_PTR, pPoint); return FALSE; } return TRUE; } #endif // USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidDriverCallback | * * validates that a driver callback is valid, to be valid a driver * callback must be a valid window, task, or a function in a FIXED DLL * code segment. * * @parm DWORD | dwCallback | callback to validate * @parm DWORD | wFlags | driver callback flags * * @rdesc Returns 0 if <dwCallback> is a valid callback * Returns error condition if <dwCallback> is not a valid callback ***************************************************************************/
BOOL ValidDriverCallback(HANDLE hCallback, DWORD dwFlags) { switch (dwFlags & DCB_TYPEMASK) { case DCB_WINDOW: if (!IsWindow(hCallback)) { LogParamError(ERR_BAD_HWND, hCallback); return FALSE; } break;
case DCB_EVENT: // BUGBUG enhance ! - how do we test that this is an event handle?
//if (hCallback is not an event)
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_TASK: // BUGBUG enhance !
//if (IsBadCodePtr((FARPROC)hCallback)) {
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_FUNCTION: // BUGBUG enhance !
if (IsBadCodePtr((FARPROC)hCallback)) { LogParamError(ERR_BAD_CALLBACK, hCallback); return FALSE; } break; }
return TRUE; }
#ifndef USE_KERNEL_VALIDATION
/* REVIEW peted: this appears to be unused */
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateStringW | * **************************************************************************/ BOOL ValidateStringW(LPCTSTR pPoint, DWORD Len) { // For now just check access - do a 'strnlen'
try { volatile TCHAR b; LPCTSTR p = pPoint;
while (Len--) { b = *p; if (!b) { break; } p++; }
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_STRING_PTR, pPoint); return FALSE; } return TRUE; } #endif //USE_KERNEL_VALIDATION
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateHandle | validates a handle created with NewHandle * * @parm PHNDL | hLocal | handle returned from NewHandle * @parm UINT | wType | unique id describing handle type * * @rdesc Returns TRUE if <h> is a valid handle of type <wType> * Returns FALSE if <h> is not a valid handle * * @comm if the handle is invalid an error will be generated. * **************************************************************************/ BOOL ValidateHandle(HANDLE hLocal, UINT uType)
{ BOOL OK;
//
// if the handle is less than 64k or a mapper id then
// don't bother with the overhead of the try-except.
//
if ((UINT)hLocal < 0x10000) { LogParamError(ERR_BAD_HANDLE, hLocal); return FALSE; }
try { OK = HtoPH(hLocal)->uType == uType;
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_HANDLE, hLocal); return FALSE; }
return OK; }
#ifdef DEBUG
char * Types[4] = {"Unknown callback type", "Window callback", "Task callback", "Function callback"}; #endif
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateCallbackType | validates a callback address, * window handle, or task handle * * @parm PHNDL | hLocal | handle returned from NewHandle * @parm UINT | wType | unique id describing handle type * * @rdesc Returns TRUE if <h> is a valid handle of type <wType> * Returns FALSE if <h> is not a valid handle * * @comm if the handle is invalid an error will be generated. * **************************************************************************/ BOOL ValidateCallbackType(DWORD dwCallback, UINT uType) {
#define DCALLBACK_WINDOW HIWORD(CALLBACK_WINDOW) // dwCallback is a HWND
#define DCALLBACK_TASK HIWORD(CALLBACK_TASK) // dwCallback is a HTASK
#define DCALLBACK_FUNCTION HIWORD(CALLBACK_FUNCTION) // dwCallback is a FARPROC
#define DCALLBACK_EVENT HIWORD(CALLBACK_EVENT) // dwCallback is an EVENT
UINT type = uType & HIWORD(CALLBACK_TYPEMASK);
#ifdef DEBUG
if (type>5) { type = 0; } DEBUGMSG(3, TEXT("Validating Callback, type=%d (%hs), handle=%8x"), type, Types[type], dwCallback); #endif
switch (type) { case DCALLBACK_WINDOW: return(IsWindow((HWND)dwCallback)); break;
case DCALLBACK_EVENT: { // ?? how to verify that this is an event handle??
//DWORD dwFlags;
//GetHandleInformation((HANDLE)dwCallback, &dwFlags);
return TRUE; } break;
case DCALLBACK_FUNCTION: return(!(IsBadCodePtr((FARPROC)dwCallback))); break;
case DCALLBACK_TASK: if (THREAD_PRIORITY_ERROR_RETURN == GetThreadPriority((HANDLE)dwCallback)) { DEBUGMSG(1, TEXT("Invalid callback task handle")); // I suspect we do not have the correct thread handle, in
// which case we can only return TRUE.
//return(FALSE);
} return(TRUE); break;
} return TRUE; }
/**************************************************************************
@doc INTERNAL
@func void | dout | Output debug string if debug flag is set
@parm LPTSTR | szString **************************************************************************/
#ifdef DEBUG
int fDebug = 1; #else
int fDebug = 0; #endif
//void dout(LPTSTR szString)
//{
// if (fDebug) {
// OutputDebugStringA(szString);
// }
//}
#ifdef LATER
This routine should probably be replaced in the headers by redefining to use OutputDebugString
#endif
#undef OutputDebugStr
// Make our function visible
/*****************************************************************************
* @doc EXTERNAL DDK * * @api void | OutputDebugStr | This function sends a debugging message * directly to the COM1 port or to a secondary monochrome display * adapter. Because it bypasses DOS, it can be called by low-level * callback functions and other code at interrupt time. * * @parm LPTSTR | lpOutputString | Specifies a far pointer to a * null-terminated string. * * @comm This function is available only in the debugging version of * Windows. The DebugOutput keyname in the [mmsystem] * section of SYSTEM.INI controls where the debugging information is * sent. If fDebugOutput is 0, all debug output is disabled. ******************************************************************************/ #endif // DEBUG_RETAIL
#endif // !UNDER_CE
#ifdef DEBUG
/***************************************************************************
@doc INTERNAL
@api void | winmmDbgOut | This function sends output to the current debug output device.
@parm LPTSTR | lpszFormat | Pointer to a printf style format string. @parm ??? | ... | Args.
@rdesc There is no return value.
****************************************************************************/ extern BOOL Quiet = FALSE;
void winmmDbgOut(LPTSTR lpszFormat, ...)
{ TCHAR buf[512]; UINT n; va_list va;
if (Quiet) { return; }
n = wsprintf(buf, TEXT("WINMM: (tid %x) "), GetCurrentThreadId());
va_start(va, lpszFormat); n += wvsprintf(buf+n, lpszFormat, va); va_end(va);
buf[n++] = '\n'; buf[n] = 0; OutputDebugString(buf); Sleep(0); // let terminal catch up
}
/***************************************************************************
@doc INTERNAL
@api void | dDbgAssert | This function prints an assertion message.
@parm LPTSTR | exp | Pointer to the expression string. @parm LPTSTR | file | Pointer to the file name. @parm int | line | The line number.
@rdesc There is no return value.
****************************************************************************/
void dDbgAssert(LPTSTR exp, LPTSTR file, int line) { DEBUGMSG(1, (TEXT("Assertion failure:"))); DEBUGMSG(1, (TEXT(" Exp: %s"), exp)); DEBUGMSG(1, (TEXT(" File: %s, line: %d"), file, line)); DebugBreak(); } #else // Still need to export this thing to help others
void winmmDbgOut(LPTSTR lpszFormat, ...) { }
#endif // DEBUG
|