//------------------------------------------------------------------------------ | |
// File: WXDebug.cpp | |
// | |
// Desc: DirectShow base classes - implements ActiveX system debugging | |
// facilities. | |
// | |
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
//------------------------------------------------------------------------------ | |
#define _WINDLL | |
#include <streams.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <dvdmedia.h> | |
#ifdef DEBUG | |
#ifdef UNICODE | |
#ifndef _UNICODE | |
#define _UNICODE | |
#endif // _UNICODE | |
#endif // UNICODE | |
#endif // DEBUG | |
#include <tchar.h> | |
#include <strsafe.h> | |
#ifdef DEBUG | |
static void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi); | |
static void DisplayRECT(LPCTSTR szLabel, const RECT& rc); | |
// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer. | |
// See the documentation for wsprintf()'s lpOut parameter for more information. | |
const INT iDEBUGINFO = 1024; // Used to format strings | |
/* For every module and executable we store a debugging level for each of | |
the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy | |
to isolate and debug individual modules without seeing everybody elses | |
spurious debug output. The keys are stored in the registry under the | |
HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values | |
NOTE these must be in the same order as their enumeration definition */ | |
const LPCTSTR pKeyNames[] = { | |
TEXT("TIMING"), // Timing and performance measurements | |
TEXT("TRACE"), // General step point call tracing | |
TEXT("MEMORY"), // Memory and object allocation/destruction | |
TEXT("LOCKING"), // Locking/unlocking of critical sections | |
TEXT("ERROR"), // Debug error notification | |
TEXT("CUSTOM1"), | |
TEXT("CUSTOM2"), | |
TEXT("CUSTOM3"), | |
TEXT("CUSTOM4"), | |
TEXT("CUSTOM5") | |
}; | |
const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s"); | |
const TCHAR CAutoTrace::_szLeaving[] = TEXT("<-: %s"); | |
const INT iMAXLEVELS = NUMELMS(pKeyNames); // Maximum debug categories | |
HINSTANCE m_hInst; // Module instance handle | |
TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name | |
DWORD m_Levels[iMAXLEVELS]; // Debug level per category | |
CRITICAL_SECTION m_CSDebug; // Controls access to list | |
DWORD m_dwNextCookie; // Next active object ID | |
ObjectDesc *pListHead = NULL; // First active object | |
DWORD m_dwObjectCount; // Active object count | |
BOOL m_bInit = FALSE; // Have we been initialised | |
HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here | |
DWORD dwWaitTimeout = INFINITE; // Default timeout value | |
DWORD dwTimeOffset; // Time of first DbgLog call | |
bool g_fUseKASSERT = false; // don't create messagebox | |
bool g_fDbgInDllEntryPoint = false; | |
bool g_fAutoRefreshLevels = false; | |
LPCTSTR pBaseKey = TEXT("SOFTWARE\\Microsoft\\DirectShow\\Debug"); | |
LPCTSTR pGlobalKey = TEXT("GLOBAL"); | |
static CHAR *pUnknownName = "UNKNOWN"; | |
LPCTSTR TimeoutName = TEXT("TIMEOUT"); | |
/* This sets the instance handle that the debug library uses to find | |
the module's file name from the Win32 GetModuleFileName function */ | |
void WINAPI DbgInitialise(HINSTANCE hInst) | |
{ | |
InitializeCriticalSection(&m_CSDebug); | |
m_bInit = TRUE; | |
m_hInst = hInst; | |
DbgInitModuleName(); | |
if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0)) | |
DebugBreak(); | |
DbgInitModuleSettings(false); | |
DbgInitGlobalSettings(true); | |
dwTimeOffset = timeGetTime(); | |
} | |
/* This is called to clear up any resources the debug library uses - at the | |
moment we delete our critical section and the object list. The values we | |
retrieve from the registry are all done during initialisation but we don't | |
go looking for update notifications while we are running, if the values | |
are changed then the application has to be restarted to pick them up */ | |
void WINAPI DbgTerminate() | |
{ | |
if (m_hOutput != INVALID_HANDLE_VALUE) { | |
EXECUTE_ASSERT(CloseHandle(m_hOutput)); | |
m_hOutput = INVALID_HANDLE_VALUE; | |
} | |
DeleteCriticalSection(&m_CSDebug); | |
m_bInit = FALSE; | |
} | |
/* This is called by DbgInitLogLevels to read the debug settings | |
for each logging category for this module from the registry */ | |
void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax) | |
{ | |
LONG lReturn; // Create key return value | |
LONG lKeyPos; // Current key category | |
DWORD dwKeySize; // Size of the key value | |
DWORD dwKeyType; // Receives it's type | |
DWORD dwKeyValue; // This fields value | |
/* Try and read a value for each key position in turn */ | |
for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) { | |
dwKeySize = sizeof(DWORD); | |
lReturn = RegQueryValueEx( | |
hKey, // Handle to an open key | |
pKeyNames[lKeyPos], // Subkey name derivation | |
NULL, // Reserved field | |
&dwKeyType, // Returns the field type | |
(LPBYTE) &dwKeyValue, // Returns the field's value | |
&dwKeySize ); // Number of bytes transferred | |
/* If either the key was not available or it was not a DWORD value | |
then we ensure only the high priority debug logging is output | |
but we try and update the field to a zero filled DWORD value */ | |
if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { | |
dwKeyValue = 0; | |
lReturn = RegSetValueEx( | |
hKey, // Handle of an open key | |
pKeyNames[lKeyPos], // Address of subkey name | |
(DWORD) 0, // Reserved field | |
REG_DWORD, // Type of the key field | |
(PBYTE) &dwKeyValue, // Value for the field | |
sizeof(DWORD)); // Size of the field buffer | |
if (lReturn != ERROR_SUCCESS) { | |
DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos])); | |
dwKeyValue = 0; | |
} | |
} | |
if(fTakeMax) | |
{ | |
m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]); | |
} | |
else | |
{ | |
if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) { | |
m_Levels[lKeyPos] = dwKeyValue; | |
} | |
} | |
} | |
/* Read the timeout value for catching hangs */ | |
dwKeySize = sizeof(DWORD); | |
lReturn = RegQueryValueEx( | |
hKey, // Handle to an open key | |
TimeoutName, // Subkey name derivation | |
NULL, // Reserved field | |
&dwKeyType, // Returns the field type | |
(LPBYTE) &dwWaitTimeout, // Returns the field's value | |
&dwKeySize ); // Number of bytes transferred | |
/* If either the key was not available or it was not a DWORD value | |
then we ensure only the high priority debug logging is output | |
but we try and update the field to a zero filled DWORD value */ | |
if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { | |
dwWaitTimeout = INFINITE; | |
lReturn = RegSetValueEx( | |
hKey, // Handle of an open key | |
TimeoutName, // Address of subkey name | |
(DWORD) 0, // Reserved field | |
REG_DWORD, // Type of the key field | |
(PBYTE) &dwWaitTimeout, // Value for the field | |
sizeof(DWORD)); // Size of the field buffer | |
if (lReturn != ERROR_SUCCESS) { | |
DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos])); | |
dwWaitTimeout = INFINITE; | |
} | |
} | |
} | |
void WINAPI DbgOutString(LPCTSTR psz) | |
{ | |
if (m_hOutput != INVALID_HANDLE_VALUE) { | |
UINT cb = lstrlen(psz); | |
DWORD dw; | |
#ifdef UNICODE | |
CHAR szDest[2048]; | |
WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0); | |
WriteFile (m_hOutput, szDest, cb, &dw, NULL); | |
#else | |
WriteFile (m_hOutput, psz, cb, &dw, NULL); | |
#endif | |
} else { | |
OutputDebugString (psz); | |
} | |
} | |
HRESULT DbgUniqueProcessName(LPCTSTR inName, LPTSTR outName) | |
{ | |
HRESULT hr = S_OK; | |
const TCHAR *pIn = inName; | |
int dotPos = -1; | |
//scan the input and record the last '.' position | |
while (*pIn && (pIn - inName) < MAX_PATH) | |
{ | |
if ( TEXT('.') == *pIn ) | |
dotPos = (int)(pIn-inName); | |
++pIn; | |
} | |
if (*pIn) //input should be zero-terminated within MAX_PATH | |
return E_INVALIDARG; | |
DWORD dwProcessId = GetCurrentProcessId(); | |
if (dotPos < 0) | |
{ | |
//no extension in the input, appending process id to the input | |
hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d"), inName, dwProcessId); | |
} | |
else | |
{ | |
TCHAR pathAndBasename[MAX_PATH] = {0}; | |
//there's an extension - zero-terminate the path and basename first by copying | |
hr = StringCchCopyN(pathAndBasename, MAX_PATH, inName, (size_t)dotPos); | |
//re-combine path, basename and extension with processId appended to a basename | |
if (SUCCEEDED(hr)) | |
hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d%s"), pathAndBasename, dwProcessId, inName + dotPos); | |
} | |
return hr; | |
} | |
/* Called by DbgInitGlobalSettings to setup alternate logging destinations | |
*/ | |
void WINAPI DbgInitLogTo ( | |
HKEY hKey) | |
{ | |
LONG lReturn; | |
DWORD dwKeyType; | |
DWORD dwKeySize; | |
TCHAR szFile[MAX_PATH] = {0}; | |
static const TCHAR cszKey[] = TEXT("LogToFile"); | |
dwKeySize = MAX_PATH; | |
lReturn = RegQueryValueEx( | |
hKey, // Handle to an open key | |
cszKey, // Subkey name derivation | |
NULL, // Reserved field | |
&dwKeyType, // Returns the field type | |
(LPBYTE) szFile, // Returns the field's value | |
&dwKeySize); // Number of bytes transferred | |
// create an empty key if it does not already exist | |
// | |
if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ) | |
{ | |
dwKeySize = sizeof(TCHAR); | |
lReturn = RegSetValueEx( | |
hKey, // Handle of an open key | |
cszKey, // Address of subkey name | |
(DWORD) 0, // Reserved field | |
REG_SZ, // Type of the key field | |
(PBYTE)szFile, // Value for the field | |
dwKeySize); // Size of the field buffer | |
} | |
// if an output-to was specified. try to open it. | |
// | |
if (m_hOutput != INVALID_HANDLE_VALUE) { | |
EXECUTE_ASSERT(CloseHandle (m_hOutput)); | |
m_hOutput = INVALID_HANDLE_VALUE; | |
} | |
if (szFile[0] != 0) | |
{ | |
if (!lstrcmpi(szFile, TEXT("Console"))) { | |
m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE); | |
if (m_hOutput == INVALID_HANDLE_VALUE) { | |
AllocConsole (); | |
m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE); | |
} | |
SetConsoleTitle (TEXT("ActiveX Debug Output")); | |
} else if (szFile[0] && | |
lstrcmpi(szFile, TEXT("Debug")) && | |
lstrcmpi(szFile, TEXT("Debugger")) && | |
lstrcmpi(szFile, TEXT("Deb"))) | |
{ | |
m_hOutput = CreateFile(szFile, GENERIC_WRITE, | |
FILE_SHARE_READ, | |
NULL, OPEN_ALWAYS, | |
FILE_ATTRIBUTE_NORMAL, | |
NULL); | |
if (INVALID_HANDLE_VALUE == m_hOutput && | |
GetLastError() == ERROR_SHARING_VIOLATION) | |
{ | |
TCHAR uniqueName[MAX_PATH] = {0}; | |
if (SUCCEEDED(DbgUniqueProcessName(szFile, uniqueName))) | |
{ | |
m_hOutput = CreateFile(uniqueName, GENERIC_WRITE, | |
FILE_SHARE_READ, | |
NULL, OPEN_ALWAYS, | |
FILE_ATTRIBUTE_NORMAL, | |
NULL); | |
} | |
} | |
if (INVALID_HANDLE_VALUE != m_hOutput) | |
{ | |
static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n"); | |
SetFilePointer (m_hOutput, 0, NULL, FILE_END); | |
DbgOutString (cszBar); | |
} | |
} | |
} | |
} | |
/* This is called by DbgInitLogLevels to read the global debug settings for | |
each logging category for this module from the registry. Normally each | |
module has it's own values set for it's different debug categories but | |
setting the global SOFTWARE\Debug\Global applies them to ALL modules */ | |
void WINAPI DbgInitGlobalSettings(bool fTakeMax) | |
{ | |
LONG lReturn; // Create key return value | |
TCHAR szInfo[iDEBUGINFO]; // Constructs key names | |
HKEY hGlobalKey; // Global override key | |
/* Construct the global base key name */ | |
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey); | |
/* Create or open the key for this module */ | |
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key | |
szInfo, // Address of subkey name | |
(DWORD) 0, // Reserved value | |
NULL, // Address of class name | |
(DWORD) 0, // Special options flags | |
GENERIC_READ | GENERIC_WRITE, // Desired security access | |
NULL, // Key security descriptor | |
&hGlobalKey, // Opened handle buffer | |
NULL); // What really happened | |
if (lReturn != ERROR_SUCCESS) { | |
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key | |
szInfo, // Address of subkey name | |
(DWORD) 0, // Reserved value | |
NULL, // Address of class name | |
(DWORD) 0, // Special options flags | |
GENERIC_READ, // Desired security access | |
NULL, // Key security descriptor | |
&hGlobalKey, // Opened handle buffer | |
NULL); // What really happened | |
if (lReturn != ERROR_SUCCESS) { | |
DbgLog((LOG_ERROR,1,TEXT("Could not access GLOBAL module key"))); | |
} | |
return; | |
} | |
DbgInitKeyLevels(hGlobalKey, fTakeMax); | |
RegCloseKey(hGlobalKey); | |
} | |
/* This sets the debugging log levels for the different categories. We start | |
by opening (or creating if not already available) the SOFTWARE\Debug key | |
that all these settings live under. We then look at the global values | |
set under SOFTWARE\Debug\Global which apply on top of the individual | |
module settings. We then load the individual module registry settings */ | |
void WINAPI DbgInitModuleSettings(bool fTakeMax) | |
{ | |
LONG lReturn; // Create key return value | |
TCHAR szInfo[iDEBUGINFO]; // Constructs key names | |
HKEY hModuleKey; // Module key handle | |
/* Construct the base key name */ | |
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,m_ModuleName); | |
/* Create or open the key for this module */ | |
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key | |
szInfo, // Address of subkey name | |
(DWORD) 0, // Reserved value | |
NULL, // Address of class name | |
(DWORD) 0, // Special options flags | |
GENERIC_READ | GENERIC_WRITE, // Desired security access | |
NULL, // Key security descriptor | |
&hModuleKey, // Opened handle buffer | |
NULL); // What really happened | |
if (lReturn != ERROR_SUCCESS) { | |
lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key | |
szInfo, // Address of subkey name | |
(DWORD) 0, // Reserved value | |
NULL, // Address of class name | |
(DWORD) 0, // Special options flags | |
GENERIC_READ, // Desired security access | |
NULL, // Key security descriptor | |
&hModuleKey, // Opened handle buffer | |
NULL); // What really happened | |
if (lReturn != ERROR_SUCCESS) { | |
DbgLog((LOG_ERROR,1,TEXT("Could not access module key"))); | |
} | |
return; | |
} | |
DbgInitLogTo(hModuleKey); | |
DbgInitKeyLevels(hModuleKey, fTakeMax); | |
RegCloseKey(hModuleKey); | |
} | |
/* Initialise the module file name */ | |
void WINAPI DbgInitModuleName() | |
{ | |
TCHAR FullName[iDEBUGINFO]; // Load the full path and module name | |
LPTSTR pName; // Searches from the end for a backslash | |
GetModuleFileName(m_hInst,FullName,iDEBUGINFO); | |
pName = _tcsrchr(FullName,'\\'); | |
if (pName == NULL) { | |
pName = FullName; | |
} else { | |
pName++; | |
} | |
(void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName); | |
} | |
struct MsgBoxMsg | |
{ | |
HWND hwnd; | |
LPCTSTR szTitle; | |
LPCTSTR szMessage; | |
DWORD dwFlags; | |
INT iResult; | |
}; | |
// | |
// create a thread to call MessageBox(). calling MessageBox() on | |
// random threads at bad times can confuse the host (eg IE). | |
// | |
DWORD WINAPI MsgBoxThread( | |
__inout LPVOID lpParameter // thread data | |
) | |
{ | |
MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter; | |
pmsg->iResult = MessageBox( | |
pmsg->hwnd, | |
pmsg->szTitle, | |
pmsg->szMessage, | |
pmsg->dwFlags); | |
return 0; | |
} | |
INT MessageBoxOtherThread( | |
HWND hwnd, | |
LPCTSTR szTitle, | |
LPCTSTR szMessage, | |
DWORD dwFlags) | |
{ | |
if(g_fDbgInDllEntryPoint) | |
{ | |
// can't wait on another thread because we have the loader | |
// lock held in the dll entry point. | |
// This can crash sometimes so just skip it | |
// return MessageBox(hwnd, szTitle, szMessage, dwFlags); | |
return IDCANCEL; | |
} | |
else | |
{ | |
MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0}; | |
DWORD dwid; | |
HANDLE hThread = CreateThread( | |
0, // security | |
0, // stack size | |
MsgBoxThread, | |
(void *)&msg, // arg | |
0, // flags | |
&dwid); | |
if(hThread) | |
{ | |
WaitForSingleObject(hThread, INFINITE); | |
CloseHandle(hThread); | |
return msg.iResult; | |
} | |
// break into debugger on failure. | |
return IDCANCEL; | |
} | |
} | |
/* Displays a message box if the condition evaluated to FALSE */ | |
void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine) | |
{ | |
if(g_fUseKASSERT) | |
{ | |
DbgKernelAssert(pCondition, pFileName, iLine); | |
} | |
else | |
{ | |
TCHAR szInfo[iDEBUGINFO]; | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), | |
pCondition, iLine, pFileName); | |
INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"), | |
MB_SYSTEMMODAL | | |
MB_ICONHAND | | |
MB_YESNOCANCEL | | |
MB_SETFOREGROUND); | |
switch (MsgId) | |
{ | |
case IDNO: /* Kill the application */ | |
FatalAppExit(FALSE, TEXT("Application terminated")); | |
break; | |
case IDCANCEL: /* Break into the debugger */ | |
DebugBreak(); | |
break; | |
case IDYES: /* Ignore assertion continue execution */ | |
break; | |
} | |
} | |
} | |
/* Displays a message box at a break point */ | |
void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine) | |
{ | |
if(g_fUseKASSERT) | |
{ | |
DbgKernelAssert(pCondition, pFileName, iLine); | |
} | |
else | |
{ | |
TCHAR szInfo[iDEBUGINFO]; | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), | |
pCondition, iLine, pFileName); | |
INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"), | |
MB_SYSTEMMODAL | | |
MB_ICONHAND | | |
MB_YESNOCANCEL | | |
MB_SETFOREGROUND); | |
switch (MsgId) | |
{ | |
case IDNO: /* Kill the application */ | |
FatalAppExit(FALSE, TEXT("Application terminated")); | |
break; | |
case IDCANCEL: /* Break into the debugger */ | |
DebugBreak(); | |
break; | |
case IDYES: /* Ignore break point continue execution */ | |
break; | |
} | |
} | |
} | |
void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...) | |
{ | |
// A debug break point message can have at most 2000 characters if | |
// ANSI or UNICODE characters are being used. A debug break point message | |
// can have between 1000 and 2000 double byte characters in it. If a | |
// particular message needs more characters, then the value of this constant | |
// should be increased. | |
const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000; | |
TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE]; | |
va_list va; | |
va_start( va, szFormatString ); | |
HRESULT hr = StringCchVPrintf( szBreakPointMessage, NUMELMS(szBreakPointMessage), szFormatString, va ); | |
va_end(va); | |
if( FAILED(hr) ) { | |
DbgBreak( "ERROR in DbgBreakPoint(). The variable length debug message could not be displayed because StringCchVPrintf() failed." ); | |
return; | |
} | |
::DbgBreakPoint( szBreakPointMessage, pFileName, iLine ); | |
} | |
/* When we initialised the library we stored in the m_Levels array the current | |
debug output level for this module for each of the five categories. When | |
some debug logging is sent to us it can be sent with a combination of the | |
categories (if it is applicable to many for example) in which case we map | |
the type's categories into their current debug levels and see if any of | |
them can be accepted. The function looks at each bit position in turn from | |
the input type field and then compares it's debug level with the modules. | |
A level of 0 means that output is always sent to the debugger. This is | |
due to producing output if the input level is <= m_Levels. | |
*/ | |
BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level) | |
{ | |
if(g_fAutoRefreshLevels) | |
{ | |
// re-read the registry every second. We cannot use RegNotify() to | |
// notice registry changes because it's not available on win9x. | |
static DWORD g_dwLastRefresh = 0; | |
DWORD dwTime = timeGetTime(); | |
if(dwTime - g_dwLastRefresh > 1000) { | |
g_dwLastRefresh = dwTime; | |
// there's a race condition: multiple threads could update the | |
// values. plus read and write not synchronized. no harm | |
// though. | |
DbgInitModuleSettings(false); | |
} | |
} | |
DWORD Mask = 0x01; | |
// If no valid bits are set return FALSE | |
if ((Type & ((1<<iMAXLEVELS)-1))) { | |
// speed up unconditional output. | |
if (0==Level) | |
return(TRUE); | |
for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) { | |
if (Type & Mask) { | |
if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) { | |
return TRUE; | |
} | |
} | |
Mask <<= 1; | |
} | |
} | |
return FALSE; | |
} | |
/* Set debug levels to a given value */ | |
void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level) | |
{ | |
DWORD Mask = 0x01; | |
for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) { | |
if (Type & Mask) { | |
m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET; | |
} | |
Mask <<= 1; | |
} | |
} | |
/* whether to check registry values periodically. this isn't turned | |
automatically because of the potential performance hit. */ | |
void WINAPI DbgSetAutoRefreshLevels(bool fAuto) | |
{ | |
g_fAutoRefreshLevels = fAuto; | |
} | |
#ifdef UNICODE | |
// | |
// warning -- this function is implemented twice for ansi applications | |
// linking to the unicode library | |
// | |
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...) | |
{ | |
/* Check the current level for this type combination */ | |
BOOL bAccept = DbgCheckModuleLevel(Type,Level); | |
if (bAccept == FALSE) { | |
return; | |
} | |
TCHAR szInfo[2000]; | |
/* Format the variable length parameter list */ | |
va_list va; | |
va_start(va, pFormat); | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo), | |
TEXT("%s(tid %x) %8d : "), | |
m_ModuleName, | |
GetCurrentThreadId(), timeGetTime() - dwTimeOffset); | |
CHAR szInfoA[2000]; | |
WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0); | |
(void)StringCchVPrintfA(szInfoA + lstrlenA(szInfoA), NUMELMS(szInfoA) - lstrlenA(szInfoA), pFormat, va); | |
(void)StringCchCatA(szInfoA, NUMELMS(szInfoA), "\r\n"); | |
WCHAR wszOutString[2000]; | |
MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString)); | |
DbgOutString(wszOutString); | |
va_end(va); | |
} | |
void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine) | |
{ | |
if(g_fUseKASSERT) | |
{ | |
DbgKernelAssert(pCondition, pFileName, iLine); | |
} | |
else | |
{ | |
TCHAR szInfo[iDEBUGINFO]; | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"), | |
pCondition, iLine, pFileName); | |
INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"), | |
MB_SYSTEMMODAL | | |
MB_ICONHAND | | |
MB_YESNOCANCEL | | |
MB_SETFOREGROUND); | |
switch (MsgId) | |
{ | |
case IDNO: /* Kill the application */ | |
FatalAppExit(FALSE, TEXT("Application terminated")); | |
break; | |
case IDCANCEL: /* Break into the debugger */ | |
DebugBreak(); | |
break; | |
case IDYES: /* Ignore assertion continue execution */ | |
break; | |
} | |
} | |
} | |
/* Displays a message box at a break point */ | |
void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine) | |
{ | |
if(g_fUseKASSERT) | |
{ | |
DbgKernelAssert(pCondition, pFileName, iLine); | |
} | |
else | |
{ | |
TCHAR szInfo[iDEBUGINFO]; | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"), | |
pCondition, iLine, pFileName); | |
INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"), | |
MB_SYSTEMMODAL | | |
MB_ICONHAND | | |
MB_YESNOCANCEL | | |
MB_SETFOREGROUND); | |
switch (MsgId) | |
{ | |
case IDNO: /* Kill the application */ | |
FatalAppExit(FALSE, TEXT("Application terminated")); | |
break; | |
case IDCANCEL: /* Break into the debugger */ | |
DebugBreak(); | |
break; | |
case IDYES: /* Ignore break point continue execution */ | |
break; | |
} | |
} | |
} | |
void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine) | |
{ | |
DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"), | |
pCondition, iLine, pFileName)); | |
DebugBreak(); | |
} | |
#endif | |
/* Print a formatted string to the debugger prefixed with this module's name | |
Because the COMBASE classes are linked statically every module loaded will | |
have their own copy of this code. It therefore helps if the module name is | |
included on the output so that the offending code can be easily found */ | |
// | |
// warning -- this function is implemented twice for ansi applications | |
// linking to the unicode library | |
// | |
void WINAPI DbgLogInfo(DWORD Type,DWORD Level,LPCTSTR pFormat,...) | |
{ | |
/* Check the current level for this type combination */ | |
BOOL bAccept = DbgCheckModuleLevel(Type,Level); | |
if (bAccept == FALSE) { | |
return; | |
} | |
TCHAR szInfo[2000]; | |
/* Format the variable length parameter list */ | |
va_list va; | |
va_start(va, pFormat); | |
(void)StringCchPrintf(szInfo, NUMELMS(szInfo), | |
TEXT("%s(tid %x) %8d : "), | |
m_ModuleName, | |
GetCurrentThreadId(), timeGetTime() - dwTimeOffset); | |
(void)StringCchVPrintf(szInfo + lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), pFormat, va); | |
(void)StringCchCat(szInfo, NUMELMS(szInfo), TEXT("\r\n")); | |
DbgOutString(szInfo); | |
va_end(va); | |
} | |
/* If we are executing as a pure kernel filter we cannot display message | |
boxes to the user, this provides an alternative which puts the error | |
condition on the debugger output with a suitable eye catching message */ | |
void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine) | |
{ | |
DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"), | |
pCondition, iLine, pFileName)); | |
DebugBreak(); | |
} | |
/* Each time we create an object derived from CBaseObject the constructor will | |
call us to register the creation of the new object. We are passed a string | |
description which we store away. We return a cookie that the constructor | |
uses to identify the object when it is destroyed later on. We update the | |
total number of active objects in the DLL mainly for debugging purposes */ | |
DWORD WINAPI DbgRegisterObjectCreation(LPCSTR szObjectName, | |
LPCWSTR wszObjectName) | |
{ | |
/* If this fires you have a mixed DEBUG/RETAIL build */ | |
ASSERT(!!szObjectName ^ !!wszObjectName); | |
/* Create a place holder for this object description */ | |
ObjectDesc *pObject = new ObjectDesc; | |
ASSERT(pObject); | |
/* It is valid to pass a NULL object name */ | |
if (pObject == NULL) { | |
return FALSE; | |
} | |
/* Check we have been initialised - we may not be initialised when we are | |
being pulled in from an executable which has globally defined objects | |
as they are created by the C++ run time before WinMain is called */ | |
if (m_bInit == FALSE) { | |
DbgInitialise(GetModuleHandle(NULL)); | |
} | |
/* Grab the list critical section */ | |
EnterCriticalSection(&m_CSDebug); | |
/* If no name then default to UNKNOWN */ | |
if (!szObjectName && !wszObjectName) { | |
szObjectName = pUnknownName; | |
} | |
/* Put the new description at the head of the list */ | |
pObject->m_szName = szObjectName; | |
pObject->m_wszName = wszObjectName; | |
pObject->m_dwCookie = ++m_dwNextCookie; | |
pObject->m_pNext = pListHead; | |
pListHead = pObject; | |
m_dwObjectCount++; | |
DWORD ObjectCookie = pObject->m_dwCookie; | |
ASSERT(ObjectCookie); | |
if(wszObjectName) { | |
DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%ls) %d Active"), | |
pObject->m_dwCookie, wszObjectName, m_dwObjectCount)); | |
} else { | |
DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%hs) %d Active"), | |
pObject->m_dwCookie, szObjectName, m_dwObjectCount)); | |
} | |
LeaveCriticalSection(&m_CSDebug); | |
return ObjectCookie; | |
} | |
/* This is called by the CBaseObject destructor when an object is about to be | |
destroyed, we are passed the cookie we returned during construction that | |
identifies this object. We scan the object list for a matching cookie and | |
remove the object if successful. We also update the active object count */ | |
BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie) | |
{ | |
/* Grab the list critical section */ | |
EnterCriticalSection(&m_CSDebug); | |
ObjectDesc *pObject = pListHead; | |
ObjectDesc *pPrevious = NULL; | |
/* Scan the object list looking for a cookie match */ | |
while (pObject) { | |
if (pObject->m_dwCookie == dwCookie) { | |
break; | |
} | |
pPrevious = pObject; | |
pObject = pObject->m_pNext; | |
} | |
if (pObject == NULL) { | |
DbgBreak("Apparently destroying a bogus object"); | |
LeaveCriticalSection(&m_CSDebug); | |
return FALSE; | |
} | |
/* Is the object at the head of the list */ | |
if (pPrevious == NULL) { | |
pListHead = pObject->m_pNext; | |
} else { | |
pPrevious->m_pNext = pObject->m_pNext; | |
} | |
/* Delete the object and update the housekeeping information */ | |
m_dwObjectCount--; | |
if(pObject->m_wszName) { | |
DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"), | |
pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount)); | |
} else { | |
DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"), | |
pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount)); | |
} | |
delete pObject; | |
LeaveCriticalSection(&m_CSDebug); | |
return TRUE; | |
} | |
/* This runs through the active object list displaying their details */ | |
void WINAPI DbgDumpObjectRegister() | |
{ | |
TCHAR szInfo[iDEBUGINFO]; | |
/* Grab the list critical section */ | |
EnterCriticalSection(&m_CSDebug); | |
ObjectDesc *pObject = pListHead; | |
/* Scan the object list displaying the name and cookie */ | |
DbgLog((LOG_MEMORY,2,TEXT(""))); | |
DbgLog((LOG_MEMORY,2,TEXT(" ID Object Description"))); | |
DbgLog((LOG_MEMORY,2,TEXT(""))); | |
while (pObject) { | |
if(pObject->m_wszName) { | |
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName); | |
} else { | |
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName); | |
} | |
DbgLog((LOG_MEMORY,2,szInfo)); | |
pObject = pObject->m_pNext; | |
} | |
(void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount); | |
DbgLog((LOG_MEMORY,2,TEXT(""))); | |
DbgLog((LOG_MEMORY,1,szInfo)); | |
LeaveCriticalSection(&m_CSDebug); | |
} | |
/* Debug infinite wait stuff */ | |
DWORD WINAPI DbgWaitForSingleObject(HANDLE h) | |
{ | |
DWORD dwWaitResult; | |
do { | |
dwWaitResult = WaitForSingleObject(h, dwWaitTimeout); | |
ASSERT(dwWaitResult == WAIT_OBJECT_0); | |
} while (dwWaitResult == WAIT_TIMEOUT); | |
return dwWaitResult; | |
} | |
DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, | |
__in_ecount(nCount) CONST HANDLE *lpHandles, | |
BOOL bWaitAll) | |
{ | |
DWORD dwWaitResult; | |
do { | |
dwWaitResult = WaitForMultipleObjects(nCount, | |
lpHandles, | |
bWaitAll, | |
dwWaitTimeout); | |
ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS); | |
} while (dwWaitResult == WAIT_TIMEOUT); | |
return dwWaitResult; | |
} | |
void WINAPI DbgSetWaitTimeout(DWORD dwTimeout) | |
{ | |
dwWaitTimeout = dwTimeout; | |
} | |
#endif /* DEBUG */ | |
#ifdef _OBJBASE_H_ | |
/* Stuff for printing out our GUID names */ | |
GUID_STRING_ENTRY g_GuidNames[] = { | |
#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ | |
{ #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } }, | |
#include <uuids.h> | |
}; | |
CGuidNameList GuidNames; | |
int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]); | |
char *CGuidNameList::operator [] (const GUID &guid) | |
{ | |
for (int i = 0; i < g_cGuidNames; i++) { | |
if (g_GuidNames[i].guid == guid) { | |
return g_GuidNames[i].szName; | |
} | |
} | |
if (guid == GUID_NULL) { | |
return "GUID_NULL"; | |
} | |
// !!! add something to print FOURCC guids? | |
// shouldn't this print the hex CLSID? | |
return "Unknown GUID Name"; | |
} | |
#endif /* _OBJBASE_H_ */ | |
/* CDisp class - display our data types */ | |
// clashes with REFERENCE_TIME | |
CDisp::CDisp(LONGLONG ll, int Format) | |
{ | |
// note: this could be combined with CDisp(LONGLONG) by | |
// introducing a default format of CDISP_REFTIME | |
LARGE_INTEGER li; | |
li.QuadPart = ll; | |
switch (Format) { | |
case CDISP_DEC: | |
{ | |
TCHAR temp[20]; | |
int pos=20; | |
temp[--pos] = 0; | |
int digit; | |
// always output at least one digit | |
do { | |
// Get the rightmost digit - we only need the low word | |
digit = li.LowPart % 10; | |
li.QuadPart /= 10; | |
temp[--pos] = (TCHAR) digit+L'0'; | |
} while (li.QuadPart); | |
(void)StringCchCopy(m_String, NUMELMS(m_String), temp+pos); | |
break; | |
} | |
case CDISP_HEX: | |
default: | |
(void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart); | |
} | |
}; | |
CDisp::CDisp(REFCLSID clsid) | |
{ | |
#ifdef UNICODE | |
(void)StringFromGUID2(clsid, m_String, NUMELMS(m_String)); | |
#else | |
WCHAR wszTemp[50]; | |
(void)StringFromGUID2(clsid, wszTemp, NUMELMS(wszTemp)); | |
(void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), wszTemp); | |
#endif | |
}; | |
#ifdef __STREAMS__ | |
/* Display stuff */ | |
CDisp::CDisp(CRefTime llTime) | |
{ | |
LONGLONG llDiv; | |
if (llTime < 0) { | |
llTime = -llTime; | |
(void)StringCchCopy(m_String, NUMELMS(m_String), TEXT("-")); | |
} | |
llDiv = (LONGLONG)24 * 3600 * 10000000; | |
if (llTime >= llDiv) { | |
(void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d days "), (LONG)(llTime / llDiv)); | |
llTime = llTime % llDiv; | |
} | |
llDiv = (LONGLONG)3600 * 10000000; | |
if (llTime >= llDiv) { | |
(void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d hrs "), (LONG)(llTime / llDiv)); | |
llTime = llTime % llDiv; | |
} | |
llDiv = (LONGLONG)60 * 10000000; | |
if (llTime >= llDiv) { | |
(void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d mins "), (LONG)(llTime / llDiv)); | |
llTime = llTime % llDiv; | |
} | |
(void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d.%3.3d sec"), | |
(LONG)llTime / 10000000, | |
(LONG)((llTime % 10000000) / 10000)); | |
}; | |
#endif // __STREAMS__ | |
/* Display pin */ | |
CDisp::CDisp(IPin *pPin) | |
{ | |
PIN_INFO pi; | |
TCHAR str[MAX_PIN_NAME]; | |
CLSID clsid; | |
if (pPin) { | |
pPin->QueryPinInfo(&pi); | |
pi.pFilter->GetClassID(&clsid); | |
QueryPinInfoReleaseFilter(pi); | |
#ifndef UNICODE | |
WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1, | |
str, MAX_PIN_NAME, NULL, NULL); | |
#else | |
(void)StringCchCopy(str, NUMELMS(str), pi.achName); | |
#endif | |
} else { | |
(void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin")); | |
} | |
m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64]; | |
if (!m_pString) { | |
return; | |
} | |
(void)StringCchPrintf(m_pString, lstrlen(str) + 64, TEXT("%hs(%s)"), GuidNames[clsid], str); | |
} | |
/* Display filter or pin */ | |
CDisp::CDisp(IUnknown *pUnk) | |
{ | |
IBaseFilter *pf; | |
HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf); | |
if(SUCCEEDED(hr)) | |
{ | |
FILTER_INFO fi; | |
hr = pf->QueryFilterInfo(&fi); | |
if(SUCCEEDED(hr)) | |
{ | |
QueryFilterInfoReleaseGraph(fi); | |
size_t len = lstrlenW(fi.achName) + 1; | |
m_pString = new TCHAR[len]; | |
if(m_pString) | |
{ | |
#ifdef UNICODE | |
(void)StringCchCopy(m_pString, len, fi.achName); | |
#else | |
(void)StringCchPrintf(m_pString, len, "%S", fi.achName); | |
#endif | |
} | |
} | |
pf->Release(); | |
return; | |
} | |
IPin *pp; | |
hr = pUnk->QueryInterface(IID_IPin, (void **)&pp); | |
if(SUCCEEDED(hr)) | |
{ | |
CDisp dummy(static_cast<IPin*>(pp)); | |
pp->Release(); | |
return; | |
} | |
} | |
CDisp::~CDisp() | |
{ | |
} | |
CDispBasic::~CDispBasic() | |
{ | |
if (m_pString != m_String) { | |
delete [] m_pString; | |
} | |
} | |
CDisp::CDisp(double d) | |
{ | |
(void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000)); | |
} | |
/* If built for debug this will display the media type details. We convert the | |
major and subtypes into strings and also ask the base classes for a string | |
description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit | |
We also display the fields in the BITMAPINFOHEADER structure, this should | |
succeed as we do not accept input types unless the format is big enough */ | |
#ifdef DEBUG | |
void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn) | |
{ | |
/* Dump the GUID types and a short description */ | |
DbgLog((LOG_TRACE,5,TEXT(""))); | |
DbgLog((LOG_TRACE,2,TEXT("%s M type %hs S type %hs"), label, | |
GuidNames[pmtIn->majortype], | |
GuidNames[pmtIn->subtype])); | |
DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype))); | |
/* Dump the generic media types */ | |
if (pmtIn->bTemporalCompression) { | |
DbgLog((LOG_TRACE,5,TEXT("Temporally compressed"))); | |
} else { | |
DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed"))); | |
} | |
if (pmtIn->bFixedSizeSamples) { | |
DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize)); | |
} else { | |
DbgLog((LOG_TRACE,5,TEXT("Variable size samples"))); | |
} | |
if (pmtIn->formattype == FORMAT_VideoInfo) { | |
VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat; | |
DisplayRECT(TEXT("Source rectangle"),pVideoInfo->rcSource); | |
DisplayRECT(TEXT("Target rectangle"),pVideoInfo->rcTarget); | |
DisplayBITMAPINFO(HEADER(pmtIn->pbFormat)); | |
} if (pmtIn->formattype == FORMAT_VideoInfo2) { | |
VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pmtIn->pbFormat; | |
DisplayRECT(TEXT("Source rectangle"),pVideoInfo2->rcSource); | |
DisplayRECT(TEXT("Target rectangle"),pVideoInfo2->rcTarget); | |
DbgLog((LOG_TRACE, 5, TEXT("Aspect Ratio: %d:%d"), | |
pVideoInfo2->dwPictAspectRatioX, | |
pVideoInfo2->dwPictAspectRatioY)); | |
DisplayBITMAPINFO(&pVideoInfo2->bmiHeader); | |
} else if (pmtIn->majortype == MEDIATYPE_Audio) { | |
DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), | |
GuidNames[pmtIn->formattype])); | |
DbgLog((LOG_TRACE,2,TEXT(" Subtype %hs"), | |
GuidNames[pmtIn->subtype])); | |
if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet) | |
&& (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT))) | |
{ | |
/* Dump the contents of the WAVEFORMATEX type-specific format structure */ | |
WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat; | |
DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag)); | |
DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels)); | |
DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec)); | |
DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec)); | |
DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign)); | |
DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample)); | |
/* PCM uses a WAVEFORMAT and does not have the extra size field */ | |
if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) { | |
DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize)); | |
} | |
} else { | |
} | |
} else { | |
DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), | |
GuidNames[pmtIn->formattype])); | |
} | |
} | |
void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi) | |
{ | |
DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize)); | |
if (pbmi->biCompression < 256) { | |
DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit (%d)"), | |
pbmi->biWidth, pbmi->biHeight, | |
pbmi->biBitCount, pbmi->biCompression)); | |
} else { | |
DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"), | |
pbmi->biWidth, pbmi->biHeight, | |
pbmi->biBitCount, &pbmi->biCompression)); | |
} | |
DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage)); | |
DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes)); | |
DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter)); | |
DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter)); | |
DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed)); | |
} | |
void DisplayRECT(LPCTSTR szLabel, const RECT& rc) | |
{ | |
DbgLog((LOG_TRACE,5,TEXT("%s (Left %d Top %d Right %d Bottom %d)"), | |
szLabel, | |
rc.left, | |
rc.top, | |
rc.right, | |
rc.bottom)); | |
} | |
void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel) | |
{ | |
if( !pGraph ) | |
{ | |
return; | |
} | |
IEnumFilters *pFilters; | |
DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph)); | |
if (FAILED(pGraph->EnumFilters(&pFilters))) { | |
DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!"))); | |
} | |
IBaseFilter *pFilter; | |
ULONG n; | |
while (pFilters->Next(1, &pFilter, &n) == S_OK) { | |
FILTER_INFO info; | |
if (FAILED(pFilter->QueryFilterInfo(&info))) { | |
DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%p] -- failed QueryFilterInfo"), pFilter)); | |
} else { | |
QueryFilterInfoReleaseGraph(info); | |
// !!! should QueryVendorInfo here! | |
DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%p] '%ls'"), pFilter, info.achName)); | |
IEnumPins *pins; | |
if (FAILED(pFilter->EnumPins(&pins))) { | |
DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!"))); | |
} else { | |
IPin *pPin; | |
while (pins->Next(1, &pPin, &n) == S_OK) { | |
PIN_INFO pinInfo; | |
if (FAILED(pPin->QueryPinInfo(&pinInfo))) { | |
DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin)); | |
} else { | |
QueryPinInfoReleaseFilter(pinInfo); | |
IPin *pPinConnected = NULL; | |
HRESULT hr = pPin->ConnectedTo(&pPinConnected); | |
if (pPinConnected) { | |
DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%p] '%ls' [%sput]") | |
TEXT(" Connected to pin [%p]"), | |
pPin, pinInfo.achName, | |
pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"), | |
pPinConnected)); | |
pPinConnected->Release(); | |
// perhaps we should really dump the type both ways as a sanity | |
// check? | |
if (pinInfo.dir == PINDIR_OUTPUT) { | |
AM_MEDIA_TYPE mt; | |
hr = pPin->ConnectionMediaType(&mt); | |
if (SUCCEEDED(hr)) { | |
DisplayType(TEXT("Connection type"), &mt); | |
FreeMediaType(mt); | |
} | |
} | |
} else { | |
DbgLog((LOG_TRACE,dwLevel, | |
TEXT(" Pin [%x] '%ls' [%sput]"), | |
pPin, pinInfo.achName, | |
pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"))); | |
} | |
} | |
pPin->Release(); | |
} | |
pins->Release(); | |
} | |
} | |
pFilter->Release(); | |
} | |
pFilters->Release(); | |
} | |
#endif | |