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