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