| //------------------------------------------------------------------------------ | |
| // 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 | |