1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes - implements ActiveX system debugging
7 // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
8 //------------------------------------------------------------------------------
10 #include <pjmedia-videodev/config.h>
12 #if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
33 static void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi);
34 static void DisplayRECT(LPCTSTR szLabel, const RECT& rc);
36 // The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.
37 // See the documentation for wsprintf()'s lpOut parameter for more information.
38 const INT iDEBUGINFO = 1024; // Used to format strings
40 /* For every module and executable we store a debugging level for each of
41 the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy
42 to isolate and debug individual modules without seeing everybody elses
43 spurious debug output. The keys are stored in the registry under the
44 HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values
45 NOTE these must be in the same order as their enumeration definition */
47 const LPCTSTR pKeyNames[] = {
48 TEXT("TIMING"), // Timing and performance measurements
49 TEXT("TRACE"), // General step point call tracing
50 TEXT("MEMORY"), // Memory and object allocation/destruction
51 TEXT("LOCKING"), // Locking/unlocking of critical sections
52 TEXT("ERROR"), // Debug error notification
60 const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s");
61 const TCHAR CAutoTrace::_szLeaving[] = TEXT("<-: %s");
63 const INT iMAXLEVELS = NUMELMS(pKeyNames); // Maximum debug categories
65 HINSTANCE m_hInst; // Module instance handle
66 TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name
67 DWORD m_Levels[iMAXLEVELS]; // Debug level per category
68 CRITICAL_SECTION m_CSDebug; // Controls access to list
69 DWORD m_dwNextCookie; // Next active object ID
70 ObjectDesc *pListHead = NULL; // First active object
71 DWORD m_dwObjectCount; // Active object count
72 BOOL m_bInit = FALSE; // Have we been initialised
73 HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here
74 DWORD dwWaitTimeout = INFINITE; // Default timeout value
75 DWORD dwTimeOffset; // Time of first DbgLog call
76 bool g_fUseKASSERT = false; // don't create messagebox
77 bool g_fDbgInDllEntryPoint = false;
78 bool g_fAutoRefreshLevels = false;
80 LPCTSTR pBaseKey = TEXT("SOFTWARE\\Microsoft\\DirectShow\\Debug");
81 LPCTSTR pGlobalKey = TEXT("GLOBAL");
82 static CHAR *pUnknownName = "UNKNOWN";
84 LPCTSTR TimeoutName = TEXT("TIMEOUT");
86 /* This sets the instance handle that the debug library uses to find
87 the module's file name from the Win32 GetModuleFileName function */
89 void WINAPI DbgInitialise(HINSTANCE hInst)
91 InitializeCriticalSection(&m_CSDebug);
96 if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))
98 DbgInitModuleSettings(false);
99 DbgInitGlobalSettings(true);
100 dwTimeOffset = timeGetTime();
104 /* This is called to clear up any resources the debug library uses - at the
105 moment we delete our critical section and the object list. The values we
106 retrieve from the registry are all done during initialisation but we don't
107 go looking for update notifications while we are running, if the values
108 are changed then the application has to be restarted to pick them up */
110 void WINAPI DbgTerminate()
112 if (m_hOutput != INVALID_HANDLE_VALUE) {
113 EXECUTE_ASSERT(CloseHandle(m_hOutput));
114 m_hOutput = INVALID_HANDLE_VALUE;
116 DeleteCriticalSection(&m_CSDebug);
121 /* This is called by DbgInitLogLevels to read the debug settings
122 for each logging category for this module from the registry */
124 void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax)
126 LONG lReturn; // Create key return value
127 LONG lKeyPos; // Current key category
128 DWORD dwKeySize; // Size of the key value
129 DWORD dwKeyType; // Receives it's type
130 DWORD dwKeyValue; // This fields value
132 /* Try and read a value for each key position in turn */
133 for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
135 dwKeySize = sizeof(DWORD);
136 lReturn = RegQueryValueEx(
137 hKey, // Handle to an open key
138 pKeyNames[lKeyPos], // Subkey name derivation
139 NULL, // Reserved field
140 &dwKeyType, // Returns the field type
141 (LPBYTE) &dwKeyValue, // Returns the field's value
142 &dwKeySize ); // Number of bytes transferred
144 /* If either the key was not available or it was not a DWORD value
145 then we ensure only the high priority debug logging is output
146 but we try and update the field to a zero filled DWORD value */
148 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) {
151 lReturn = RegSetValueEx(
152 hKey, // Handle of an open key
153 pKeyNames[lKeyPos], // Address of subkey name
154 (DWORD) 0, // Reserved field
155 REG_DWORD, // Type of the key field
156 (PBYTE) &dwKeyValue, // Value for the field
157 sizeof(DWORD)); // Size of the field buffer
159 if (lReturn != ERROR_SUCCESS) {
160 DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
166 m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);
170 if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) {
171 m_Levels[lKeyPos] = dwKeyValue;
176 /* Read the timeout value for catching hangs */
177 dwKeySize = sizeof(DWORD);
178 lReturn = RegQueryValueEx(
179 hKey, // Handle to an open key
180 TimeoutName, // Subkey name derivation
181 NULL, // Reserved field
182 &dwKeyType, // Returns the field type
183 (LPBYTE) &dwWaitTimeout, // Returns the field's value
184 &dwKeySize ); // Number of bytes transferred
186 /* If either the key was not available or it was not a DWORD value
187 then we ensure only the high priority debug logging is output
188 but we try and update the field to a zero filled DWORD value */
190 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) {
192 dwWaitTimeout = INFINITE;
193 lReturn = RegSetValueEx(
194 hKey, // Handle of an open key
195 TimeoutName, // Address of subkey name
196 (DWORD) 0, // Reserved field
197 REG_DWORD, // Type of the key field
198 (PBYTE) &dwWaitTimeout, // Value for the field
199 sizeof(DWORD)); // Size of the field buffer
201 if (lReturn != ERROR_SUCCESS) {
202 DbgLog((LOG_ERROR,1,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
203 dwWaitTimeout = INFINITE;
208 void WINAPI DbgOutString(LPCTSTR psz)
210 if (m_hOutput != INVALID_HANDLE_VALUE) {
211 UINT cb = lstrlen(psz);
215 WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0);
216 WriteFile (m_hOutput, szDest, cb, &dw, NULL);
218 WriteFile (m_hOutput, psz, cb, &dw, NULL);
221 OutputDebugString (psz);
228 HRESULT DbgUniqueProcessName(LPCTSTR inName, LPTSTR outName)
231 const TCHAR *pIn = inName;
234 //scan the input and record the last '.' position
235 while (*pIn && (pIn - inName) < MAX_PATH)
237 if ( TEXT('.') == *pIn )
238 dotPos = (int)(pIn-inName);
242 if (*pIn) //input should be zero-terminated within MAX_PATH
245 DWORD dwProcessId = GetCurrentProcessId();
249 //no extension in the input, appending process id to the input
250 hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d"), inName, dwProcessId);
254 TCHAR pathAndBasename[MAX_PATH] = {0};
256 //there's an extension - zero-terminate the path and basename first by copying
257 hr = StringCchCopyN(pathAndBasename, MAX_PATH, inName, (size_t)dotPos);
259 //re-combine path, basename and extension with processId appended to a basename
261 hr = StringCchPrintf(outName, MAX_PATH, TEXT("%s_%d%s"), pathAndBasename, dwProcessId, inName + dotPos);
268 /* Called by DbgInitGlobalSettings to setup alternate logging destinations
271 void WINAPI DbgInitLogTo (
277 TCHAR szFile[MAX_PATH] = {0};
278 static const TCHAR cszKey[] = TEXT("LogToFile");
280 dwKeySize = MAX_PATH;
281 lReturn = RegQueryValueEx(
282 hKey, // Handle to an open key
283 cszKey, // Subkey name derivation
284 NULL, // Reserved field
285 &dwKeyType, // Returns the field type
286 (LPBYTE) szFile, // Returns the field's value
287 &dwKeySize); // Number of bytes transferred
289 // create an empty key if it does not already exist
291 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)
293 dwKeySize = sizeof(TCHAR);
294 lReturn = RegSetValueEx(
295 hKey, // Handle of an open key
296 cszKey, // Address of subkey name
297 (DWORD) 0, // Reserved field
298 REG_SZ, // Type of the key field
299 (PBYTE)szFile, // Value for the field
300 dwKeySize); // Size of the field buffer
303 // if an output-to was specified. try to open it.
305 if (m_hOutput != INVALID_HANDLE_VALUE) {
306 EXECUTE_ASSERT(CloseHandle (m_hOutput));
307 m_hOutput = INVALID_HANDLE_VALUE;
311 if (!lstrcmpi(szFile, TEXT("Console"))) {
312 m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
313 if (m_hOutput == INVALID_HANDLE_VALUE) {
315 m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
317 SetConsoleTitle (TEXT("ActiveX Debug Output"));
318 } else if (szFile[0] &&
319 lstrcmpi(szFile, TEXT("Debug")) &&
320 lstrcmpi(szFile, TEXT("Debugger")) &&
321 lstrcmpi(szFile, TEXT("Deb")))
323 m_hOutput = CreateFile(szFile, GENERIC_WRITE,
326 FILE_ATTRIBUTE_NORMAL,
329 if (INVALID_HANDLE_VALUE == m_hOutput &&
330 GetLastError() == ERROR_SHARING_VIOLATION)
332 TCHAR uniqueName[MAX_PATH] = {0};
333 if (SUCCEEDED(DbgUniqueProcessName(szFile, uniqueName)))
335 m_hOutput = CreateFile(uniqueName, GENERIC_WRITE,
338 FILE_ATTRIBUTE_NORMAL,
343 if (INVALID_HANDLE_VALUE != m_hOutput)
345 static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");
346 SetFilePointer (m_hOutput, 0, NULL, FILE_END);
347 DbgOutString (cszBar);
355 /* This is called by DbgInitLogLevels to read the global debug settings for
356 each logging category for this module from the registry. Normally each
357 module has it's own values set for it's different debug categories but
358 setting the global SOFTWARE\Debug\Global applies them to ALL modules */
360 void WINAPI DbgInitGlobalSettings(bool fTakeMax)
362 LONG lReturn; // Create key return value
363 TCHAR szInfo[iDEBUGINFO]; // Constructs key names
364 HKEY hGlobalKey; // Global override key
366 /* Construct the global base key name */
367 (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey);
369 /* Create or open the key for this module */
370 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
371 szInfo, // Address of subkey name
372 (DWORD) 0, // Reserved value
373 NULL, // Address of class name
374 (DWORD) 0, // Special options flags
375 GENERIC_READ | GENERIC_WRITE, // Desired security access
376 NULL, // Key security descriptor
377 &hGlobalKey, // Opened handle buffer
378 NULL); // What really happened
380 if (lReturn != ERROR_SUCCESS) {
381 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
382 szInfo, // Address of subkey name
383 (DWORD) 0, // Reserved value
384 NULL, // Address of class name
385 (DWORD) 0, // Special options flags
386 GENERIC_READ, // Desired security access
387 NULL, // Key security descriptor
388 &hGlobalKey, // Opened handle buffer
389 NULL); // What really happened
390 if (lReturn != ERROR_SUCCESS) {
391 DbgLog((LOG_ERROR,1,TEXT("Could not access GLOBAL module key")));
396 DbgInitKeyLevels(hGlobalKey, fTakeMax);
397 RegCloseKey(hGlobalKey);
401 /* This sets the debugging log levels for the different categories. We start
402 by opening (or creating if not already available) the SOFTWARE\Debug key
403 that all these settings live under. We then look at the global values
404 set under SOFTWARE\Debug\Global which apply on top of the individual
405 module settings. We then load the individual module registry settings */
407 void WINAPI DbgInitModuleSettings(bool fTakeMax)
409 LONG lReturn; // Create key return value
410 TCHAR szInfo[iDEBUGINFO]; // Constructs key names
411 HKEY hModuleKey; // Module key handle
413 /* Construct the base key name */
414 (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,m_ModuleName);
416 /* Create or open the key for this module */
417 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
418 szInfo, // Address of subkey name
419 (DWORD) 0, // Reserved value
420 NULL, // Address of class name
421 (DWORD) 0, // Special options flags
422 GENERIC_READ | GENERIC_WRITE, // Desired security access
423 NULL, // Key security descriptor
424 &hModuleKey, // Opened handle buffer
425 NULL); // What really happened
427 if (lReturn != ERROR_SUCCESS) {
428 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
429 szInfo, // Address of subkey name
430 (DWORD) 0, // Reserved value
431 NULL, // Address of class name
432 (DWORD) 0, // Special options flags
433 GENERIC_READ, // Desired security access
434 NULL, // Key security descriptor
435 &hModuleKey, // Opened handle buffer
436 NULL); // What really happened
437 if (lReturn != ERROR_SUCCESS) {
438 DbgLog((LOG_ERROR,1,TEXT("Could not access module key")));
443 DbgInitLogTo(hModuleKey);
444 DbgInitKeyLevels(hModuleKey, fTakeMax);
445 RegCloseKey(hModuleKey);
449 /* Initialise the module file name */
451 void WINAPI DbgInitModuleName()
453 TCHAR FullName[iDEBUGINFO]; // Load the full path and module name
454 LPTSTR pName; // Searches from the end for a backslash
456 GetModuleFileName(m_hInst,FullName,iDEBUGINFO);
457 pName = _tcsrchr(FullName,'\\');
463 (void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName);
476 // create a thread to call MessageBox(). calling MessageBox() on
477 // random threads at bad times can confuse the host (eg IE).
479 DWORD WINAPI MsgBoxThread(
480 __inout LPVOID lpParameter // thread data
483 MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter;
484 pmsg->iResult = MessageBox(
493 INT MessageBoxOtherThread(
499 if(g_fDbgInDllEntryPoint)
501 // can't wait on another thread because we have the loader
502 // lock held in the dll entry point.
503 // This can crash sometimes so just skip it
504 // return MessageBox(hwnd, szTitle, szMessage, dwFlags);
509 MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0};
511 HANDLE hThread = CreateThread(
520 WaitForSingleObject(hThread, INFINITE);
521 CloseHandle(hThread);
525 // break into debugger on failure.
530 /* Displays a message box if the condition evaluated to FALSE */
532 void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
536 DbgKernelAssert(pCondition, pFileName, iLine);
541 TCHAR szInfo[iDEBUGINFO];
543 (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
544 pCondition, iLine, pFileName);
546 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
553 case IDNO: /* Kill the application */
555 FatalAppExit(FALSE, TEXT("Application terminated"));
558 case IDCANCEL: /* Break into the debugger */
563 case IDYES: /* Ignore assertion continue execution */
569 /* Displays a message box at a break point */
571 void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
575 DbgKernelAssert(pCondition, pFileName, iLine);
579 TCHAR szInfo[iDEBUGINFO];
581 (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
582 pCondition, iLine, pFileName);
584 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
591 case IDNO: /* Kill the application */
593 FatalAppExit(FALSE, TEXT("Application terminated"));
596 case IDCANCEL: /* Break into the debugger */
601 case IDYES: /* Ignore break point continue execution */
607 void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...)
609 // A debug break point message can have at most 2000 characters if
610 // ANSI or UNICODE characters are being used. A debug break point message
611 // can have between 1000 and 2000 double byte characters in it. If a
612 // particular message needs more characters, then the value of this constant
613 // should be increased.
614 const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000;
616 TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE];
619 va_start( va, szFormatString );
621 HRESULT hr = StringCchVPrintf( szBreakPointMessage, NUMELMS(szBreakPointMessage), szFormatString, va );
626 DbgBreak( "ERROR in DbgBreakPoint(). The variable length debug message could not be displayed because StringCchVPrintf() failed." );
630 ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine );
634 /* When we initialised the library we stored in the m_Levels array the current
635 debug output level for this module for each of the five categories. When
636 some debug logging is sent to us it can be sent with a combination of the
637 categories (if it is applicable to many for example) in which case we map
638 the type's categories into their current debug levels and see if any of
639 them can be accepted. The function looks at each bit position in turn from
640 the input type field and then compares it's debug level with the modules.
642 A level of 0 means that output is always sent to the debugger. This is
643 due to producing output if the input level is <= m_Levels.
647 BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level)
649 if(g_fAutoRefreshLevels)
651 // re-read the registry every second. We cannot use RegNotify() to
652 // notice registry changes because it's not available on win9x.
653 static DWORD g_dwLastRefresh = 0;
654 DWORD dwTime = timeGetTime();
655 if(dwTime - g_dwLastRefresh > 1000) {
656 g_dwLastRefresh = dwTime;
658 // there's a race condition: multiple threads could update the
659 // values. plus read and write not synchronized. no harm
661 DbgInitModuleSettings(false);
668 // If no valid bits are set return FALSE
669 if ((Type & ((1<<iMAXLEVELS)-1))) {
671 // speed up unconditional output.
675 for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
677 if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) {
688 /* Set debug levels to a given value */
690 void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level)
694 for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
696 m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET;
702 /* whether to check registry values periodically. this isn't turned
703 automatically because of the potential performance hit. */
704 void WINAPI DbgSetAutoRefreshLevels(bool fAuto)
706 g_fAutoRefreshLevels = fAuto;
711 // warning -- this function is implemented twice for ansi applications
712 // linking to the unicode library
714 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...)
716 /* Check the current level for this type combination */
718 BOOL bAccept = DbgCheckModuleLevel(Type,Level);
719 if (bAccept == FALSE) {
725 /* Format the variable length parameter list */
728 va_start(va, pFormat);
730 (void)StringCchPrintf(szInfo, NUMELMS(szInfo),
731 TEXT("%s(tid %x) %8d : "),
733 GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
736 WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0);
738 (void)StringCchVPrintfA(szInfoA + lstrlenA(szInfoA), NUMELMS(szInfoA) - lstrlenA(szInfoA), pFormat, va);
739 (void)StringCchCatA(szInfoA, NUMELMS(szInfoA), "\r\n");
741 WCHAR wszOutString[2000];
742 MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString));
743 DbgOutString(wszOutString);
748 void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
752 DbgKernelAssert(pCondition, pFileName, iLine);
757 TCHAR szInfo[iDEBUGINFO];
759 (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
760 pCondition, iLine, pFileName);
762 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
769 case IDNO: /* Kill the application */
771 FatalAppExit(FALSE, TEXT("Application terminated"));
774 case IDCANCEL: /* Break into the debugger */
779 case IDYES: /* Ignore assertion continue execution */
785 /* Displays a message box at a break point */
787 void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
791 DbgKernelAssert(pCondition, pFileName, iLine);
795 TCHAR szInfo[iDEBUGINFO];
797 (void)StringCchPrintf(szInfo, NUMELMS(szInfo),TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
798 pCondition, iLine, pFileName);
800 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
807 case IDNO: /* Kill the application */
809 FatalAppExit(FALSE, TEXT("Application terminated"));
812 case IDCANCEL: /* Break into the debugger */
817 case IDYES: /* Ignore break point continue execution */
823 void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine)
825 DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"),
826 pCondition, iLine, pFileName));
832 /* Print a formatted string to the debugger prefixed with this module's name
833 Because the COMBASE classes are linked statically every module loaded will
834 have their own copy of this code. It therefore helps if the module name is
835 included on the output so that the offending code can be easily found */
838 // warning -- this function is implemented twice for ansi applications
839 // linking to the unicode library
841 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,LPCTSTR pFormat,...)
844 /* Check the current level for this type combination */
846 BOOL bAccept = DbgCheckModuleLevel(Type,Level);
847 if (bAccept == FALSE) {
853 /* Format the variable length parameter list */
856 va_start(va, pFormat);
858 (void)StringCchPrintf(szInfo, NUMELMS(szInfo),
859 TEXT("%s(tid %x) %8d : "),
861 GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
863 (void)StringCchVPrintf(szInfo + lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), pFormat, va);
864 (void)StringCchCat(szInfo, NUMELMS(szInfo), TEXT("\r\n"));
865 DbgOutString(szInfo);
871 /* If we are executing as a pure kernel filter we cannot display message
872 boxes to the user, this provides an alternative which puts the error
873 condition on the debugger output with a suitable eye catching message */
875 void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine)
877 DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),
878 pCondition, iLine, pFileName));
884 /* Each time we create an object derived from CBaseObject the constructor will
885 call us to register the creation of the new object. We are passed a string
886 description which we store away. We return a cookie that the constructor
887 uses to identify the object when it is destroyed later on. We update the
888 total number of active objects in the DLL mainly for debugging purposes */
890 DWORD WINAPI DbgRegisterObjectCreation(LPCSTR szObjectName,
891 LPCWSTR wszObjectName)
893 /* If this fires you have a mixed DEBUG/RETAIL build */
895 ASSERT(!!szObjectName ^ !!wszObjectName);
897 /* Create a place holder for this object description */
899 ObjectDesc *pObject = new ObjectDesc;
902 /* It is valid to pass a NULL object name */
903 if (pObject == NULL) {
907 /* Check we have been initialised - we may not be initialised when we are
908 being pulled in from an executable which has globally defined objects
909 as they are created by the C++ run time before WinMain is called */
911 if (m_bInit == FALSE) {
912 DbgInitialise(GetModuleHandle(NULL));
915 /* Grab the list critical section */
916 EnterCriticalSection(&m_CSDebug);
918 /* If no name then default to UNKNOWN */
919 if (!szObjectName && !wszObjectName) {
920 szObjectName = pUnknownName;
923 /* Put the new description at the head of the list */
925 pObject->m_szName = szObjectName;
926 pObject->m_wszName = wszObjectName;
927 pObject->m_dwCookie = ++m_dwNextCookie;
928 pObject->m_pNext = pListHead;
933 DWORD ObjectCookie = pObject->m_dwCookie;
934 ASSERT(ObjectCookie);
937 DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%ls) %d Active"),
938 pObject->m_dwCookie, wszObjectName, m_dwObjectCount));
940 DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%hs) %d Active"),
941 pObject->m_dwCookie, szObjectName, m_dwObjectCount));
944 LeaveCriticalSection(&m_CSDebug);
949 /* This is called by the CBaseObject destructor when an object is about to be
950 destroyed, we are passed the cookie we returned during construction that
951 identifies this object. We scan the object list for a matching cookie and
952 remove the object if successful. We also update the active object count */
954 BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie)
956 /* Grab the list critical section */
957 EnterCriticalSection(&m_CSDebug);
959 ObjectDesc *pObject = pListHead;
960 ObjectDesc *pPrevious = NULL;
962 /* Scan the object list looking for a cookie match */
965 if (pObject->m_dwCookie == dwCookie) {
969 pObject = pObject->m_pNext;
972 if (pObject == NULL) {
973 DbgBreak("Apparently destroying a bogus object");
974 LeaveCriticalSection(&m_CSDebug);
978 /* Is the object at the head of the list */
980 if (pPrevious == NULL) {
981 pListHead = pObject->m_pNext;
983 pPrevious->m_pNext = pObject->m_pNext;
986 /* Delete the object and update the housekeeping information */
990 if(pObject->m_wszName) {
991 DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"),
992 pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount));
994 DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"),
995 pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount));
999 LeaveCriticalSection(&m_CSDebug);
1004 /* This runs through the active object list displaying their details */
1006 void WINAPI DbgDumpObjectRegister()
1008 TCHAR szInfo[iDEBUGINFO];
1010 /* Grab the list critical section */
1012 EnterCriticalSection(&m_CSDebug);
1013 ObjectDesc *pObject = pListHead;
1015 /* Scan the object list displaying the name and cookie */
1017 DbgLog((LOG_MEMORY,2,TEXT("")));
1018 DbgLog((LOG_MEMORY,2,TEXT(" ID Object Description")));
1019 DbgLog((LOG_MEMORY,2,TEXT("")));
1022 if(pObject->m_wszName) {
1023 (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName);
1025 (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%5d (%p) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName);
1027 DbgLog((LOG_MEMORY,2,szInfo));
1028 pObject = pObject->m_pNext;
1031 (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount);
1032 DbgLog((LOG_MEMORY,2,TEXT("")));
1033 DbgLog((LOG_MEMORY,1,szInfo));
1034 LeaveCriticalSection(&m_CSDebug);
1037 /* Debug infinite wait stuff */
1038 DWORD WINAPI DbgWaitForSingleObject(HANDLE h)
1042 dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);
1043 ASSERT(dwWaitResult == WAIT_OBJECT_0);
1044 } while (dwWaitResult == WAIT_TIMEOUT);
1045 return dwWaitResult;
1047 DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
1048 __in_ecount(nCount) CONST HANDLE *lpHandles,
1053 dwWaitResult = WaitForMultipleObjects(nCount,
1057 ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);
1058 } while (dwWaitResult == WAIT_TIMEOUT);
1059 return dwWaitResult;
1062 void WINAPI DbgSetWaitTimeout(DWORD dwTimeout)
1064 dwWaitTimeout = dwTimeout;
1071 /* Stuff for printing out our GUID names */
1073 GUID_STRING_ENTRY g_GuidNames[] = {
1074 #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
1075 { #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } },
1079 CGuidNameList GuidNames;
1080 int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);
1082 char *CGuidNameList::operator [] (const GUID &guid)
1084 for (int i = 0; i < g_cGuidNames; i++) {
1085 if (g_GuidNames[i].guid == guid) {
1086 return g_GuidNames[i].szName;
1089 if (guid == GUID_NULL) {
1093 // !!! add something to print FOURCC guids?
1095 // shouldn't this print the hex CLSID?
1096 return "Unknown GUID Name";
1099 #endif /* _OBJBASE_H_ */
1101 /* CDisp class - display our data types */
1103 // clashes with REFERENCE_TIME
1104 CDisp::CDisp(LONGLONG ll, int Format)
1106 // note: this could be combined with CDisp(LONGLONG) by
1107 // introducing a default format of CDISP_REFTIME
1117 // always output at least one digit
1119 // Get the rightmost digit - we only need the low word
1120 digit = li.LowPart % 10;
1122 temp[--pos] = (TCHAR) digit+L'0';
1123 } while (li.QuadPart);
1124 (void)StringCchCopy(m_String, NUMELMS(m_String), temp+pos);
1129 (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);
1133 CDisp::CDisp(REFCLSID clsid)
1136 (void)StringFromGUID2(clsid, m_String, NUMELMS(m_String));
1139 (void)StringFromGUID2(clsid, wszTemp, NUMELMS(wszTemp));
1140 (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), wszTemp);
1146 CDisp::CDisp(CRefTime llTime)
1151 (void)StringCchCopy(m_String, NUMELMS(m_String), TEXT("-"));
1153 llDiv = (LONGLONG)24 * 3600 * 10000000;
1154 if (llTime >= llDiv) {
1155 (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d days "), (LONG)(llTime / llDiv));
1156 llTime = llTime % llDiv;
1158 llDiv = (LONGLONG)3600 * 10000000;
1159 if (llTime >= llDiv) {
1160 (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d hrs "), (LONG)(llTime / llDiv));
1161 llTime = llTime % llDiv;
1163 llDiv = (LONGLONG)60 * 10000000;
1164 if (llTime >= llDiv) {
1165 (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d mins "), (LONG)(llTime / llDiv));
1166 llTime = llTime % llDiv;
1168 (void)StringCchPrintf(m_String + lstrlen(m_String), NUMELMS(m_String) - lstrlen(m_String), TEXT("%d.%3.3d sec"),
1169 (LONG)llTime / 10000000,
1170 (LONG)((llTime % 10000000) / 10000));
1173 #endif // __STREAMS__
1177 CDisp::CDisp(IPin *pPin)
1180 TCHAR str[MAX_PIN_NAME];
1184 pPin->QueryPinInfo(&pi);
1185 pi.pFilter->GetClassID(&clsid);
1186 QueryPinInfoReleaseFilter(pi);
1188 WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1,
1189 str, MAX_PIN_NAME, NULL, NULL);
1191 (void)StringCchCopy(str, NUMELMS(str), pi.achName);
1194 (void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin"));
1197 m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];
1202 (void)StringCchPrintf(m_pString, lstrlen(str) + 64, TEXT("%hs(%s)"), GuidNames[clsid], str);
1205 /* Display filter or pin */
1206 CDisp::CDisp(IUnknown *pUnk)
1209 HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf);
1213 hr = pf->QueryFilterInfo(&fi);
1216 QueryFilterInfoReleaseGraph(fi);
1218 size_t len = lstrlenW(fi.achName) + 1;
1220 m_pString = new TCHAR[len];
1224 (void)StringCchCopy(m_pString, len, fi.achName);
1226 (void)StringCchPrintf(m_pString, len, "%S", fi.achName);
1237 hr = pUnk->QueryInterface(IID_IPin, (void **)&pp);
1251 CDispBasic::~CDispBasic()
1253 if (m_pString != m_String) {
1254 delete [] m_pString;
1258 CDisp::CDisp(double d)
1260 (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000));
1264 /* If built for debug this will display the media type details. We convert the
1265 major and subtypes into strings and also ask the base classes for a string
1266 description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit
1267 We also display the fields in the BITMAPINFOHEADER structure, this should
1268 succeed as we do not accept input types unless the format is big enough */
1271 void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn)
1274 /* Dump the GUID types and a short description */
1276 DbgLog((LOG_TRACE,5,TEXT("")));
1277 DbgLog((LOG_TRACE,2,TEXT("%s M type %hs S type %hs"), label,
1278 GuidNames[pmtIn->majortype],
1279 GuidNames[pmtIn->subtype]));
1280 DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));
1282 /* Dump the generic media types */
1284 if (pmtIn->bTemporalCompression) {
1285 DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));
1287 DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));
1290 if (pmtIn->bFixedSizeSamples) {
1291 DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));
1293 DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));
1296 if (pmtIn->formattype == FORMAT_VideoInfo) {
1298 VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat;
1300 DisplayRECT(TEXT("Source rectangle"),pVideoInfo->rcSource);
1301 DisplayRECT(TEXT("Target rectangle"),pVideoInfo->rcTarget);
1302 DisplayBITMAPINFO(HEADER(pmtIn->pbFormat));
1304 } if (pmtIn->formattype == FORMAT_VideoInfo2) {
1306 VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pmtIn->pbFormat;
1308 DisplayRECT(TEXT("Source rectangle"),pVideoInfo2->rcSource);
1309 DisplayRECT(TEXT("Target rectangle"),pVideoInfo2->rcTarget);
1310 DbgLog((LOG_TRACE, 5, TEXT("Aspect Ratio: %d:%d"),
1311 pVideoInfo2->dwPictAspectRatioX,
1312 pVideoInfo2->dwPictAspectRatioY));
1313 DisplayBITMAPINFO(&pVideoInfo2->bmiHeader);
1315 } else if (pmtIn->majortype == MEDIATYPE_Audio) {
1316 DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"),
1317 GuidNames[pmtIn->formattype]));
1318 DbgLog((LOG_TRACE,2,TEXT(" Subtype %hs"),
1319 GuidNames[pmtIn->subtype]));
1321 if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)
1322 && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))
1324 /* Dump the contents of the WAVEFORMATEX type-specific format structure */
1326 WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;
1327 DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));
1328 DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));
1329 DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));
1330 DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));
1331 DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));
1332 DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));
1334 /* PCM uses a WAVEFORMAT and does not have the extra size field */
1336 if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {
1337 DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));
1343 DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"),
1344 GuidNames[pmtIn->formattype]));
1349 void DisplayBITMAPINFO(const BITMAPINFOHEADER* pbmi)
1351 DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));
1352 if (pbmi->biCompression < 256) {
1353 DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit (%d)"),
1354 pbmi->biWidth, pbmi->biHeight,
1355 pbmi->biBitCount, pbmi->biCompression));
1357 DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),
1358 pbmi->biWidth, pbmi->biHeight,
1359 pbmi->biBitCount, &pbmi->biCompression));
1362 DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));
1363 DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));
1364 DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));
1365 DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));
1366 DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));
1370 void DisplayRECT(LPCTSTR szLabel, const RECT& rc)
1372 DbgLog((LOG_TRACE,5,TEXT("%s (Left %d Top %d Right %d Bottom %d)"),
1381 void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel)
1388 IEnumFilters *pFilters;
1390 DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph));
1392 if (FAILED(pGraph->EnumFilters(&pFilters))) {
1393 DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!")));
1396 IBaseFilter *pFilter;
1398 while (pFilters->Next(1, &pFilter, &n) == S_OK) {
1401 if (FAILED(pFilter->QueryFilterInfo(&info))) {
1402 DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%p] -- failed QueryFilterInfo"), pFilter));
1404 QueryFilterInfoReleaseGraph(info);
1406 // !!! should QueryVendorInfo here!
1408 DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%p] '%ls'"), pFilter, info.achName));
1412 if (FAILED(pFilter->EnumPins(&pins))) {
1413 DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!")));
1417 while (pins->Next(1, &pPin, &n) == S_OK) {
1420 if (FAILED(pPin->QueryPinInfo(&pinInfo))) {
1421 DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin));
1423 QueryPinInfoReleaseFilter(pinInfo);
1425 IPin *pPinConnected = NULL;
1427 HRESULT hr = pPin->ConnectedTo(&pPinConnected);
1429 if (pPinConnected) {
1430 DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%p] '%ls' [%sput]")
1431 TEXT(" Connected to pin [%p]"),
1432 pPin, pinInfo.achName,
1433 pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"),
1436 pPinConnected->Release();
1438 // perhaps we should really dump the type both ways as a sanity
1440 if (pinInfo.dir == PINDIR_OUTPUT) {
1443 hr = pPin->ConnectionMediaType(&mt);
1445 if (SUCCEEDED(hr)) {
1446 DisplayType(TEXT("Connection type"), &mt);
1452 DbgLog((LOG_TRACE,dwLevel,
1453 TEXT(" Pin [%x] '%ls' [%sput]"),
1454 pPin, pinInfo.achName,
1455 pinInfo.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out")));
1472 pFilters->Release();
1478 #endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */