Update UNRAR.H
[xy_vsfilter.git] / src / filters / BaseClasses / wxdebug.cpp
blob5c35aee7b1a70c412a2996966e28844f11b83bdc
1 //------------------------------------------------------------------------------
2 // File: WXDebug.cpp
3 //
4 // Desc: DirectShow base classes - implements ActiveX system debugging
5 // facilities.
6 //
7 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
8 //------------------------------------------------------------------------------
11 #define _WINDLL
13 #include <streams.h>
14 #include <stdarg.h>
15 #include <stdio.h>
17 #ifdef DEBUG
18 #ifdef UNICODE
19 #ifndef _UNICODE
20 #define _UNICODE
21 #endif // _UNICODE
22 #endif // UNICODE
23 #endif // DEBUG
25 #include <tchar.h>
27 #ifdef DEBUG
29 // The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer.
30 // See the documentation for wsprintf()'s lpOut parameter for more information.
31 const INT iDEBUGINFO = 1024; // Used to format strings
33 /* For every module and executable we store a debugging level for each of
34 the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy
35 to isolate and debug individual modules without seeing everybody elses
36 spurious debug output. The keys are stored in the registry under the
37 HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values
38 NOTE these must be in the same order as their enumeration definition */
40 TCHAR *pKeyNames[] = {
41 TEXT("TIMING"), // Timing and performance measurements
42 TEXT("TRACE"), // General step point call tracing
43 TEXT("MEMORY"), // Memory and object allocation/destruction
44 TEXT("LOCKING"), // Locking/unlocking of critical sections
45 TEXT("ERROR"), // Debug error notification
46 TEXT("CUSTOM1"),
47 TEXT("CUSTOM2"),
48 TEXT("CUSTOM3"),
49 TEXT("CUSTOM4"),
50 TEXT("CUSTOM5")
53 const TCHAR CAutoTrace::_szEntering[] = TEXT("Entering: %s");
54 const TCHAR CAutoTrace::_szLeaving[] = TEXT("Leaving: %s");
56 const INT iMAXLEVELS = NUMELMS(pKeyNames); // Maximum debug categories
58 HINSTANCE m_hInst; // Module instance handle
59 TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name
60 DWORD m_Levels[iMAXLEVELS]; // Debug level per category
61 CRITICAL_SECTION m_CSDebug; // Controls access to list
62 DWORD m_dwNextCookie; // Next active object ID
63 ObjectDesc *pListHead = NULL; // First active object
64 DWORD m_dwObjectCount; // Active object count
65 BOOL m_bInit = FALSE; // Have we been initialised
66 HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here
67 DWORD dwWaitTimeout = INFINITE; // Default timeout value
68 DWORD dwTimeOffset; // Time of first DbgLog call
69 bool g_fUseKASSERT = false; // don't create messagebox
70 bool g_fDbgInDllEntryPoint = false;
71 bool g_fAutoRefreshLevels = false;
73 const TCHAR *pBaseKey = TEXT("SOFTWARE\\Debug");
74 const TCHAR *pGlobalKey = TEXT("GLOBAL");
75 static CHAR *pUnknownName = "UNKNOWN";
77 TCHAR *TimeoutName = TEXT("TIMEOUT");
79 /* This sets the instance handle that the debug library uses to find
80 the module's file name from the Win32 GetModuleFileName function */
82 void WINAPI DbgInitialise(HINSTANCE hInst)
84 InitializeCriticalSection(&m_CSDebug);
85 m_bInit = TRUE;
87 m_hInst = hInst;
88 DbgInitModuleName();
89 if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))
90 DebugBreak();
91 DbgInitModuleSettings(false);
92 DbgInitGlobalSettings(true);
93 dwTimeOffset = timeGetTime();
97 /* This is called to clear up any resources the debug library uses - at the
98 moment we delete our critical section and the object list. The values we
99 retrieve from the registry are all done during initialisation but we don't
100 go looking for update notifications while we are running, if the values
101 are changed then the application has to be restarted to pick them up */
103 void WINAPI DbgTerminate()
105 if (m_hOutput != INVALID_HANDLE_VALUE) {
106 EXECUTE_ASSERT(CloseHandle(m_hOutput));
107 m_hOutput = INVALID_HANDLE_VALUE;
109 DeleteCriticalSection(&m_CSDebug);
110 m_bInit = FALSE;
114 /* This is called by DbgInitLogLevels to read the debug settings
115 for each logging category for this module from the registry */
117 void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax)
119 LONG lReturn; // Create key return value
120 LONG lKeyPos; // Current key category
121 DWORD dwKeySize; // Size of the key value
122 DWORD dwKeyType; // Receives it's type
123 DWORD dwKeyValue; // This fields value
125 /* Try and read a value for each key position in turn */
126 for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
128 dwKeySize = sizeof(DWORD);
129 lReturn = RegQueryValueEx(
130 hKey, // Handle to an open key
131 pKeyNames[lKeyPos], // Subkey name derivation
132 NULL, // Reserved field
133 &dwKeyType, // Returns the field type
134 (LPBYTE) &dwKeyValue, // Returns the field's value
135 &dwKeySize ); // Number of bytes transferred
137 /* If either the key was not available or it was not a DWORD value
138 then we ensure only the high priority debug logging is output
139 but we try and update the field to a zero filled DWORD value */
141 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) {
143 dwKeyValue = 0;
144 lReturn = RegSetValueEx(
145 hKey, // Handle of an open key
146 pKeyNames[lKeyPos], // Address of subkey name
147 (DWORD) 0, // Reserved field
148 REG_DWORD, // Type of the key field
149 (PBYTE) &dwKeyValue, // Value for the field
150 sizeof(DWORD)); // Size of the field buffer
152 if (lReturn != ERROR_SUCCESS) {
153 DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
154 dwKeyValue = 0;
157 if(fTakeMax)
159 m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);
161 else
163 if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) {
164 m_Levels[lKeyPos] = dwKeyValue;
169 /* Read the timeout value for catching hangs */
170 dwKeySize = sizeof(DWORD);
171 lReturn = RegQueryValueEx(
172 hKey, // Handle to an open key
173 TimeoutName, // Subkey name derivation
174 NULL, // Reserved field
175 &dwKeyType, // Returns the field type
176 (LPBYTE) &dwWaitTimeout, // Returns the field's value
177 &dwKeySize ); // Number of bytes transferred
179 /* If either the key was not available or it was not a DWORD value
180 then we ensure only the high priority debug logging is output
181 but we try and update the field to a zero filled DWORD value */
183 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) {
185 dwWaitTimeout = INFINITE;
186 lReturn = RegSetValueEx(
187 hKey, // Handle of an open key
188 TimeoutName, // Address of subkey name
189 (DWORD) 0, // Reserved field
190 REG_DWORD, // Type of the key field
191 (PBYTE) &dwWaitTimeout, // Value for the field
192 sizeof(DWORD)); // Size of the field buffer
194 if (lReturn != ERROR_SUCCESS) {
195 DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
196 dwWaitTimeout = INFINITE;
201 void WINAPI DbgOutString(LPCTSTR psz)
203 if (m_hOutput != INVALID_HANDLE_VALUE) {
204 UINT cb = lstrlen(psz);
205 DWORD dw;
206 #ifdef UNICODE
207 CHAR szDest[2048];
208 WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0);
209 WriteFile (m_hOutput, szDest, cb, &dw, NULL);
210 #else
211 WriteFile (m_hOutput, psz, cb, &dw, NULL);
212 #endif
213 } else {
214 OutputDebugString (psz);
218 /* Called by DbgInitGlobalSettings to setup alternate logging destinations
221 void WINAPI DbgInitLogTo (
222 HKEY hKey)
224 LONG lReturn;
225 DWORD dwKeyType;
226 DWORD dwKeySize;
227 TCHAR szFile[MAX_PATH] = {0};
228 static const TCHAR cszKey[] = TEXT("LogToFile");
230 dwKeySize = MAX_PATH;
231 lReturn = RegQueryValueEx(
232 hKey, // Handle to an open key
233 cszKey, // Subkey name derivation
234 NULL, // Reserved field
235 &dwKeyType, // Returns the field type
236 (LPBYTE) szFile, // Returns the field's value
237 &dwKeySize); // Number of bytes transferred
239 // create an empty key if it does not already exist
241 if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)
243 dwKeySize = sizeof(TCHAR);
244 lReturn = RegSetValueEx(
245 hKey, // Handle of an open key
246 cszKey, // Address of subkey name
247 (DWORD) 0, // Reserved field
248 REG_SZ, // Type of the key field
249 (PBYTE)szFile, // Value for the field
250 dwKeySize); // Size of the field buffer
253 // if an output-to was specified. try to open it.
255 if (m_hOutput != INVALID_HANDLE_VALUE) {
256 EXECUTE_ASSERT(CloseHandle (m_hOutput));
257 m_hOutput = INVALID_HANDLE_VALUE;
259 if (szFile[0] != 0)
261 if (!lstrcmpi(szFile, TEXT("Console"))) {
262 m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
263 if (m_hOutput == INVALID_HANDLE_VALUE) {
264 AllocConsole ();
265 m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
267 SetConsoleTitle (TEXT("ActiveX Debug Output"));
268 } else if (szFile[0] &&
269 lstrcmpi(szFile, TEXT("Debug")) &&
270 lstrcmpi(szFile, TEXT("Debugger")) &&
271 lstrcmpi(szFile, TEXT("Deb")))
273 m_hOutput = CreateFile(szFile, GENERIC_WRITE,
274 FILE_SHARE_READ,
275 NULL, OPEN_ALWAYS,
276 FILE_ATTRIBUTE_NORMAL,
277 NULL);
278 if (INVALID_HANDLE_VALUE != m_hOutput)
280 static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");
281 SetFilePointer (m_hOutput, 0, NULL, FILE_END);
282 DbgOutString (cszBar);
290 /* This is called by DbgInitLogLevels to read the global debug settings for
291 each logging category for this module from the registry. Normally each
292 module has it's own values set for it's different debug categories but
293 setting the global SOFTWARE\Debug\Global applies them to ALL modules */
295 void WINAPI DbgInitGlobalSettings(bool fTakeMax)
297 LONG lReturn; // Create key return value
298 TCHAR szInfo[iDEBUGINFO]; // Constructs key names
299 HKEY hGlobalKey; // Global override key
301 /* Construct the global base key name */
302 wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,pGlobalKey);
304 /* Create or open the key for this module */
305 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
306 szInfo, // Address of subkey name
307 (DWORD) 0, // Reserved value
308 NULL, // Address of class name
309 (DWORD) 0, // Special options flags
310 KEY_ALL_ACCESS, // Desired security access
311 NULL, // Key security descriptor
312 &hGlobalKey, // Opened handle buffer
313 NULL); // What really happened
315 if (lReturn != ERROR_SUCCESS) {
316 DbgLog((LOG_ERROR,0,TEXT("Could not access GLOBAL module key")));
317 return;
320 DbgInitKeyLevels(hGlobalKey, fTakeMax);
321 RegCloseKey(hGlobalKey);
325 /* This sets the debugging log levels for the different categories. We start
326 by opening (or creating if not already available) the SOFTWARE\Debug key
327 that all these settings live under. We then look at the global values
328 set under SOFTWARE\Debug\Global which apply on top of the individual
329 module settings. We then load the individual module registry settings */
331 void WINAPI DbgInitModuleSettings(bool fTakeMax)
333 LONG lReturn; // Create key return value
334 TCHAR szInfo[iDEBUGINFO]; // Constructs key names
335 HKEY hModuleKey; // Module key handle
337 /* Construct the base key name */
338 wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,m_ModuleName);
340 /* Create or open the key for this module */
341 lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key
342 szInfo, // Address of subkey name
343 (DWORD) 0, // Reserved value
344 NULL, // Address of class name
345 (DWORD) 0, // Special options flags
346 KEY_ALL_ACCESS, // Desired security access
347 NULL, // Key security descriptor
348 &hModuleKey, // Opened handle buffer
349 NULL); // What really happened
351 if (lReturn != ERROR_SUCCESS) {
352 DbgLog((LOG_ERROR,0,TEXT("Could not access module key")));
353 return;
356 DbgInitLogTo(hModuleKey);
357 DbgInitKeyLevels(hModuleKey, fTakeMax);
358 RegCloseKey(hModuleKey);
362 /* Initialise the module file name */
364 void WINAPI DbgInitModuleName()
366 TCHAR FullName[iDEBUGINFO]; // Load the full path and module name
367 TCHAR *pName; // Searches from the end for a backslash
369 GetModuleFileName(m_hInst,FullName,iDEBUGINFO);
370 pName = _tcsrchr(FullName,'\\');
371 if (pName == NULL) {
372 pName = FullName;
373 } else {
374 pName++;
376 lstrcpy(m_ModuleName,pName);
379 struct MsgBoxMsg
381 HWND hwnd;
382 TCHAR *szTitle;
383 TCHAR *szMessage;
384 DWORD dwFlags;
385 INT iResult;
389 // create a thread to call MessageBox(). calling MessageBox() on
390 // random threads at bad times can confuse the host (eg IE).
392 DWORD WINAPI MsgBoxThread(
393 LPVOID lpParameter // thread data
396 MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter;
397 pmsg->iResult = MessageBox(
398 pmsg->hwnd,
399 pmsg->szTitle,
400 pmsg->szMessage,
401 pmsg->dwFlags);
403 return 0;
406 INT MessageBoxOtherThread(
407 HWND hwnd,
408 TCHAR *szTitle,
409 TCHAR *szMessage,
410 DWORD dwFlags)
412 if(g_fDbgInDllEntryPoint)
414 // can't wait on another thread because we have the loader
415 // lock held in the dll entry point.
416 return MessageBox(hwnd, szTitle, szMessage, dwFlags);
418 else
420 MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0};
421 DWORD dwid;
422 HANDLE hThread = CreateThread(
423 0, // security
424 0, // stack size
425 MsgBoxThread,
426 (void *)&msg, // arg
427 0, // flags
428 &dwid);
429 if(hThread)
431 WaitForSingleObject(hThread, INFINITE);
432 CloseHandle(hThread);
433 return msg.iResult;
436 // break into debugger on failure.
437 return IDCANCEL;
441 /* Displays a message box if the condition evaluated to FALSE */
443 void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
445 if(g_fUseKASSERT)
447 DbgKernelAssert(pCondition, pFileName, iLine);
449 else
452 TCHAR szInfo[iDEBUGINFO];
454 wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
455 pCondition, iLine, pFileName);
457 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
458 MB_SYSTEMMODAL |
459 MB_ICONHAND |
460 MB_YESNOCANCEL |
461 MB_SETFOREGROUND);
462 switch (MsgId)
464 case IDNO: /* Kill the application */
466 FatalAppExit(FALSE, TEXT("Application terminated"));
467 break;
469 case IDCANCEL: /* Break into the debugger */
471 DebugBreak();
472 break;
474 case IDYES: /* Ignore assertion continue execution */
475 break;
480 /* Displays a message box at a break point */
482 void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
484 if(g_fUseKASSERT)
486 DbgKernelAssert(pCondition, pFileName, iLine);
488 else
490 TCHAR szInfo[iDEBUGINFO];
492 wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
493 pCondition, iLine, pFileName);
495 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
496 MB_SYSTEMMODAL |
497 MB_ICONHAND |
498 MB_YESNOCANCEL |
499 MB_SETFOREGROUND);
500 switch (MsgId)
502 case IDNO: /* Kill the application */
504 FatalAppExit(FALSE, TEXT("Application terminated"));
505 break;
507 case IDCANCEL: /* Break into the debugger */
509 DebugBreak();
510 break;
512 case IDYES: /* Ignore break point continue execution */
513 break;
518 void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...)
520 // A debug break point message can have at most 2000 characters if
521 // ANSI or UNICODE characters are being used. A debug break point message
522 // can have between 1000 and 2000 double byte characters in it. If a
523 // particular message needs more characters, then the value of this constant
524 // should be increased.
525 const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000;
527 TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE];
529 const DWORD MAX_CHARS_IN_BREAK_POINT_MESSAGE = sizeof(szBreakPointMessage) / sizeof(TCHAR);
531 va_list va;
532 va_start( va, szFormatString );
534 int nReturnValue = _vsntprintf( szBreakPointMessage, MAX_CHARS_IN_BREAK_POINT_MESSAGE, szFormatString, va );
536 va_end(va);
538 // _vsnprintf() returns -1 if an error occurs.
539 if( -1 == nReturnValue ) {
540 DbgBreak( "ERROR in DbgBreakPoint(). The variable length debug message could not be displayed because _vsnprintf() failed." );
541 return;
544 ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine );
548 /* When we initialised the library we stored in the m_Levels array the current
549 debug output level for this module for each of the five categories. When
550 some debug logging is sent to us it can be sent with a combination of the
551 categories (if it is applicable to many for example) in which case we map
552 the type's categories into their current debug levels and see if any of
553 them can be accepted. The function looks at each bit position in turn from
554 the input type field and then compares it's debug level with the modules.
556 A level of 0 means that output is always sent to the debugger. This is
557 due to producing output if the input level is <= m_Levels.
561 BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level)
563 if(g_fAutoRefreshLevels)
565 // re-read the registry every second. We cannot use RegNotify() to
566 // notice registry changes because it's not available on win9x.
567 static DWORD g_dwLastRefresh = 0;
568 DWORD dwTime = timeGetTime();
569 if(dwTime - g_dwLastRefresh > 1000) {
570 g_dwLastRefresh = dwTime;
572 // there's a race condition: multiple threads could update the
573 // values. plus read and write not synchronized. no harm
574 // though.
575 DbgInitModuleSettings(false);
580 DWORD Mask = 0x01;
582 // If no valid bits are set return FALSE
583 if ((Type & ((1<<iMAXLEVELS)-1))) {
585 // speed up unconditional output.
586 if (0==Level)
587 return(TRUE);
589 for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
590 if (Type & Mask) {
591 if (Level <= (m_Levels[lKeyPos] & ~LOG_FORCIBLY_SET)) {
592 return TRUE;
595 Mask <<= 1;
598 return FALSE;
602 /* Set debug levels to a given value */
604 void WINAPI DbgSetModuleLevel(DWORD Type, DWORD Level)
606 DWORD Mask = 0x01;
608 for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
609 if (Type & Mask) {
610 m_Levels[lKeyPos] = Level | LOG_FORCIBLY_SET;
612 Mask <<= 1;
616 /* whether to check registry values periodically. this isn't turned
617 automatically because of the potential performance hit. */
618 void WINAPI DbgSetAutoRefreshLevels(bool fAuto)
620 g_fAutoRefreshLevels = fAuto;
623 #ifdef UNICODE
625 // warning -- this function is implemented twice for ansi applications
626 // linking to the unicode library
628 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const CHAR *pFormat,...)
630 /* Check the current level for this type combination */
632 BOOL bAccept = DbgCheckModuleLevel(Type,Level);
633 if (bAccept == FALSE) {
634 return;
637 TCHAR szInfo[2000];
639 /* Format the variable length parameter list */
641 va_list va;
642 va_start(va, pFormat);
644 lstrcpy(szInfo,m_ModuleName);
645 wsprintf(szInfo + lstrlen(szInfo),
646 TEXT("(tid %x) %8d : "),
647 GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
649 CHAR szInfoA[2000];
650 WideCharToMultiByte(CP_ACP, 0, szInfo, -1, szInfoA, NUMELMS(szInfoA), 0, 0);
652 wvsprintfA(szInfoA + lstrlenA(szInfoA), pFormat, va);
653 lstrcatA(szInfoA, "\r\n");
655 WCHAR wszOutString[2000];
656 MultiByteToWideChar(CP_ACP, 0, szInfoA, -1, wszOutString, NUMELMS(wszOutString));
657 DbgOutString(wszOutString);
659 va_end(va);
662 void DbgAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine)
664 if(g_fUseKASSERT)
666 DbgKernelAssert(pCondition, pFileName, iLine);
668 else
671 TCHAR szInfo[iDEBUGINFO];
673 wsprintf(szInfo, TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
674 pCondition, iLine, pFileName);
676 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"),
677 MB_SYSTEMMODAL |
678 MB_ICONHAND |
679 MB_YESNOCANCEL |
680 MB_SETFOREGROUND);
681 switch (MsgId)
683 case IDNO: /* Kill the application */
685 FatalAppExit(FALSE, TEXT("Application terminated"));
686 break;
688 case IDCANCEL: /* Break into the debugger */
690 DebugBreak();
691 break;
693 case IDYES: /* Ignore assertion continue execution */
694 break;
699 /* Displays a message box at a break point */
701 void WINAPI DbgBreakPoint(const CHAR *pCondition,const CHAR *pFileName,INT iLine)
703 if(g_fUseKASSERT)
705 DbgKernelAssert(pCondition, pFileName, iLine);
707 else
709 TCHAR szInfo[iDEBUGINFO];
711 wsprintf(szInfo, TEXT("%hs \nAt line %d of %hs\nContinue? (Cancel to debug)"),
712 pCondition, iLine, pFileName);
714 INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"),
715 MB_SYSTEMMODAL |
716 MB_ICONHAND |
717 MB_YESNOCANCEL |
718 MB_SETFOREGROUND);
719 switch (MsgId)
721 case IDNO: /* Kill the application */
723 FatalAppExit(FALSE, TEXT("Application terminated"));
724 break;
726 case IDCANCEL: /* Break into the debugger */
728 DebugBreak();
729 break;
731 case IDYES: /* Ignore break point continue execution */
732 break;
737 void WINAPI DbgKernelAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine)
739 DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%hs) at line %d in file %hs"),
740 pCondition, iLine, pFileName));
741 DebugBreak();
744 #endif
746 /* Print a formatted string to the debugger prefixed with this module's name
747 Because the COMBASE classes are linked statically every module loaded will
748 have their own copy of this code. It therefore helps if the module name is
749 included on the output so that the offending code can be easily found */
752 // warning -- this function is implemented twice for ansi applications
753 // linking to the unicode library
755 void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...)
758 /* Check the current level for this type combination */
760 BOOL bAccept = DbgCheckModuleLevel(Type,Level);
761 if (bAccept == FALSE) {
762 return;
765 TCHAR szInfo[2000];
767 /* Format the variable length parameter list */
769 va_list va;
770 va_start(va, pFormat);
772 lstrcpy(szInfo,m_ModuleName);
773 wsprintf(szInfo + lstrlen(szInfo),
774 TEXT("(tid %x) %8d : "),
775 GetCurrentThreadId(), timeGetTime() - dwTimeOffset);
777 _vstprintf(szInfo + lstrlen(szInfo), pFormat, va);
778 lstrcat(szInfo, TEXT("\r\n"));
779 DbgOutString(szInfo);
781 va_end(va);
785 /* If we are executing as a pure kernel filter we cannot display message
786 boxes to the user, this provides an alternative which puts the error
787 condition on the debugger output with a suitable eye catching message */
789 void WINAPI DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
791 DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),
792 pCondition, iLine, pFileName));
793 DebugBreak();
798 /* Each time we create an object derived from CBaseObject the constructor will
799 call us to register the creation of the new object. We are passed a string
800 description which we store away. We return a cookie that the constructor
801 uses to identify the object when it is destroyed later on. We update the
802 total number of active objects in the DLL mainly for debugging purposes */
804 DWORD WINAPI DbgRegisterObjectCreation(const CHAR *szObjectName,
805 const WCHAR *wszObjectName)
807 /* If this fires you have a mixed DEBUG/RETAIL build */
809 ASSERT(!!szObjectName ^ !!wszObjectName);
811 /* Create a place holder for this object description */
813 ObjectDesc *pObject = new ObjectDesc;
814 ASSERT(pObject);
816 /* It is valid to pass a NULL object name */
817 if (pObject == NULL) {
818 return FALSE;
821 /* Check we have been initialised - we may not be initialised when we are
822 being pulled in from an executable which has globally defined objects
823 as they are created by the C++ run time before WinMain is called */
825 if (m_bInit == FALSE) {
826 DbgInitialise(GetModuleHandle(NULL));
829 /* Grab the list critical section */
830 EnterCriticalSection(&m_CSDebug);
832 /* If no name then default to UNKNOWN */
833 if (!szObjectName && !wszObjectName) {
834 szObjectName = pUnknownName;
837 /* Put the new description at the head of the list */
839 pObject->m_szName = szObjectName;
840 pObject->m_wszName = wszObjectName;
841 pObject->m_dwCookie = ++m_dwNextCookie;
842 pObject->m_pNext = pListHead;
844 pListHead = pObject;
845 m_dwObjectCount++;
847 DWORD ObjectCookie = pObject->m_dwCookie;
848 ASSERT(ObjectCookie);
850 if(wszObjectName) {
851 DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%ls) %d Active"),
852 pObject->m_dwCookie, wszObjectName, m_dwObjectCount));
853 } else {
854 DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%hs) %d Active"),
855 pObject->m_dwCookie, szObjectName, m_dwObjectCount));
858 LeaveCriticalSection(&m_CSDebug);
859 return ObjectCookie;
863 /* This is called by the CBaseObject destructor when an object is about to be
864 destroyed, we are passed the cookie we returned during construction that
865 identifies this object. We scan the object list for a matching cookie and
866 remove the object if successful. We also update the active object count */
868 BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie)
870 /* Grab the list critical section */
871 EnterCriticalSection(&m_CSDebug);
873 ObjectDesc *pObject = pListHead;
874 ObjectDesc *pPrevious = NULL;
876 /* Scan the object list looking for a cookie match */
878 while (pObject) {
879 if (pObject->m_dwCookie == dwCookie) {
880 break;
882 pPrevious = pObject;
883 pObject = pObject->m_pNext;
886 if (pObject == NULL) {
887 DbgBreak("Apparently destroying a bogus object");
888 LeaveCriticalSection(&m_CSDebug);
889 return FALSE;
892 /* Is the object at the head of the list */
894 if (pPrevious == NULL) {
895 pListHead = pObject->m_pNext;
896 } else {
897 pPrevious->m_pNext = pObject->m_pNext;
900 /* Delete the object and update the housekeeping information */
902 m_dwObjectCount--;
904 if(pObject->m_wszName) {
905 DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"),
906 pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount));
907 } else {
908 DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"),
909 pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount));
912 delete pObject;
913 LeaveCriticalSection(&m_CSDebug);
914 return TRUE;
918 /* This runs through the active object list displaying their details */
920 void WINAPI DbgDumpObjectRegister()
922 TCHAR szInfo[iDEBUGINFO];
924 /* Grab the list critical section */
926 EnterCriticalSection(&m_CSDebug);
927 ObjectDesc *pObject = pListHead;
929 /* Scan the object list displaying the name and cookie */
931 DbgLog((LOG_MEMORY,2,TEXT("")));
932 DbgLog((LOG_MEMORY,2,TEXT(" ID Object Description")));
933 DbgLog((LOG_MEMORY,2,TEXT("")));
935 while (pObject) {
936 if(pObject->m_wszName) {
937 wsprintf(szInfo,TEXT("%5d (%8x) %30ls"),pObject->m_dwCookie, &pObject, pObject->m_wszName);
938 } else {
939 wsprintf(szInfo,TEXT("%5d (%8x) %30hs"),pObject->m_dwCookie, &pObject, pObject->m_szName);
941 DbgLog((LOG_MEMORY,2,szInfo));
942 pObject = pObject->m_pNext;
945 wsprintf(szInfo,TEXT("Total object count %5d"),m_dwObjectCount);
946 DbgLog((LOG_MEMORY,2,TEXT("")));
947 DbgLog((LOG_MEMORY,1,szInfo));
948 LeaveCriticalSection(&m_CSDebug);
951 /* Debug infinite wait stuff */
952 DWORD WINAPI DbgWaitForSingleObject(HANDLE h)
954 DWORD dwWaitResult;
955 do {
956 dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);
957 ASSERT(dwWaitResult == WAIT_OBJECT_0);
958 } while (dwWaitResult == WAIT_TIMEOUT);
959 return dwWaitResult;
961 DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount,
962 CONST HANDLE *lpHandles,
963 BOOL bWaitAll)
965 DWORD dwWaitResult;
966 do {
967 dwWaitResult = WaitForMultipleObjects(nCount,
968 lpHandles,
969 bWaitAll,
970 dwWaitTimeout);
971 ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);
972 } while (dwWaitResult == WAIT_TIMEOUT);
973 return dwWaitResult;
976 void WINAPI DbgSetWaitTimeout(DWORD dwTimeout)
978 dwWaitTimeout = dwTimeout;
981 #endif /* DEBUG */
983 #ifdef _OBJBASE_H_
985 /* Stuff for printing out our GUID names */
987 GUID_STRING_ENTRY g_GuidNames[] = {
988 #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
989 { #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } },
990 #include <uuids.h>
993 CGuidNameList GuidNames;
994 int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);
996 char *CGuidNameList::operator [] (const GUID &guid)
998 for (int i = 0; i < g_cGuidNames; i++) {
999 if (g_GuidNames[i].guid == guid) {
1000 return g_GuidNames[i].szName;
1003 if (guid == GUID_NULL) {
1004 return "GUID_NULL";
1007 // !!! add something to print FOURCC guids?
1009 // shouldn't this print the hex CLSID?
1010 return "Unknown GUID Name";
1013 #endif /* _OBJBASE_H_ */
1015 /* CDisp class - display our data types */
1017 // clashes with REFERENCE_TIME
1018 CDisp::CDisp(LONGLONG ll, int Format)
1020 // note: this could be combined with CDisp(LONGLONG) by
1021 // introducing a default format of CDISP_REFTIME
1022 LARGE_INTEGER li;
1023 li.QuadPart = ll;
1024 switch (Format) {
1025 case CDISP_DEC:
1027 TCHAR temp[20];
1028 int pos=20;
1029 temp[--pos] = 0;
1030 int digit;
1031 // always output at least one digit
1032 do {
1033 // Get the rightmost digit - we only need the low word
1034 digit = li.LowPart % 10;
1035 li.QuadPart /= 10;
1036 temp[--pos] = (TCHAR) digit+L'0';
1037 } while (li.QuadPart);
1038 wsprintf(m_String, TEXT("%s"), temp+pos);
1039 break;
1041 case CDISP_HEX:
1042 default:
1043 wsprintf(m_String, TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);
1047 CDisp::CDisp(REFCLSID clsid)
1049 WCHAR strClass[CHARS_IN_GUID+1];
1050 StringFromGUID2(clsid, strClass, sizeof(strClass) / sizeof(strClass[0]));
1051 ASSERT(sizeof(m_String)/sizeof(m_String[0]) >= CHARS_IN_GUID+1);
1052 wsprintf(m_String, TEXT("%ls"), strClass);
1055 #ifdef __STREAMS__
1056 /* Display stuff */
1057 CDisp::CDisp(CRefTime llTime)
1059 LPTSTR lpsz = m_String;
1060 LONGLONG llDiv;
1061 if (llTime < 0) {
1062 llTime = -llTime;
1063 lpsz += wsprintf(lpsz, TEXT("-"));
1065 llDiv = (LONGLONG)24 * 3600 * 10000000;
1066 if (llTime >= llDiv) {
1067 lpsz += wsprintf(lpsz, TEXT("%d days "), (LONG)(llTime / llDiv));
1068 llTime = llTime % llDiv;
1070 llDiv = (LONGLONG)3600 * 10000000;
1071 if (llTime >= llDiv) {
1072 lpsz += wsprintf(lpsz, TEXT("%d hrs "), (LONG)(llTime / llDiv));
1073 llTime = llTime % llDiv;
1075 llDiv = (LONGLONG)60 * 10000000;
1076 if (llTime >= llDiv) {
1077 lpsz += wsprintf(lpsz, TEXT("%d mins "), (LONG)(llTime / llDiv));
1078 llTime = llTime % llDiv;
1080 wsprintf(lpsz, TEXT("%d.%3.3d sec"),
1081 (LONG)llTime / 10000000,
1082 (LONG)((llTime % 10000000) / 10000));
1085 #endif // __STREAMS__
1088 /* Display pin */
1089 CDisp::CDisp(IPin *pPin)
1091 PIN_INFO pi;
1092 TCHAR str[MAX_PIN_NAME];
1093 CLSID clsid;
1095 if (pPin) {
1096 pPin->QueryPinInfo(&pi);
1097 pi.pFilter->GetClassID(&clsid);
1098 QueryPinInfoReleaseFilter(pi);
1099 #ifndef UNICODE
1100 WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1,
1101 str, MAX_PIN_NAME, NULL, NULL);
1102 #else
1103 lstrcpy(str, pi.achName);
1104 #endif
1105 } else {
1106 lstrcpy(str, TEXT("NULL IPin"));
1109 m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];
1110 if (!m_pString) {
1111 return;
1114 wsprintf(m_pString, TEXT("%hs(%s)"), GuidNames[clsid], str);
1117 /* Display filter or pin */
1118 CDisp::CDisp(IUnknown *pUnk)
1120 IBaseFilter *pf;
1121 HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf);
1122 if(SUCCEEDED(hr))
1124 FILTER_INFO fi;
1125 hr = pf->QueryFilterInfo(&fi);
1126 if(SUCCEEDED(hr))
1128 QueryFilterInfoReleaseGraph(fi);
1130 m_pString = new TCHAR[lstrlenW(fi.achName) + 1];
1131 if(m_pString)
1133 wsprintf(m_pString, TEXT("%ls"), fi.achName);
1137 pf->Release();
1139 return;
1142 IPin *pp;
1143 hr = pUnk->QueryInterface(IID_IPin, (void **)&pp);
1144 if(SUCCEEDED(hr))
1146 CDisp::CDisp(pp);
1147 pp->Release();
1148 return;
1153 CDisp::~CDisp()
1157 CDispBasic::~CDispBasic()
1159 if (m_pString != m_String) {
1160 delete [] m_pString;
1164 CDisp::CDisp(double d)
1166 #ifdef DEBUG
1167 _stprintf(m_String, TEXT("%.16g"), d);
1168 #else
1169 wsprintf(m_String, TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000));
1170 #endif
1174 /* If built for debug this will display the media type details. We convert the
1175 major and subtypes into strings and also ask the base classes for a string
1176 description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit
1177 We also display the fields in the BITMAPINFOHEADER structure, this should
1178 succeed as we do not accept input types unless the format is big enough */
1180 #ifdef DEBUG
1181 void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn)
1184 /* Dump the GUID types and a short description */
1186 DbgLog((LOG_TRACE,5,TEXT("")));
1187 DbgLog((LOG_TRACE,2,TEXT("%s M type %hs S type %hs"), label,
1188 GuidNames[pmtIn->majortype],
1189 GuidNames[pmtIn->subtype]));
1190 DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));
1192 /* Dump the generic media types */
1194 if (pmtIn->bTemporalCompression) {
1195 DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));
1196 } else {
1197 DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));
1200 if (pmtIn->bFixedSizeSamples) {
1201 DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));
1202 } else {
1203 DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));
1206 if (pmtIn->formattype == FORMAT_VideoInfo) {
1207 /* Dump the contents of the BITMAPINFOHEADER structure */
1208 BITMAPINFOHEADER *pbmi = HEADER(pmtIn->pbFormat);
1209 VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat;
1211 DbgLog((LOG_TRACE,5,TEXT("Source rectangle (Left %d Top %d Right %d Bottom %d)"),
1212 pVideoInfo->rcSource.left,
1213 pVideoInfo->rcSource.top,
1214 pVideoInfo->rcSource.right,
1215 pVideoInfo->rcSource.bottom));
1217 DbgLog((LOG_TRACE,5,TEXT("Target rectangle (Left %d Top %d Right %d Bottom %d)"),
1218 pVideoInfo->rcTarget.left,
1219 pVideoInfo->rcTarget.top,
1220 pVideoInfo->rcTarget.right,
1221 pVideoInfo->rcTarget.bottom));
1223 DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));
1224 if (pbmi->biCompression < 256) {
1225 DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit (%d)"),
1226 pbmi->biWidth, pbmi->biHeight,
1227 pbmi->biBitCount, pbmi->biCompression));
1228 } else {
1229 DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),
1230 pbmi->biWidth, pbmi->biHeight,
1231 pbmi->biBitCount, &pbmi->biCompression));
1234 DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));
1235 DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));
1236 DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));
1237 DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));
1238 DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));
1240 } else if (pmtIn->majortype == MEDIATYPE_Audio) {
1241 DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"),
1242 GuidNames[pmtIn->formattype]));
1243 DbgLog((LOG_TRACE,2,TEXT(" Subtype %hs"),
1244 GuidNames[pmtIn->subtype]));
1246 if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)
1247 && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))
1249 /* Dump the contents of the WAVEFORMATEX type-specific format structure */
1251 WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;
1252 DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));
1253 DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));
1254 DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));
1255 DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));
1256 DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));
1257 DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));
1259 /* PCM uses a WAVEFORMAT and does not have the extra size field */
1261 if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {
1262 DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));
1264 } else {
1267 } else {
1268 DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"),
1269 GuidNames[pmtIn->formattype]));
1270 // !!!! should add code to dump wave format, others
1275 void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel)
1277 if( !pGraph )
1279 return;
1282 IEnumFilters *pFilters;
1284 DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph));
1286 if (FAILED(pGraph->EnumFilters(&pFilters))) {
1287 DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!")));
1290 IBaseFilter *pFilter;
1291 ULONG n;
1292 while (pFilters->Next(1, &pFilter, &n) == S_OK) {
1293 FILTER_INFO info;
1295 if (FAILED(pFilter->QueryFilterInfo(&info))) {
1296 DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] -- failed QueryFilterInfo"), pFilter));
1297 } else {
1298 QueryFilterInfoReleaseGraph(info);
1300 // !!! should QueryVendorInfo here!
1302 DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] '%ls'"), pFilter, info.achName));
1304 IEnumPins *pins;
1306 if (FAILED(pFilter->EnumPins(&pins))) {
1307 DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!")));
1308 } else {
1310 IPin *pPin;
1311 while (pins->Next(1, &pPin, &n) == S_OK) {
1312 PIN_INFO info;
1314 if (FAILED(pPin->QueryPinInfo(&info))) {
1315 DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin));
1316 } else {
1317 QueryPinInfoReleaseFilter(info);
1319 IPin *pPinConnected = NULL;
1321 HRESULT hr = pPin->ConnectedTo(&pPinConnected);
1323 if (pPinConnected) {
1324 DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] '%ls' [%sput]")
1325 TEXT(" Connected to pin [%x]"),
1326 pPin, info.achName,
1327 info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"),
1328 pPinConnected));
1330 pPinConnected->Release();
1332 // perhaps we should really dump the type both ways as a sanity
1333 // check?
1334 if (info.dir == PINDIR_OUTPUT) {
1335 AM_MEDIA_TYPE mt;
1337 hr = pPin->ConnectionMediaType(&mt);
1339 if (SUCCEEDED(hr)) {
1340 DisplayType(TEXT("Connection type"), &mt);
1342 FreeMediaType(mt);
1345 } else {
1346 DbgLog((LOG_TRACE,dwLevel,
1347 TEXT(" Pin [%x] '%ls' [%sput]"),
1348 pPin, info.achName,
1349 info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out")));
1354 pPin->Release();
1358 pins->Release();
1363 pFilter->Release();
1366 pFilters->Release();
1370 #endif