debug.cpp
上传用户:xhy777
上传日期:2007-02-14
资源大小:24088k
文件大小:19k
源码类别:

系统编程

开发平台:

Visual C++

  1. /*----------------------------------------------------------------------------
  2. / Title;
  3. /   debug.cpp
  4. /
  5. / Authors;
  6. /   Jeffrey Saathoff (jeffreys)
  7. /
  8. / Notes;
  9. /   Provides printf style debug output
  10. /----------------------------------------------------------------------------*/
  11. #include "stdafx.h"
  12. #include <stdio.h>
  13. #include <comctrlp.h>
  14. #pragma hdrstop
  15. // Define some things for debug.h
  16. //
  17. #define SZ_DEBUGINI         "ccshell.ini"
  18. #define SZ_DEBUGSECTION     "netplwiz"
  19. #define SZ_MODULE           "NETPLWIZ"
  20. #define DECLARE_DEBUG
  21. #include "debug.h"
  22. #ifdef DEBUG
  23. DWORD g_dwTraceMask = 0;
  24. DWORD g_tlsDebug = 0xffffffffL;
  25. #define MAX_CALL_DEPTH  64
  26. #define BUFFER_SIZE     2048
  27. class CDebugStack
  28. {
  29. private:
  30.     DWORD m_dwThreadID;
  31.     LONG m_cDepth;
  32.     struct
  33.     {
  34.         BOOL    fTracedYet;
  35.         LPCTSTR pszFunctionName;
  36.         DWORD   dwMask;
  37.     }
  38.     m_CallStack[MAX_CALL_DEPTH];
  39.     TCHAR m_szStringBuffer[BUFFER_SIZE];
  40. public:
  41.     CDebugStack() : m_dwThreadID(GetCurrentThreadId()), m_cDepth(-1)
  42.     { ZeroMemory(&m_CallStack, SIZEOF(m_CallStack)); }
  43. public:
  44.     void _Indent(LONG iDepth, LPCTSTR pszFormat, ...);
  45.     void _vIndent(LONG iDepth, LPCTSTR pszFormat, va_list va);
  46.     BOOL _TraceProlog(LONG iDepth, BOOL fForce);
  47.     void _TraceEnter(DWORD dwMask, LPCTSTR pName);
  48.     void _TraceLeave(void);
  49.     void _Trace(BOOL bForce, LPCTSTR pszFormat, ...);
  50.     void _vTrace(BOOL bForce, LPCTSTR pszFormat, va_list va);
  51.     void _TraceGUID(LPCTSTR pPrefix, REFGUID rGUID);
  52.     void _TraceAssert(int iLine, LPTSTR pFilename);
  53. };
  54. typedef CDebugStack *PDEBUGSTACK;
  55. class CDebugStackHolder
  56. {
  57. private:
  58.     HDPA m_hDebugStackList;
  59.     CRITICAL_SECTION m_csStackList;
  60. public:
  61.     CDebugStackHolder() : m_hDebugStackList(NULL) { InitializeCriticalSection(&m_csStackList); }
  62.     ~CDebugStackHolder();
  63. public:
  64.     void Add(PDEBUGSTACK pDebugStack);
  65.     void Remove(PDEBUGSTACK pDebugStack);
  66. };
  67. typedef CDebugStackHolder *PDEBUGSTACKHOLDER;
  68. PDEBUGSTACKHOLDER g_pStackHolder = NULL;
  69. /*-----------------------------------------------------------------------------
  70. / _Indent
  71. / -------
  72. /   Output to the debug stream indented by n columns.
  73. /
  74. / In:
  75. /   i = column to indent to.
  76. /   pszFormat -> string to be indented
  77. /
  78. / Out:
  79. /   -
  80. /----------------------------------------------------------------------------*/
  81. void CDebugStack::_Indent(LONG iDepth, LPCTSTR pszFormat, ...)
  82. {
  83.     va_list va;
  84.     va_start(va, pszFormat);
  85.     _vIndent(iDepth, pszFormat, va);
  86.     va_end(va);
  87. }
  88. void CDebugStack::_vIndent(LONG iDepth, LPCTSTR pszFormat, va_list va)
  89. {
  90.     m_szStringBuffer[0] = TEXT('');
  91.     wsprintf(m_szStringBuffer, TEXT("%08x "), m_dwThreadID);
  92.     iDepth = min(iDepth, MAX_CALL_DEPTH - 1);
  93.     for ( ; iDepth > 0 ; iDepth-- )
  94.         lstrcat(m_szStringBuffer, TEXT("  "));
  95.     wvsprintf(m_szStringBuffer + lstrlen(m_szStringBuffer), pszFormat, va);
  96.     lstrcat(m_szStringBuffer, TEXT("n"));
  97.     OutputDebugString(m_szStringBuffer);
  98. }
  99. /*-----------------------------------------------------------------------------
  100. / _TraceProlog
  101. / -------------
  102. /   Handle the prolog to a prefix string, including outputting the
  103. /   function name if we haven't already.
  104. /
  105. / In:
  106. /   iDepth = depth in the call stack
  107. /   fForce = ignore flags
  108. /
  109. / Out:
  110. /   BOOL if trace output should be made
  111. /----------------------------------------------------------------------------*/
  112. BOOL CDebugStack::_TraceProlog(LONG iDepth, BOOL fForce)
  113. {
  114.     if ( iDepth < 0 || iDepth >= MAX_CALL_DEPTH )
  115.         return FALSE;
  116.     if  ( (g_dwTraceMask & m_CallStack[iDepth].dwMask) || fForce )
  117.     {
  118.         if ( !m_CallStack[iDepth].fTracedYet )
  119.         {
  120.             if ( iDepth > 0 )
  121.                 _TraceProlog(iDepth-1, TRUE);
  122.             _Indent(iDepth, m_CallStack[iDepth].pszFunctionName);
  123.             m_CallStack[iDepth].fTracedYet = TRUE;
  124.         }
  125.         return TRUE;
  126.     }
  127.     return FALSE;
  128. }
  129. /*-----------------------------------------------------------------------------
  130. / _TraceEnter
  131. / ------------
  132. /   Set the debugging call stack up to indicate which function we are in.
  133. /
  134. / In:
  135. /   pName -> function name to be displayed in subsequent trace output.
  136. /
  137. / Out:
  138. /   -
  139. /----------------------------------------------------------------------------*/
  140. void CDebugStack::_TraceEnter(DWORD dwMask, LPCTSTR pName)
  141. {
  142.     m_cDepth++;
  143.     if ( m_cDepth < MAX_CALL_DEPTH )
  144.     {
  145.         if ( !pName )    
  146.             pName = TEXT("<no name>");         // no function name given
  147.         m_CallStack[m_cDepth].fTracedYet = FALSE;
  148.         m_CallStack[m_cDepth].pszFunctionName = pName;
  149.         m_CallStack[m_cDepth].dwMask = dwMask;
  150.         //if ( m_cDepth > 0 )
  151.         //    _TraceProlog(m_cDepth-1, FALSE);
  152.     }
  153. }
  154. /*-----------------------------------------------------------------------------
  155. / _TraceLeave
  156. / ------------
  157. /   On exit from a function this will adjust the function stack pointer to 
  158. /   point to our previous function.  If no trace output has been made then 
  159. /   we will output the function name on a single line (to indicate we went somewhere).
  160. /
  161. / In:
  162. /    -
  163. / Out:
  164. /   -
  165. /----------------------------------------------------------------------------*/
  166. void CDebugStack::_TraceLeave(void)
  167. {
  168.     //_TraceProlog(m_cDepth, FALSE);
  169.     //if ( !m_cDepth && m_CallStack[0].fTracedYet )
  170.     //    OutputDebugString(TEXT("n"));
  171.     
  172.     m_cDepth = max(m_cDepth-1, -1);         // account for underflow
  173. }
  174. /*-----------------------------------------------------------------------------
  175. / _Trace
  176. / -------
  177. /   Perform printf formatting to the debugging stream.  We indent the output
  178. /   and stream the function name as required to give some indication of 
  179. /   call stack depth.
  180. /
  181. / In:
  182. /   pszFormat -> printf style formatting string
  183. /   ... = arguments as required for the formatting
  184. /
  185. / Out:
  186. /   -
  187. /----------------------------------------------------------------------------*/
  188. void CDebugStack::_Trace(BOOL bForce, LPCTSTR pszFormat, ...)
  189. {
  190.     va_list va;
  191.     va_start(va, pszFormat);
  192.     _vTrace(bForce, pszFormat, va);
  193.     va_end(va);
  194. }
  195. void CDebugStack::_vTrace(BOOL bForce, LPCTSTR pszFormat, va_list va)
  196. {
  197.     if ( _TraceProlog(m_cDepth, bForce) || bForce )
  198.         _vIndent(m_cDepth+1, pszFormat, va);
  199. }
  200. /*-----------------------------------------------------------------------------
  201. / _TraceGUID
  202. / -----------
  203. /   Given a GUID output it into the debug string, first we try and map it
  204. /   to a name (ie. IShellFolder), if that didn't work then we convert it
  205. /   to its human readable form.
  206. /
  207. / In:
  208. /   pszPrefix -> prefix string
  209. /   lpGuid -> guid to be streamed   
  210. /
  211. / Out:
  212. /   -
  213. /----------------------------------------------------------------------------*/
  214. #ifdef UNICODE
  215. #define MAP_GUID(x)     &x, TEXT(""L#x)
  216. #else
  217. #define MAP_GUID(x)     &x, TEXT(""#x)
  218. #endif
  219. #define MAP_GUID2(x,y)  MAP_GUID(x), MAP_GUID(y)
  220. const struct 
  221. {
  222.     const GUID* m_pGUID;
  223.     LPCTSTR     m_pName;
  224. }
  225. _guid_map[] = 
  226. {
  227.     MAP_GUID(IID_IUnknown),
  228.     MAP_GUID(IID_IClassFactory),
  229.     MAP_GUID(IID_IDropTarget),
  230.     MAP_GUID(IID_IDataObject),
  231.     MAP_GUID(IID_IPersist),
  232.     MAP_GUID(IID_IOleWindow),
  233.     MAP_GUID2(IID_INewShortcutHookA, IID_INewShortcutHookW),
  234.     MAP_GUID(IID_IShellBrowser),
  235.     MAP_GUID(IID_IShellView),
  236.     MAP_GUID(IID_IContextMenu),
  237.     MAP_GUID(IID_IShellIcon),
  238.     MAP_GUID(IID_IShellFolder),
  239.     MAP_GUID(IID_IShellExtInit),
  240.     MAP_GUID(IID_IShellPropSheetExt),
  241.     MAP_GUID(IID_IPersistFolder),  
  242.     MAP_GUID2(IID_IExtractIconA, IID_IExtractIconW),
  243.     MAP_GUID2(IID_IShellLinkA, IID_IShellLinkW),
  244.     MAP_GUID2(IID_IShellCopyHookA, IID_IShellCopyHookW),
  245.     MAP_GUID2(IID_IFileViewerA, IID_IFileViewerW),
  246.     MAP_GUID(IID_ICommDlgBrowser),
  247.     MAP_GUID(IID_IEnumIDList),
  248.     MAP_GUID(IID_IFileViewerSite),
  249.     MAP_GUID(IID_IContextMenu2),
  250.     MAP_GUID2(IID_IShellExecuteHookA, IID_IShellExecuteHookW),
  251.     MAP_GUID(IID_IPropSheetPage),
  252.     MAP_GUID(IID_IShellView2),
  253.     MAP_GUID(IID_IUniformResourceLocator),
  254. };
  255. void CDebugStack::_TraceGUID(LPCTSTR pPrefix, REFGUID rGUID)
  256. {
  257.     TCHAR szGUID[40];
  258.     LPCTSTR pName = NULL;
  259.     int i;
  260.     
  261.     for ( i = 0 ; i < ARRAYSIZE(_guid_map); i++ )
  262.     {
  263.         if ( IsEqualGUID(rGUID, *_guid_map[i].m_pGUID) )
  264.         {
  265.             pName = _guid_map[i].m_pName;
  266.             break;
  267.         }
  268.     }
  269.     if ( !pName )
  270.     {
  271. // StringFromGUID2 only does UNICODE.  SHStringFromGUID goes both ways,
  272. // but requires shlwapip.h and shlwapi.lib.
  273. #ifndef UNICODE
  274.   #error "_TraceGUID needs fixing"
  275. #endif    
  276.         StringFromGUID2(rGUID, szGUID, ARRAYSIZE(szGUID));
  277.         //SHStringFromGUID(rGUID, szGUID, ARRAYSIZE(szGUID));
  278.         pName = szGUID;
  279.     }
  280.     _Trace(FALSE, TEXT("%s %s"), pPrefix, pName);
  281. }
  282. /*-----------------------------------------------------------------------------
  283. / _TraceAssert
  284. / -------------
  285. /   Our assert handler, out faults it the trace mask as enabled assert
  286. /   faulting.
  287. /
  288. / In:
  289. /   iLine = line 
  290. /   pFilename -> filename of the file we asserted in
  291. /
  292. / Out:
  293. /   -
  294. /----------------------------------------------------------------------------*/
  295. void CDebugStack::_TraceAssert(int iLine, LPTSTR pFilename)
  296. {
  297.     // nb: TRUE --> asserts always displayed
  298.     _Trace(TRUE, TEXT("Assert failed in %s, line %d"), pFilename, iLine);
  299.     if ( g_dwTraceMask & TRACE_COMMON_ASSERT )
  300.         DebugBreak();
  301. }
  302. /*-----------------------------------------------------------------------------
  303. / ~CDebugStackHolder
  304. / ------------------
  305. /   Free any DebugStack objects that exist
  306. /
  307. / In:
  308. /   -
  309. /
  310. / Out:
  311. /   -
  312. /----------------------------------------------------------------------------*/
  313. int CALLBACK
  314. _DeleteCB(LPVOID pVoid, LPVOID /*pData*/)
  315. {
  316.     PDEBUGSTACK pDebugStack = (PDEBUGSTACK)pVoid;
  317.     if (pDebugStack)
  318.     {
  319.         //pDebugStack->_Trace(TRUE, TEXT("~CDebugStackHolder destroying DebugStack"));
  320.         delete pDebugStack;
  321.     }
  322.     return 1;
  323. }
  324. CDebugStackHolder::~CDebugStackHolder()
  325. {
  326.     EnterCriticalSection(&m_csStackList);
  327.     if (NULL != m_hDebugStackList)
  328.     {
  329.         DPA_DestroyCallback(m_hDebugStackList, _DeleteCB, NULL);
  330.         m_hDebugStackList = NULL;
  331.     }
  332.     LeaveCriticalSection(&m_csStackList);
  333.     DeleteCriticalSection(&m_csStackList);
  334. }
  335. /*-----------------------------------------------------------------------------
  336. / CDebugStackHolder::Add
  337. / ----------------------
  338. /   Saves the DebugStack object in a list
  339. /
  340. / In:
  341. /   PDEBUGSTACK pointer to the thread's debug stack object
  342. /
  343. / Out:
  344. /   -
  345. /----------------------------------------------------------------------------*/
  346. void
  347. CDebugStackHolder::Add(PDEBUGSTACK pDebugStack)
  348. {
  349.     EnterCriticalSection(&m_csStackList);
  350.     if (NULL == m_hDebugStackList)
  351.         m_hDebugStackList = DPA_Create(4);
  352.     if (NULL != m_hDebugStackList)
  353.         DPA_AppendPtr(m_hDebugStackList, pDebugStack);
  354.     LeaveCriticalSection(&m_csStackList);
  355. }
  356. /*-----------------------------------------------------------------------------
  357. / CDebugStackHolder::Remove
  358. / -------------------------
  359. /   Removes the DebugStack object from the list
  360. /
  361. / In:
  362. /   PDEBUGSTACK pointer to the thread's debug stack object
  363. /
  364. / Out:
  365. /   -
  366. /----------------------------------------------------------------------------*/
  367. void
  368. CDebugStackHolder::Remove(PDEBUGSTACK pDebugStack)
  369. {
  370.     EnterCriticalSection(&m_csStackList);
  371.     if (NULL != m_hDebugStackList)
  372.     {
  373.         int iStack = DPA_GetPtrIndex(m_hDebugStackList, pDebugStack);
  374.         if (-1 != iStack)
  375.             DPA_DeletePtr(m_hDebugStackList, iStack);
  376.     }
  377.     LeaveCriticalSection(&m_csStackList);
  378. }
  379. /*-----------------------------------------------------------------------------
  380. / GetThreadStack
  381. / --------------
  382. /   Create (if necessary) and return the per-thread debug stack object.
  383. /
  384. / In:
  385. /   -
  386. /
  387. / Out:
  388. /   PDEBUGSTACK pointer to the thread's debug stack object
  389. /----------------------------------------------------------------------------*/
  390. PDEBUGSTACK GetThreadStack()
  391. {
  392.     PDEBUGSTACK pDebugStack;
  393.     if (0xffffffffL == g_tlsDebug)
  394.         return NULL;
  395.     pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
  396.     if (!pDebugStack)
  397.     {
  398.         pDebugStack = new CDebugStack;
  399.         TlsSetValue(g_tlsDebug, pDebugStack);
  400.         if (!g_pStackHolder)
  401.             g_pStackHolder = new CDebugStackHolder;
  402.         if (g_pStackHolder)
  403.             g_pStackHolder->Add(pDebugStack);
  404.     }
  405.     return pDebugStack;
  406. }
  407.     
  408. /*-----------------------------------------------------------------------------
  409. / DoTraceSetMask
  410. / --------------
  411. /   Adjust the trace mask to reflect the state given.
  412. /
  413. / In:
  414. /   dwMask = mask for enabling / disable trace output
  415. /
  416. / Out:
  417. /   -
  418. /----------------------------------------------------------------------------*/
  419. void DoTraceSetMask(DWORD dwMask)
  420. {
  421.     g_dwTraceMask = dwMask;
  422. }
  423. /*-----------------------------------------------------------------------------
  424. / DoTraceSetMaskFromRegKey
  425. / ------------------------
  426. /   Pick up the TraceMask value from the given registry key and
  427. /   set the trace mask using that.
  428. /
  429. / In:
  430. /   hkRoot = handle of open key
  431. /   pszSubKey = name of subkey to open
  432. /
  433. / Out:
  434. /   -
  435. /----------------------------------------------------------------------------*/
  436. void DoTraceSetMaskFromRegKey(HKEY hkRoot, LPCTSTR pszSubKey)
  437. {
  438.     HKEY hKey;
  439.     if (ERROR_SUCCESS == RegOpenKey(hkRoot, pszSubKey, &hKey))
  440.     {
  441.         DWORD dwTraceMask = 0;
  442.         DWORD cbTraceMask = SIZEOF(dwTraceMask);
  443.         RegQueryValueEx(hKey,
  444.                         TEXT("TraceMask"),
  445.                         NULL,
  446.                         NULL,
  447.                         (LPBYTE)&dwTraceMask,
  448.                         &cbTraceMask);
  449.         DoTraceSetMask(dwTraceMask);
  450.         RegCloseKey(hKey);
  451.     }
  452. }
  453. /*-----------------------------------------------------------------------------
  454. / DoTraceSetMaskFromCLSID
  455. / -----------------------
  456. /   Pick up the TraceMask value from the given CLSID value and
  457. /   set the trace mask using that.
  458. /
  459. / In:
  460. /   rCLSID = CLSID to query the value from
  461. /
  462. / Out:
  463. /   -
  464. /----------------------------------------------------------------------------*/
  465. void DoTraceSetMaskFromCLSID(REFCLSID rCLSID)
  466. {
  467.     TCHAR szClsidKey[48] = TEXT("CLSID\");
  468.     int nLength = lstrlen(szClsidKey);
  469. // StringFromGUID2 only does UNICODE.  SHStringFromGUID goes both ways,
  470. // but requires shlwapip.h and shlwapi.lib.
  471. #ifdef UNICODE
  472.     if (0 == StringFromGUID2(rCLSID, szClsidKey + nLength, ARRAYSIZE(szClsidKey) - nLength))
  473. #else
  474. #error "DoTraceSetMaskFromCLSID needs fixing"
  475.     if (0 == SHStringFromGUID(rCLSID, szClsidKey + nLength, ARRAYSIZE(szClsidKey) - nLength))
  476. #endif    
  477.         return;
  478.     DoTraceSetMaskFromRegKey(HKEY_CLASSES_ROOT, szClsidKey);
  479. }
  480. /*-----------------------------------------------------------------------------
  481. / DoTraceEnter
  482. / ------------
  483. /   Set the debugging call stack up to indicate which function we are in.
  484. /
  485. / In:
  486. /   pName -> function name to be displayed in subsequent trace output.
  487. /
  488. / Out:
  489. /   -
  490. /----------------------------------------------------------------------------*/
  491. void DoTraceEnter(DWORD dwMask, LPCTSTR pName)
  492. {
  493.     PDEBUGSTACK pDebugStack = GetThreadStack();
  494.     if (pDebugStack)
  495.         pDebugStack->_TraceEnter(dwMask, pName);
  496. }
  497. /*-----------------------------------------------------------------------------
  498. / DoTraceLeave
  499. / ------------
  500. /   On exit from a function this will adjust the function stack pointer to 
  501. /   point to our previous function.  If no trace output has been made then 
  502. /   we will output the function name on a single line (to indicate we went somewhere).
  503. /
  504. / In:
  505. /    -
  506. / Out:
  507. /   -
  508. /----------------------------------------------------------------------------*/
  509. void DoTraceLeave(void)
  510. {
  511.     PDEBUGSTACK pDebugStack = GetThreadStack();
  512.     if (pDebugStack)
  513.         pDebugStack->_TraceLeave();
  514. }
  515. /*-----------------------------------------------------------------------------
  516. / DoTrace
  517. / -------
  518. /   Perform printf formatting to the debugging stream.  We indent the output
  519. /   and stream the function name as required to give some indication of 
  520. /   call stack depth.
  521. /
  522. / In:
  523. /   pszFormat -> printf style formatting string
  524. /   ... = arguments as required for the formatting
  525. /
  526. / Out:
  527. /   -
  528. /----------------------------------------------------------------------------*/
  529. void DoTrace(LPCTSTR pszFormat, ...)
  530. {
  531.     PDEBUGSTACK pDebugStack = GetThreadStack();
  532.     va_list va;
  533.     if (pDebugStack)
  534.     {
  535.         va_start(va, pszFormat);
  536.         pDebugStack->_vTrace(FALSE, pszFormat, va);
  537.         va_end(va);
  538.     }
  539. }
  540. /*-----------------------------------------------------------------------------
  541. / DoTraceGuid
  542. / -----------
  543. /   Given a GUID output it into the debug string, first we try and map it
  544. /   to a name (ie. IShellFolder), if that didn't work then we convert it
  545. /   to its human readable form.
  546. /
  547. / In:
  548. /   pszPrefix -> prefix string
  549. /   lpGuid -> guid to be streamed   
  550. /
  551. / Out:
  552. /   -
  553. /----------------------------------------------------------------------------*/
  554. void DoTraceGUID(LPCTSTR pPrefix, REFGUID rGUID)
  555. {
  556.     PDEBUGSTACK pDebugStack = GetThreadStack();
  557.     if (pDebugStack)
  558.         pDebugStack->_TraceGUID(pPrefix, rGUID);
  559. }
  560. /*-----------------------------------------------------------------------------
  561. / DoTraceAssert
  562. / -------------
  563. /   Our assert handler, out faults it the trace mask as enabled assert
  564. /   faulting.
  565. /
  566. / In:
  567. /   iLine = line 
  568. /   pFilename -> filename of the file we asserted in
  569. /
  570. / Out:
  571. /   -
  572. /----------------------------------------------------------------------------*/
  573. void DoTraceAssert(int iLine, LPTSTR pFilename)
  574. {
  575.     PDEBUGSTACK pDebugStack = GetThreadStack();
  576.     if (pDebugStack)
  577.         pDebugStack->_TraceAssert(iLine, pFilename);
  578. }
  579. /*-----------------------------------------------------------------------------
  580. / DebugThreadDetach
  581. / DebugProcessAttach
  582. / DebugProcessDetach
  583. / -------------
  584. /   These must be called from DllMain
  585. /
  586. / In:
  587. /   -
  588. /
  589. / Out:
  590. /   -
  591. /----------------------------------------------------------------------------*/
  592. void DebugThreadDetach(void)
  593. {
  594.     PDEBUGSTACK pDebugStack;
  595.     if (0xffffffffL == g_tlsDebug)
  596.         return;
  597.     pDebugStack = (PDEBUGSTACK)TlsGetValue(g_tlsDebug);
  598.     if (pDebugStack)
  599.     {
  600.         if (g_pStackHolder)
  601.             g_pStackHolder->Remove(pDebugStack);
  602.         delete pDebugStack;
  603.         TlsSetValue(g_tlsDebug, NULL);
  604.     }
  605. }
  606. void DebugProcessAttach(void)
  607. {
  608.     g_tlsDebug = TlsAlloc();
  609. }
  610. void DebugProcessDetach(void)
  611. {
  612.     DebugThreadDetach();
  613.     if (NULL != g_pStackHolder)
  614.     {
  615.         delete g_pStackHolder;
  616.         g_pStackHolder = NULL;
  617.     }
  618.     if (0xffffffffL != g_tlsDebug)
  619.     {
  620.         TlsFree(g_tlsDebug);
  621.         g_tlsDebug = 0xffffffffL;
  622.     }
  623. }
  624. #endif  // DEBUG