home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / wxdebug.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-06  |  30.6 KB  |  916 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11.  
  12. // ActiveX system debugging facilities
  13.  
  14. #define _WINDLL
  15.  
  16. #include <streams.h>
  17. #include <stdarg.h>
  18. #include <stdio.h>
  19. #include <tchar.h>
  20.  
  21. #ifdef DEBUG
  22.  
  23. const INT iDEBUGINFO = 512;                 // Used to format strings
  24. const INT iMAXLEVELS = 5;                   // Maximum debug categories
  25.  
  26. HINSTANCE m_hInst;                          // Module instance handle
  27. TCHAR m_ModuleName[iDEBUGINFO];             // Cut down module name
  28. DWORD m_Levels[iMAXLEVELS];                 // Debug level per category
  29. CRITICAL_SECTION m_CSDebug;                 // Controls access to list
  30. DWORD m_dwNextCookie;                       // Next active object ID
  31. ObjectDesc *pListHead = NULL;               // First active object
  32. DWORD m_dwObjectCount;                      // Active object count
  33. BOOL m_bInit = FALSE;                       // Have we been initialised
  34. HANDLE m_hOutput = INVALID_HANDLE_VALUE;    // Optional output written here
  35. DWORD dwWaitTimeout = INFINITE;             // Default timeout value
  36.  
  37. const TCHAR *pBaseKey = TEXT("SOFTWARE\\Debug");
  38. const TCHAR *pGlobalKey = TEXT("GLOBAL");
  39. static TCHAR *pUnknownName = TEXT("UNKNOWN");
  40.  
  41. /* For every module and executable we store a debugging level for each of
  42.    the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy
  43.    to isolate and debug individual modules without seeing everybody elses
  44.    spurious debug output. The keys are stored in the registry under the
  45.    HKEY_LOCAL_MACHINE\SOFTWARE\Debug\<Module Name>\<KeyName> key values
  46.    NOTE these must be in the same order as their enumeration definition */
  47.  
  48. TCHAR *pKeyNames[] = {
  49.     TEXT("TIMING"),      // Timing and performance measurements
  50.     TEXT("TRACE"),       // General step point call tracing
  51.     TEXT("MEMORY"),      // Memory and object allocation/destruction
  52.     TEXT("LOCKING"),     // Locking/unlocking of critical sections
  53.     TEXT("ERROR")        // Debug error notification
  54.     };
  55.  
  56. TCHAR *TimeoutName = TEXT("TIMEOUT");
  57.  
  58. /* This sets the instance handle that the debug library uses to find
  59.    the module's file name from the Win32 GetModuleFileName function */
  60.  
  61. void DbgInitialise(HINSTANCE hInst)
  62. {
  63.     InitializeCriticalSection(&m_CSDebug);
  64.     m_bInit = TRUE;
  65.  
  66.     m_hInst = hInst;
  67.     DbgInitModuleName();
  68.     if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0))
  69.        DebugBreak();
  70.     DbgInitModuleSettings();
  71.     DbgInitGlobalSettings();
  72. }
  73.  
  74.  
  75. /* This is called to clear up any resources the debug library uses - at the
  76.    moment we delete our critical section and the object list. The values we
  77.    retrieve from the registry are all done during initialisation but we don't
  78.    go looking for update notifications while we are running, if the values
  79.    are changed then the application has to be restarted to pick them up */
  80.  
  81. void DbgTerminate()
  82. {
  83.     if (m_hOutput != INVALID_HANDLE_VALUE) {
  84.        EXECUTE_ASSERT(CloseHandle(m_hOutput));
  85.        m_hOutput = INVALID_HANDLE_VALUE;
  86.     }
  87.     DeleteCriticalSection(&m_CSDebug);
  88.     m_bInit = FALSE;
  89. }
  90.  
  91.  
  92. /* This is called by DbgInitLogLevels to read the debug settings
  93.    for each logging category for this module from the registry */
  94.  
  95. void DbgInitKeyLevels(HKEY hKey)
  96. {
  97.     LONG lReturn;               // Create key return value
  98.     LONG lKeyPos;               // Current key category
  99.     DWORD dwKeySize;            // Size of the key value
  100.     DWORD dwKeyType;            // Receives it's type
  101.     DWORD dwKeyValue;           // This fields value
  102.  
  103.     /* Try and read a value for each key position in turn */
  104.     for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
  105.  
  106.         dwKeySize = sizeof(DWORD);
  107.         lReturn = RegQueryValueEx(
  108.             hKey,                       // Handle to an open key
  109.             pKeyNames[lKeyPos],         // Subkey name derivation
  110.             NULL,                       // Reserved field
  111.             &dwKeyType,                 // Returns the field type
  112.             (LPBYTE) &dwKeyValue,       // Returns the field's value
  113.             &dwKeySize );               // Number of bytes transferred
  114.  
  115.         /* If either the key was not available or it was not a DWORD value
  116.            then we ensure only the high priority debug logging is output
  117.            but we try and update the field to a zero filled DWORD value */
  118.  
  119.         if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {
  120.  
  121.             dwKeyValue = 0;
  122.             lReturn = RegSetValueEx(
  123.                 hKey,                   // Handle of an open key
  124.                 pKeyNames[lKeyPos],     // Address of subkey name
  125.                 (DWORD) 0,              // Reserved field
  126.                 REG_DWORD,              // Type of the key field
  127.                 (PBYTE) &dwKeyValue,    // Value for the field
  128.                 sizeof(DWORD));         // Size of the field buffer
  129.  
  130.             if (lReturn != ERROR_SUCCESS) {
  131.                 DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
  132.                 dwKeyValue = 0;
  133.             }
  134.         }
  135.         m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]);
  136.     }
  137.  
  138.     /*  Read the timeout value for catching hangs */
  139.     dwKeySize = sizeof(DWORD);
  140.     lReturn = RegQueryValueEx(
  141.         hKey,                       // Handle to an open key
  142.         TimeoutName,                // Subkey name derivation
  143.         NULL,                       // Reserved field
  144.         &dwKeyType,                 // Returns the field type
  145.         (LPBYTE) &dwWaitTimeout,    // Returns the field's value
  146.         &dwKeySize );               // Number of bytes transferred
  147.  
  148.     /* If either the key was not available or it was not a DWORD value
  149.        then we ensure only the high priority debug logging is output
  150.        but we try and update the field to a zero filled DWORD value */
  151.  
  152.     if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD)  {
  153.  
  154.         dwWaitTimeout = INFINITE;
  155.         lReturn = RegSetValueEx(
  156.             hKey,                   // Handle of an open key
  157.             TimeoutName,            // Address of subkey name
  158.             (DWORD) 0,              // Reserved field
  159.             REG_DWORD,              // Type of the key field
  160.             (PBYTE) &dwWaitTimeout, // Value for the field
  161.             sizeof(DWORD));         // Size of the field buffer
  162.  
  163.         if (lReturn != ERROR_SUCCESS) {
  164.             DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos]));
  165.             dwWaitTimeout = INFINITE;
  166.         }
  167.     }
  168. }
  169.  
  170. void DbgOutString(LPCTSTR psz)
  171. {
  172.     if (m_hOutput != INVALID_HANDLE_VALUE) {
  173.         UINT  cb = lstrlen(psz);
  174.         DWORD dw;
  175.         WriteFile (m_hOutput, psz, cb, &dw, NULL);
  176.     } else {
  177.         OutputDebugString (psz);
  178.     }
  179. }
  180.  
  181. /* Called by DbgInitGlobalSettings to setup alternate logging destinations
  182.  */
  183.  
  184. void DbgInitLogTo (
  185.     HKEY hKey)
  186. {
  187.     LONG  lReturn;
  188.     DWORD dwKeyType;
  189.     DWORD dwKeySize;
  190.     TCHAR szFile[MAX_PATH] = {0};
  191.     static const TCHAR cszKey[] = TEXT("LogToFile");
  192.  
  193.     dwKeySize = MAX_PATH;
  194.     lReturn = RegQueryValueEx(
  195.         hKey,                       // Handle to an open key
  196.         cszKey,                     // Subkey name derivation
  197.         NULL,                       // Reserved field
  198.         &dwKeyType,                 // Returns the field type
  199.         (LPBYTE) szFile,            // Returns the field's value
  200.         &dwKeySize);                // Number of bytes transferred
  201.  
  202.     // create an empty key if it does not already exist
  203.     //
  204.     if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ)
  205.        {
  206.        dwKeySize = 1;
  207.        lReturn = RegSetValueEx(
  208.             hKey,                   // Handle of an open key
  209.             cszKey,                 // Address of subkey name
  210.             (DWORD) 0,              // Reserved field
  211.             REG_SZ,                 // Type of the key field
  212.             (PBYTE)szFile,          // Value for the field
  213.             dwKeySize);            // Size of the field buffer
  214.        }
  215.  
  216.     // if an output-to was specified.  try to open it.
  217.     //
  218.     if (m_hOutput != INVALID_HANDLE_VALUE) {
  219.        EXECUTE_ASSERT(CloseHandle (m_hOutput));
  220.        m_hOutput = INVALID_HANDLE_VALUE;
  221.     }
  222.     if (szFile[0] != 0)
  223.        {
  224.        if (!lstrcmpi(szFile, TEXT("Console"))) {
  225.           m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
  226.           if (m_hOutput == INVALID_HANDLE_VALUE) {
  227.              AllocConsole ();
  228.              m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
  229.           }
  230.           SetConsoleTitle (TEXT("ActiveX Debug Output"));
  231.        } else if (szFile[0] &&
  232.                 lstrcmpi(szFile, TEXT("Debug")) &&
  233.                 lstrcmpi(szFile, TEXT("Debugger")) &&
  234.                 lstrcmpi(szFile, TEXT("Deb")))
  235.           {
  236.           m_hOutput = CreateFile(szFile, GENERIC_WRITE,
  237.                                  FILE_SHARE_READ,
  238.                                  NULL, OPEN_ALWAYS,
  239.                                  FILE_ATTRIBUTE_NORMAL,
  240.                                  NULL);
  241.           if (INVALID_HANDLE_VALUE != m_hOutput)
  242.               {
  243.               static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n");
  244.               SetFilePointer (m_hOutput, 0, NULL, FILE_END);
  245.               DbgOutString (cszBar);
  246.               }
  247.           }
  248.        }
  249. }
  250.  
  251.  
  252.  
  253. /* This is called by DbgInitLogLevels to read the global debug settings for
  254.    each logging category for this module from the registry. Normally each
  255.    module has it's own values set for it's different debug categories but
  256.    setting the global SOFTWARE\Debug\Global applies them to ALL modules */
  257.  
  258. void DbgInitGlobalSettings()
  259. {
  260.     LONG lReturn;               // Create key return value
  261.     TCHAR szInfo[iDEBUGINFO];   // Constructs key names
  262.     HKEY hGlobalKey;            // Global override key
  263.  
  264.     /* Construct the global base key name */
  265.     wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,pGlobalKey);
  266.  
  267.     /* Create or open the key for this module */
  268.     lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
  269.                              szInfo,               // Address of subkey name
  270.                              (DWORD) 0,            // Reserved value
  271.                              NULL,                 // Address of class name
  272.                              (DWORD) 0,            // Special options flags
  273.                              KEY_ALL_ACCESS,       // Desired security access
  274.                              NULL,                 // Key security descriptor
  275.                              &hGlobalKey,          // Opened handle buffer
  276.                              NULL);                // What really happened
  277.  
  278.     if (lReturn != ERROR_SUCCESS) {
  279.         DbgLog((LOG_ERROR,0,TEXT("Could not access GLOBAL module key")));
  280.         return;
  281.     }
  282.  
  283.     DbgInitKeyLevels(hGlobalKey);
  284.     RegCloseKey(hGlobalKey);
  285. }
  286.  
  287.  
  288. /* This sets the debugging log levels for the different categories. We start
  289.    by opening (or creating if not already available) the SOFTWARE\Debug key
  290.    that all these settings live under. We then look at the global values
  291.    set under SOFTWARE\Debug\Global which apply on top of the individual
  292.    module settings. We then load the individual module registry settings */
  293.  
  294. void DbgInitModuleSettings()
  295. {
  296.     LONG lReturn;               // Create key return value
  297.     TCHAR szInfo[iDEBUGINFO];   // Constructs key names
  298.     HKEY hModuleKey;            // Module key handle
  299.  
  300.     /* Construct the base key name */
  301.     wsprintf(szInfo,TEXT("%s\\%s"),pBaseKey,m_ModuleName);
  302.  
  303.     /* Create or open the key for this module */
  304.     lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE,   // Handle of an open key
  305.                              szInfo,               // Address of subkey name
  306.                              (DWORD) 0,            // Reserved value
  307.                              NULL,                 // Address of class name
  308.                              (DWORD) 0,            // Special options flags
  309.                              KEY_ALL_ACCESS,       // Desired security access
  310.                              NULL,                 // Key security descriptor
  311.                              &hModuleKey,          // Opened handle buffer
  312.                              NULL);                // What really happened
  313.  
  314.     if (lReturn != ERROR_SUCCESS) {
  315.         DbgLog((LOG_ERROR,0,TEXT("Could not access module key")));
  316.         return;
  317.     }
  318.  
  319.     DbgInitLogTo(hModuleKey);
  320.     DbgInitKeyLevels(hModuleKey);
  321.     RegCloseKey(hModuleKey);
  322. }
  323.  
  324.  
  325. /* Initialise the module file name */
  326.  
  327. void DbgInitModuleName()
  328. {
  329.     TCHAR FullName[iDEBUGINFO];     // Load the full path and module name
  330.     TCHAR *pName;                   // Searches from the end for a backslash
  331.  
  332.     GetModuleFileName(m_hInst,FullName,iDEBUGINFO);
  333.     pName = _tcsrchr(FullName,'\\');
  334.     if (pName == NULL) {
  335.         pName = FullName;
  336.     } else {
  337.         pName++;
  338.     }
  339.     lstrcpy(m_ModuleName,pName);
  340. }
  341.  
  342.  
  343. /* Displays a message box if the condition evaluated to FALSE */
  344.  
  345. void DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
  346. {
  347.     TCHAR szInfo[iDEBUGINFO];
  348.  
  349.     wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
  350.              pCondition, iLine, pFileName);
  351.  
  352.     INT MsgId = MessageBox(NULL,szInfo,TEXT("ASSERT Failed"),
  353.                            MB_SYSTEMMODAL |
  354.                            MB_ICONHAND |
  355.                            MB_YESNOCANCEL |
  356.                            MB_SETFOREGROUND);
  357.     switch (MsgId)
  358.     {
  359.         case IDNO:              /* Kill the application */
  360.  
  361.             FatalAppExit(FALSE, TEXT("Application terminated"));
  362.             break;
  363.  
  364.         case IDCANCEL:          /* Break into the debugger */
  365.  
  366.             DebugBreak();
  367.             break;
  368.  
  369.         case IDYES:             /* Ignore assertion continue execution */
  370.             break;
  371.     }
  372. }
  373.  
  374. /* Displays a message box at a break point */
  375.  
  376. void DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
  377. {
  378.     TCHAR szInfo[iDEBUGINFO];
  379.  
  380.     wsprintf(szInfo, TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"),
  381.              pCondition, iLine, pFileName);
  382.  
  383.     INT MsgId = MessageBox(NULL,szInfo,TEXT("Hard coded break point"),
  384.                            MB_SYSTEMMODAL |
  385.                            MB_ICONHAND |
  386.                            MB_YESNOCANCEL |
  387.                            MB_SETFOREGROUND);
  388.     switch (MsgId)
  389.     {
  390.         case IDNO:              /* Kill the application */
  391.  
  392.             FatalAppExit(FALSE, TEXT("Application terminated"));
  393.             break;
  394.  
  395.         case IDCANCEL:          /* Break into the debugger */
  396.  
  397.             DebugBreak();
  398.             break;
  399.  
  400.         case IDYES:             /* Ignore break point continue execution */
  401.             break;
  402.     }
  403. }
  404.  
  405.  
  406. /* When we initialised the library we stored in the m_Levels array the current
  407.    debug output level for this module for each of the five categories. When
  408.    some debug logging is sent to us it can be sent with a combination of the
  409.    categories (if it is applicable to many for example) in which case we map
  410.    the type's categories into their current debug levels and see if any of
  411.    them can be accepted. The function looks at each bit position in turn from
  412.    the input type field and then compares it's debug level with the modules.
  413.  
  414.    A level of 0 means that output is always sent to the debugger.  This is
  415.    due to producing output if the input level is <= m_Levels.
  416. */
  417.  
  418.  
  419. BOOL DbgCheckModuleLevel(DWORD Type,DWORD Level)
  420. {
  421.     DWORD Mask = 0x01;
  422.  
  423.     // If no valid bits are set return FALSE
  424.     if ((Type & ((1<<iMAXLEVELS)-1))) {
  425.  
  426.     // speed up unconditional output.
  427.     if (0==Level)
  428.         return(TRUE);
  429.     
  430.         for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
  431.             if (Type & Mask) {
  432.                 if (Level <= m_Levels[lKeyPos]) {
  433.                     return TRUE;
  434.                 }
  435.             }
  436.             Mask <<= 1;
  437.         }
  438.     }
  439.     return FALSE;
  440. }
  441.  
  442.  
  443. /* Set debug levels to a given value */
  444.  
  445. void DbgSetModuleLevel(DWORD Type, DWORD Level)
  446. {
  447.     DWORD Mask = 0x01;
  448.  
  449.     for (LONG lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) {
  450.         if (Type & Mask) {
  451.             m_Levels[lKeyPos] = Level;
  452.         }
  453.         Mask <<= 1;
  454.     }
  455. }
  456.  
  457. /* Print a formatted string to the debugger prefixed with this module's name
  458.    Because the COMBASE classes are linked statically every module loaded will
  459.    have their own copy of this code. It therefore helps if the module name is
  460.    included on the output so that the offending code can be easily found */
  461.  
  462. void DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...)
  463. {
  464.     /* Check the current level for this type combination */
  465.  
  466.     BOOL bAccept = DbgCheckModuleLevel(Type,Level);
  467.     if (bAccept == FALSE) {
  468.         return;
  469.     }
  470.  
  471.     TCHAR szInfo[iDEBUGINFO];
  472.  
  473.     /* Format the variable length parameter list */
  474.  
  475.     va_list va;
  476.     va_start(va, pFormat);
  477.  
  478.     lstrcpy(szInfo,m_ModuleName);
  479.     wsprintf(szInfo + lstrlen(szInfo),
  480.              TEXT("(tid %x) : "),
  481.              GetCurrentThreadId());
  482.  
  483.     wvsprintf(szInfo + lstrlen(szInfo), pFormat, va);
  484.     lstrcat(szInfo, TEXT("\r\n"));
  485.     DbgOutString(szInfo);
  486.  
  487.     va_end(va);
  488. }
  489.  
  490.  
  491. /* If we are executing as a pure kernel filter we cannot display message
  492.    boxes to the user, this provides an alternative which puts the error
  493.    condition on the debugger output with a suitable eye catching message */
  494.  
  495. void DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine)
  496. {
  497.     DbgLog((LOG_ERROR,0,TEXT("Assertion FAILED (%s) at line %d in file %s"),
  498.            pCondition, iLine, pFileName));
  499. }
  500.  
  501.  
  502.  
  503. /* Each time we create an object derived from CBaseObject the constructor will
  504.    call us to register the creation of the new object. We are passed a string
  505.    description which we store away. We return a cookie that the constructor
  506.    uses to identify the object when it is destroyed later on. We update the
  507.    total number of active objects in the DLL mainly for debugging purposes */
  508.  
  509. DWORD DbgRegisterObjectCreation(TCHAR *pObjectName)
  510. {
  511.     /* If this fires you have a mixed DEBUG/RETAIL build */
  512.  
  513.     ASSERT(pObjectName);
  514.  
  515.     /* Create a place holder for this object description */
  516.  
  517.     ObjectDesc *pObject = new ObjectDesc;
  518.     ASSERT(pObject);
  519.  
  520.     /* It is valid to pass a NULL object name */
  521.     if (pObject == NULL) {
  522.         return FALSE;
  523.     }
  524.  
  525.     /* Check we have been initialised - we may not be initialised when we are
  526.        being pulled in from an executable which has globally defined objects
  527.        as they are created by the C++ run time before WinMain is called */
  528.  
  529.     if (m_bInit == FALSE) {
  530.         DbgInitialise(GetModuleHandle(NULL));
  531.     }
  532.  
  533.     /* Grab the list critical section */
  534.     EnterCriticalSection(&m_CSDebug);
  535.  
  536.     /* If no name then default to UNKNOWN */
  537.     if (pObjectName == NULL) {
  538.         pObjectName = pUnknownName;
  539.     }
  540.  
  541.     /* Put the new description at the head of the list */
  542.  
  543.     pObject->m_pName = pObjectName;
  544.     pObject->m_dwCookie = ++m_dwNextCookie;
  545.     pObject->m_pNext = pListHead;
  546.  
  547.     pListHead = pObject;
  548.     m_dwObjectCount++;
  549.  
  550.     DWORD ObjectCookie = pObject->m_dwCookie;
  551.     ASSERT(ObjectCookie);
  552.  
  553.     DbgLog((LOG_MEMORY,2,TEXT("Object created   %d (%s) %d Active"),
  554.             pObject->m_dwCookie, pObjectName, m_dwObjectCount));
  555.  
  556.     LeaveCriticalSection(&m_CSDebug);
  557.     return ObjectCookie;
  558. }
  559.  
  560.  
  561. /* This is called by the CBaseObject destructor when an object is about to be
  562.    destroyed, we are passed the cookie we returned during construction that
  563.    identifies this object. We scan the object list for a matching cookie and
  564.    remove the object if successful. We also update the active object count */
  565.  
  566. BOOL DbgRegisterObjectDestruction(DWORD dwCookie)
  567. {
  568.     /* Grab the list critical section */
  569.     EnterCriticalSection(&m_CSDebug);
  570.  
  571.     ObjectDesc *pObject = pListHead;
  572.     ObjectDesc *pPrevious = NULL;
  573.  
  574.     /* Scan the object list looking for a cookie match */
  575.  
  576.     while (pObject) {
  577.         if (pObject->m_dwCookie == dwCookie) {
  578.             break;
  579.         }
  580.         pPrevious = pObject;
  581.         pObject = pObject->m_pNext;
  582.     }
  583.  
  584.     if (pObject == NULL) {
  585.         DbgBreak("Apparently destroying a bogus object");
  586.         LeaveCriticalSection(&m_CSDebug);
  587.         return FALSE;
  588.     }
  589.  
  590.     /* Is the object at the head of the list */
  591.  
  592.     if (pPrevious == NULL) {
  593.         pListHead = pObject->m_pNext;
  594.     } else {
  595.         pPrevious->m_pNext = pObject->m_pNext;
  596.     }
  597.  
  598.     /* Delete the object and update the housekeeping information */
  599.  
  600.     m_dwObjectCount--;
  601.     DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%s) %d Active"),
  602.             pObject->m_dwCookie, pObject->m_pName, m_dwObjectCount));
  603.  
  604.     delete pObject;
  605.     LeaveCriticalSection(&m_CSDebug);
  606.     return TRUE;
  607. }
  608.  
  609.  
  610. /* This runs through the active object list displaying their details */
  611.  
  612. void DbgDumpObjectRegister()
  613. {
  614.     TCHAR szInfo[iDEBUGINFO];
  615.  
  616.     /* Grab the list critical section */
  617.  
  618.     EnterCriticalSection(&m_CSDebug);
  619.     ObjectDesc *pObject = pListHead;
  620.  
  621.     /* Scan the object list displaying the name and cookie */
  622.  
  623.     DbgLog((LOG_MEMORY,2,TEXT("")));
  624.     DbgLog((LOG_MEMORY,2,TEXT("   ID             Object Description")));
  625.     DbgLog((LOG_MEMORY,2,TEXT("")));
  626.  
  627.     while (pObject) {
  628.         wsprintf(szInfo,TEXT("%5d (%8x) %30s"),pObject->m_dwCookie, &pObject, pObject->m_pName);
  629.         DbgLog((LOG_MEMORY,2,szInfo));
  630.         pObject = pObject->m_pNext;
  631.     }
  632.  
  633.     wsprintf(szInfo,TEXT("Total object count %5d"),m_dwObjectCount);
  634.     DbgLog((LOG_MEMORY,2,TEXT("")));
  635.     DbgLog((LOG_MEMORY,1,szInfo));
  636.     LeaveCriticalSection(&m_CSDebug);
  637. }
  638.  
  639. /*  Debug infinite wait stuff */
  640. DWORD DbgWaitForSingleObject(HANDLE h)
  641. {
  642.     DWORD dwWaitResult;
  643.     do {
  644.         dwWaitResult = WaitForSingleObject(h, dwWaitTimeout);
  645.         ASSERT(dwWaitResult == WAIT_OBJECT_0);
  646.     } while (dwWaitResult == WAIT_TIMEOUT);
  647.     return dwWaitResult;
  648. }
  649. DWORD DbgWaitForMultipleObjects(DWORD nCount,
  650.                                 CONST HANDLE *lpHandles,
  651.                                 BOOL bWaitAll)
  652. {
  653.     DWORD dwWaitResult;
  654.     do {
  655.         dwWaitResult = WaitForMultipleObjects(nCount,
  656.                                               lpHandles,
  657.                                               bWaitAll,
  658.                                               dwWaitTimeout);
  659.         ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS);
  660.     } while (dwWaitResult == WAIT_TIMEOUT);
  661.     return dwWaitResult;
  662. }
  663.  
  664. void DbgSetWaitTimeout(DWORD dwTimeout)
  665. {
  666.     dwWaitTimeout = dwTimeout;
  667. }
  668.  
  669. #endif /* DEBUG */
  670.  
  671. #ifdef _OBJBASE_H_
  672.  
  673.     /*  Stuff for printing out our GUID names */
  674.  
  675.     GUID_STRING_ENTRY g_GuidNames[] = {
  676.     #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
  677.     { TEXT(#name), { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } } },
  678.         #include <uuids.h>
  679.     };
  680.  
  681.     CGuidNameList GuidNames;
  682.     int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]);
  683.  
  684.     TCHAR *CGuidNameList::operator [] (const GUID &guid)
  685.     {
  686.         for (int i = 0; i < g_cGuidNames; i++) {
  687.             if (g_GuidNames[i].guid == guid) {
  688.                 return g_GuidNames[i].szName;
  689.             }
  690.         }
  691.         if (guid == GUID_NULL) {
  692.             return TEXT("GUID_NULL");
  693.         }
  694.  
  695.     // shouldn't this print the hex CLSID?
  696.         return TEXT("Unknown GUID Name");
  697.     }
  698.  
  699. #endif /* _OBJBASE_H_ */
  700.  
  701. /*  CDisp class - display our data types */
  702.  
  703. // clashes with REFERENCE_TIME
  704. CDisp::CDisp(LONGLONG ll, int Format)
  705. {
  706.     // note: this could be combined with CDisp(LONGLONG) by
  707.     // introducing a default format of CDISP_REFTIME
  708.     LARGE_INTEGER li;
  709.     li.QuadPart = ll;
  710.     switch (Format) {
  711.     case CDISP_DEC:
  712.     {
  713.         TCHAR  temp[20];
  714.         int pos=20;
  715.         temp[--pos] = 0;
  716.         int digit;
  717.         // always output at least one digit
  718.         do {
  719.         // Get the rightmost digit - we only need the low word
  720.             digit = li.LowPart % 10;
  721.         li.QuadPart /= 10;
  722.         temp[--pos] = (TCHAR) digit+L'0';
  723.         } while (li.QuadPart);
  724.         wsprintf(m_String, TEXT("%s"), temp+pos);
  725.         break;
  726.     }
  727.     case CDISP_HEX:
  728.     default:
  729.         wsprintf(m_String, TEXT("0x%X%8.8X"), li.HighPart, li.LowPart);
  730.     }
  731. };
  732.  
  733. CDisp::CDisp(REFCLSID clsid)
  734. {
  735.     WCHAR strClass[CHARS_IN_GUID+1];
  736.     StringFromGUID2(clsid, strClass, sizeof(strClass) / sizeof(strClass[0]));
  737.     ASSERT(sizeof(m_String)/sizeof(m_String[0]) >= CHARS_IN_GUID+1);
  738.     wsprintf(m_String, TEXT("%ls"), strClass);
  739. };
  740.  
  741. /*  Display stuff */
  742. CDisp::CDisp(CRefTime llTime)
  743. {
  744.     LPTSTR lpsz = m_String;
  745.     LONGLONG llDiv;
  746.     if (llTime < 0) {
  747.         llTime = -llTime;
  748.         lpsz += wsprintf(lpsz, TEXT("-"));
  749.     }
  750.     llDiv = (LONGLONG)24 * 3600 * 10000000;
  751.     if (llTime >= llDiv) {
  752.         lpsz += wsprintf(lpsz, TEXT("%d days "), (LONG)(llTime / llDiv));
  753.         llTime = llTime % llDiv;
  754.     }
  755.     llDiv = (LONGLONG)3600 * 10000000;
  756.     if (llTime >= llDiv) {
  757.         lpsz += wsprintf(lpsz, TEXT("%d hrs "), (LONG)(llTime / llDiv));
  758.         llTime = llTime % llDiv;
  759.     }
  760.     llDiv = (LONGLONG)60 * 10000000;
  761.     if (llTime >= llDiv) {
  762.         lpsz += wsprintf(lpsz, TEXT("%d mins "), (LONG)(llTime / llDiv));
  763.         llTime = llTime % llDiv;
  764.     }
  765.     wsprintf(lpsz, TEXT("%d.%3.3d sec"),
  766.              (LONG)llTime / 10000000,
  767.              (LONG)((llTime % 10000000) / 10000));
  768. };
  769.  
  770. /*  Display pin */
  771. CDisp::CDisp(IPin *pPin)
  772. {
  773.     PIN_INFO pi;
  774.     TCHAR str[MAX_PIN_NAME];
  775.     if (pPin) {
  776.        pPin->QueryPinInfo(&pi);
  777.        QueryPinInfoReleaseFilter(pi);
  778.       #ifndef UNICODE
  779.        WideCharToMultiByte(GetACP(), 0, pi.achName, wcslen(pi.achName) + 1,
  780.                            str, MAX_PIN_NAME, NULL, NULL);
  781.       #else
  782.        lstrcpy(str, pi.achName);
  783.       #endif
  784.     } else {
  785.        lstrcpy(str, TEXT("NULL IPin"));
  786.     }
  787.  
  788.     m_pString = (PTCHAR) new TCHAR[lstrlen(str)+64];
  789.     if (!m_pString) {
  790.         return;
  791.     }
  792.  
  793.     CLSID clsid;
  794.     pi.pFilter->GetClassID(&clsid);
  795.     wsprintf(m_pString, TEXT("%s(%s)"), GuidNames[clsid], str);
  796. }
  797.  
  798. CDisp::~CDisp()
  799. {
  800. }
  801.  
  802. CDispBasic::~CDispBasic()
  803. {
  804.     if (m_pString != m_String) {
  805.     delete [] m_pString;
  806.     }
  807. }
  808.  
  809. CDisp::CDisp(double d)
  810. {
  811.     _stprintf(m_String, TEXT("%f"), d);
  812. }
  813.  
  814.  
  815. /* If built for debug this will display the media type details. We convert the
  816.    major and subtypes into strings and also ask the base classes for a string
  817.    description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit
  818.    We also display the fields in the BITMAPINFOHEADER structure, this should
  819.    succeed as we do not accept input types unless the format is big enough */
  820.  
  821. #ifdef DEBUG
  822. void DisplayType(LPSTR label, const AM_MEDIA_TYPE *pmtIn)
  823. {
  824.  
  825.     /* Dump the GUID types and a short description */
  826.  
  827.     DbgLog((LOG_TRACE,5,TEXT("")));
  828.     DbgLog((LOG_TRACE,2,TEXT("%hs  M type %s  S type %s"), label,
  829.         GuidNames[pmtIn->majortype],
  830.         GuidNames[pmtIn->subtype]));
  831.     DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype)));
  832.  
  833.     /* Dump the generic media types */
  834.  
  835.     if (pmtIn->bTemporalCompression) {
  836.     DbgLog((LOG_TRACE,5,TEXT("Temporally compressed")));
  837.     } else {
  838.     DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed")));
  839.     }
  840.  
  841.     if (pmtIn->bFixedSizeSamples) {
  842.     DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize));
  843.     } else {
  844.     DbgLog((LOG_TRACE,5,TEXT("Variable size samples")));
  845.     }
  846.  
  847.     if (pmtIn->formattype == FORMAT_VideoInfo) {
  848.     /* Dump the contents of the BITMAPINFOHEADER structure */
  849.     BITMAPINFOHEADER *pbmi = HEADER(pmtIn->pbFormat);
  850.     VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmtIn->pbFormat;
  851.  
  852.     DbgLog((LOG_TRACE,5,TEXT("Source rectangle (Left %d Top %d Right %d Bottom %d)"),
  853.            pVideoInfo->rcSource.left,
  854.            pVideoInfo->rcSource.top,
  855.            pVideoInfo->rcSource.right,
  856.            pVideoInfo->rcSource.bottom));
  857.  
  858.     DbgLog((LOG_TRACE,5,TEXT("Target rectangle (Left %d Top %d Right %d Bottom %d)"),
  859.            pVideoInfo->rcTarget.left,
  860.            pVideoInfo->rcTarget.top,
  861.            pVideoInfo->rcTarget.right,
  862.            pVideoInfo->rcTarget.bottom));
  863.  
  864.     DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize));
  865.     if (pbmi->biCompression < 256) {
  866.         DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit  (%d)"),
  867.             pbmi->biWidth, pbmi->biHeight,
  868.             pbmi->biBitCount, pbmi->biCompression));
  869.     } else {
  870.         DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"),
  871.             pbmi->biWidth, pbmi->biHeight,
  872.             pbmi->biBitCount, &pbmi->biCompression));
  873.     }
  874.  
  875.     DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage));
  876.     DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes));
  877.     DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter));
  878.     DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter));
  879.     DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed));
  880.  
  881.     } else if (pmtIn->majortype == MEDIATYPE_Audio) {
  882.     DbgLog((LOG_TRACE,2,TEXT("     Format type %s"),
  883.         GuidNames[pmtIn->formattype]));
  884.     DbgLog((LOG_TRACE,2,TEXT("     Subtype %s"),
  885.         GuidNames[pmtIn->subtype]));
  886.  
  887.     if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet)
  888.       && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT)))
  889.     {
  890.         /* Dump the contents of the WAVEFORMATEX type-specific format structure */
  891.  
  892.         WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat;
  893.             DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));
  894.             DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));
  895.             DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));
  896.             DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));
  897.             DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));
  898.             DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));
  899.  
  900.             /* PCM uses a WAVEFORMAT and does not have the extra size field */
  901.  
  902.             if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) {
  903.                 DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));
  904.             }
  905.     } else {
  906.     }
  907.  
  908.     } else {
  909.     DbgLog((LOG_TRACE,2,TEXT("     Format type %s"),
  910.         GuidNames[pmtIn->formattype]));
  911.     // !!!! should add code to dump wave format, others
  912.     }
  913. }
  914. #endif
  915.  
  916.