1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes - implements ActiveX system debugging
7 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
8 //------------------------------------------------------------------------------
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
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
);
89 if (GetProfileInt(m_ModuleName
, TEXT("BreakOnLoad"), 0))
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
);
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
) {
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
]));
159 m_Levels
[lKeyPos
] = max(dwKeyValue
,m_Levels
[lKeyPos
]);
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
);
208 WideCharToMultiByte(CP_ACP
, 0, psz
, -1, szDest
, NUMELMS(szDest
), 0, 0);
209 WriteFile (m_hOutput
, szDest
, cb
, &dw
, NULL
);
211 WriteFile (m_hOutput
, psz
, cb
, &dw
, NULL
);
214 OutputDebugString (psz
);
218 /* Called by DbgInitGlobalSettings to setup alternate logging destinations
221 void WINAPI
DbgInitLogTo (
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
;
261 if (!lstrcmpi(szFile
, TEXT("Console"))) {
262 m_hOutput
= GetStdHandle (STD_OUTPUT_HANDLE
);
263 if (m_hOutput
== INVALID_HANDLE_VALUE
) {
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
,
276 FILE_ATTRIBUTE_NORMAL
,
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")));
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")));
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
,'\\');
376 lstrcpy(m_ModuleName
,pName
);
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(
406 INT
MessageBoxOtherThread(
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
);
420 MsgBoxMsg msg
= {hwnd
, szTitle
, szMessage
, dwFlags
, 0};
422 HANDLE hThread
= CreateThread(
431 WaitForSingleObject(hThread
, INFINITE
);
432 CloseHandle(hThread
);
436 // break into debugger on failure.
441 /* Displays a message box if the condition evaluated to FALSE */
443 void WINAPI
DbgAssert(const TCHAR
*pCondition
,const TCHAR
*pFileName
,INT iLine
)
447 DbgKernelAssert(pCondition
, pFileName
, iLine
);
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"),
464 case IDNO
: /* Kill the application */
466 FatalAppExit(FALSE
, TEXT("Application terminated"));
469 case IDCANCEL
: /* Break into the debugger */
474 case IDYES
: /* Ignore assertion continue execution */
480 /* Displays a message box at a break point */
482 void WINAPI
DbgBreakPoint(const TCHAR
*pCondition
,const TCHAR
*pFileName
,INT iLine
)
486 DbgKernelAssert(pCondition
, pFileName
, iLine
);
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"),
502 case IDNO
: /* Kill the application */
504 FatalAppExit(FALSE
, TEXT("Application terminated"));
507 case IDCANCEL
: /* Break into the debugger */
512 case IDYES
: /* Ignore break point continue execution */
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
);
532 va_start( va
, szFormatString
);
534 int nReturnValue
= _vsntprintf( szBreakPointMessage
, MAX_CHARS_IN_BREAK_POINT_MESSAGE
, szFormatString
, 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." );
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
575 DbgInitModuleSettings(false);
582 // If no valid bits are set return FALSE
583 if ((Type
& ((1<<iMAXLEVELS
)-1))) {
585 // speed up unconditional output.
589 for (LONG lKeyPos
= 0;lKeyPos
< iMAXLEVELS
;lKeyPos
++) {
591 if (Level
<= (m_Levels
[lKeyPos
] & ~LOG_FORCIBLY_SET
)) {
602 /* Set debug levels to a given value */
604 void WINAPI
DbgSetModuleLevel(DWORD Type
, DWORD Level
)
608 for (LONG lKeyPos
= 0;lKeyPos
< iMAXLEVELS
;lKeyPos
++) {
610 m_Levels
[lKeyPos
] = Level
| LOG_FORCIBLY_SET
;
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
;
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
) {
639 /* Format the variable length parameter list */
642 va_start(va
, pFormat
);
644 lstrcpy(szInfo
,m_ModuleName
);
645 wsprintf(szInfo
+ lstrlen(szInfo
),
646 TEXT("(tid %x) %8d : "),
647 GetCurrentThreadId(), timeGetTime() - dwTimeOffset
);
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
);
662 void DbgAssert(const CHAR
*pCondition
,const CHAR
*pFileName
,INT iLine
)
666 DbgKernelAssert(pCondition
, pFileName
, iLine
);
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"),
683 case IDNO
: /* Kill the application */
685 FatalAppExit(FALSE
, TEXT("Application terminated"));
688 case IDCANCEL
: /* Break into the debugger */
693 case IDYES
: /* Ignore assertion continue execution */
699 /* Displays a message box at a break point */
701 void WINAPI
DbgBreakPoint(const CHAR
*pCondition
,const CHAR
*pFileName
,INT iLine
)
705 DbgKernelAssert(pCondition
, pFileName
, iLine
);
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"),
721 case IDNO
: /* Kill the application */
723 FatalAppExit(FALSE
, TEXT("Application terminated"));
726 case IDCANCEL
: /* Break into the debugger */
731 case IDYES
: /* Ignore break point continue execution */
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
));
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
) {
767 /* Format the variable length parameter list */
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
);
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
));
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
;
816 /* It is valid to pass a NULL object name */
817 if (pObject
== NULL
) {
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
;
847 DWORD ObjectCookie
= pObject
->m_dwCookie
;
848 ASSERT(ObjectCookie
);
851 DbgLog((LOG_MEMORY
,2,TEXT("Object created %d (%ls) %d Active"),
852 pObject
->m_dwCookie
, wszObjectName
, m_dwObjectCount
));
854 DbgLog((LOG_MEMORY
,2,TEXT("Object created %d (%hs) %d Active"),
855 pObject
->m_dwCookie
, szObjectName
, m_dwObjectCount
));
858 LeaveCriticalSection(&m_CSDebug
);
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 */
879 if (pObject
->m_dwCookie
== dwCookie
) {
883 pObject
= pObject
->m_pNext
;
886 if (pObject
== NULL
) {
887 DbgBreak("Apparently destroying a bogus object");
888 LeaveCriticalSection(&m_CSDebug
);
892 /* Is the object at the head of the list */
894 if (pPrevious
== NULL
) {
895 pListHead
= pObject
->m_pNext
;
897 pPrevious
->m_pNext
= pObject
->m_pNext
;
900 /* Delete the object and update the housekeeping information */
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
));
908 DbgLog((LOG_MEMORY
,2,TEXT("Object destroyed %d (%hs) %d Active"),
909 pObject
->m_dwCookie
, pObject
->m_szName
, m_dwObjectCount
));
913 LeaveCriticalSection(&m_CSDebug
);
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("")));
936 if(pObject
->m_wszName
) {
937 wsprintf(szInfo
,TEXT("%5d (%8x) %30ls"),pObject
->m_dwCookie
, &pObject
, pObject
->m_wszName
);
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
)
956 dwWaitResult
= WaitForSingleObject(h
, dwWaitTimeout
);
957 ASSERT(dwWaitResult
== WAIT_OBJECT_0
);
958 } while (dwWaitResult
== WAIT_TIMEOUT
);
961 DWORD WINAPI
DbgWaitForMultipleObjects(DWORD nCount
,
962 CONST HANDLE
*lpHandles
,
967 dwWaitResult
= WaitForMultipleObjects(nCount
,
971 ASSERT((DWORD
)(dwWaitResult
- WAIT_OBJECT_0
) < MAXIMUM_WAIT_OBJECTS
);
972 } while (dwWaitResult
== WAIT_TIMEOUT
);
976 void WINAPI
DbgSetWaitTimeout(DWORD dwTimeout
)
978 dwWaitTimeout
= dwTimeout
;
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 } } },
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
) {
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
1031 // always output at least one digit
1033 // Get the rightmost digit - we only need the low word
1034 digit
= li
.LowPart
% 10;
1036 temp
[--pos
] = (TCHAR
) digit
+L
'0';
1037 } while (li
.QuadPart
);
1038 wsprintf(m_String
, TEXT("%s"), temp
+pos
);
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
);
1057 CDisp::CDisp(CRefTime llTime
)
1059 LPTSTR lpsz
= m_String
;
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__
1089 CDisp::CDisp(IPin
*pPin
)
1092 TCHAR str
[MAX_PIN_NAME
];
1096 pPin
->QueryPinInfo(&pi
);
1097 pi
.pFilter
->GetClassID(&clsid
);
1098 QueryPinInfoReleaseFilter(pi
);
1100 WideCharToMultiByte(GetACP(), 0, pi
.achName
, lstrlenW(pi
.achName
) + 1,
1101 str
, MAX_PIN_NAME
, NULL
, NULL
);
1103 lstrcpy(str
, pi
.achName
);
1106 lstrcpy(str
, TEXT("NULL IPin"));
1109 m_pString
= (PTCHAR
) new TCHAR
[lstrlen(str
)+64];
1114 wsprintf(m_pString
, TEXT("%hs(%s)"), GuidNames
[clsid
], str
);
1117 /* Display filter or pin */
1118 CDisp::CDisp(IUnknown
*pUnk
)
1121 HRESULT hr
= pUnk
->QueryInterface(IID_IBaseFilter
, (void **)&pf
);
1125 hr
= pf
->QueryFilterInfo(&fi
);
1128 QueryFilterInfoReleaseGraph(fi
);
1130 m_pString
= new TCHAR
[lstrlenW(fi
.achName
) + 1];
1133 wsprintf(m_pString
, TEXT("%ls"), fi
.achName
);
1143 hr
= pUnk
->QueryInterface(IID_IPin
, (void **)&pp
);
1157 CDispBasic::~CDispBasic()
1159 if (m_pString
!= m_String
) {
1160 delete [] m_pString
;
1164 CDisp::CDisp(double d
)
1167 _stprintf(m_String
, TEXT("%.16g"), d
);
1169 wsprintf(m_String
, TEXT("%d.%03d"), (int) d
, (int) ((d
- (int) d
) * 1000));
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 */
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")));
1197 DbgLog((LOG_TRACE
,5,TEXT("Not temporally compressed")));
1200 if (pmtIn
->bFixedSizeSamples
) {
1201 DbgLog((LOG_TRACE
,5,TEXT("Sample size %d"),pmtIn
->lSampleSize
));
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
));
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
));
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
)
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
;
1292 while (pFilters
->Next(1, &pFilter
, &n
) == S_OK
) {
1295 if (FAILED(pFilter
->QueryFilterInfo(&info
))) {
1296 DbgLog((LOG_TRACE
,dwLevel
,TEXT(" Filter [%x] -- failed QueryFilterInfo"), pFilter
));
1298 QueryFilterInfoReleaseGraph(info
);
1300 // !!! should QueryVendorInfo here!
1302 DbgLog((LOG_TRACE
,dwLevel
,TEXT(" Filter [%x] '%ls'"), pFilter
, info
.achName
));
1306 if (FAILED(pFilter
->EnumPins(&pins
))) {
1307 DbgLog((LOG_TRACE
,dwLevel
,TEXT("EnumPins failed!")));
1311 while (pins
->Next(1, &pPin
, &n
) == S_OK
) {
1314 if (FAILED(pPin
->QueryPinInfo(&info
))) {
1315 DbgLog((LOG_TRACE
,dwLevel
,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin
));
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]"),
1327 info
.dir
== PINDIR_INPUT
? TEXT("In") : TEXT("Out"),
1330 pPinConnected
->Release();
1332 // perhaps we should really dump the type both ways as a sanity
1334 if (info
.dir
== PINDIR_OUTPUT
) {
1337 hr
= pPin
->ConnectionMediaType(&mt
);
1339 if (SUCCEEDED(hr
)) {
1340 DisplayType(TEXT("Connection type"), &mt
);
1346 DbgLog((LOG_TRACE
,dwLevel
,
1347 TEXT(" Pin [%x] '%ls' [%sput]"),
1349 info
.dir
== PINDIR_INPUT
? TEXT("In") : TEXT("Out")));
1366 pFilters
->Release();