debug.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 34k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /////////////////////////////////////////////////////////////////////////////
  2. // DEBUG.CPP
  3. //
  4. // Debugging functions.
  5. //
  6. // History:
  7. //
  8. // Author   Date        Description
  9. // ------   ----        -----------
  10. // jaym     04/26/97    Stolen from ntprivateshelllib
  11. // jaym     04/26/97    Removed try/except
  12. /////////////////////////////////////////////////////////////////////////////
  13. #include "precomp.h"
  14. // Define some things for debug.h
  15. //
  16. #define SZ_DEBUGINI     "actsaver.ini"
  17. #define SZ_DEBUGSECTION "actsaver"
  18. #define SZ_MODULE       "ACTSAVER"
  19. #define DECLARE_DEBUG
  20. #include <debug.h>
  21. #ifdef DEBUG
  22. DWORD g_dwDumpFlags     = 0;        // DF_*
  23. #ifdef FULL_DEBUG
  24. DWORD g_dwBreakFlags    = BF_ONVALIDATE;        // BF_*
  25. #else
  26. DWORD g_dwBreakFlags    = 0;        // BF_*
  27. #endif
  28. DWORD g_dwTraceFlags    = 0;        // TF_*
  29. DWORD g_dwPrototype     = 0;        
  30. DWORD g_dwFuncTraceFlags = 0;       // FTF_*
  31. // TLS slot used to store depth for CcshellFuncMsg indentation
  32. static DWORD g_tlsStackDepth = TLS_OUT_OF_INDEXES;
  33. // Hack stack depth counter used when g_tlsStackDepth is not available
  34. static DWORD g_dwHackStackDepth = 0;
  35. static char g_szIndentLeader[] = "                                                                                ";
  36. static WCHAR g_wszIndentLeader[] = L"                                                                                ";
  37. #pragma data_seg(DATASEG_READONLY)
  38. static CHAR const FAR c_szNewline[] = "rn";   // (Deliberately CHAR)
  39. static WCHAR const FAR c_wszNewline[] = TEXTW("rn");
  40. #pragma data_seg()
  41. extern CHAR const FAR c_szTrace[];              // (Deliberately CHAR)
  42. extern CHAR const FAR c_szErrorDbg[];           // (Deliberately CHAR)
  43. extern CHAR const FAR c_szWarningDbg[];         // (Deliberately CHAR)
  44. extern WCHAR const FAR c_wszTrace[];
  45. extern WCHAR const FAR c_wszErrorDbg[]; 
  46. extern WCHAR const FAR c_wszWarningDbg[];
  47. extern const CHAR  FAR c_szAssertMsg[];
  48. extern CHAR const FAR c_szAssertFailed[];
  49. extern const WCHAR  FAR c_wszAssertMsg[];
  50. extern WCHAR const FAR c_wszAssertFailed[];
  51. void
  52. SetPrefixStringA(
  53.     OUT LPSTR pszBuf,
  54.     IN  DWORD dwFlags)
  55. {
  56.     if (TF_ALWAYS == dwFlags)
  57.         lstrcpyA(pszBuf, c_szTrace);
  58.     else if (IsFlagSet(dwFlags, TF_WARNING))
  59.         lstrcpyA(pszBuf, c_szWarningDbg);
  60.     else if (IsFlagSet(dwFlags, TF_ERROR))
  61.         lstrcpyA(pszBuf, c_szErrorDbg);
  62.     else
  63.         lstrcpyA(pszBuf, c_szTrace);
  64. }
  65. void
  66. SetPrefixStringW(
  67.     OUT LPWSTR pszBuf,
  68.     IN  DWORD  dwFlags)
  69. {
  70.     if (TF_ALWAYS == dwFlags)
  71.         lstrcpyW(pszBuf, c_wszTrace);
  72.     else if (IsFlagSet(dwFlags, TF_WARNING))
  73.         lstrcpyW(pszBuf, c_wszWarningDbg);
  74.     else if (IsFlagSet(dwFlags, TF_ERROR))
  75.         lstrcpyW(pszBuf, c_wszErrorDbg);
  76.     else
  77.         lstrcpyW(pszBuf, c_wszTrace);
  78. }
  79. // Hack!  The MSDEV debugger has some smarts where if it sees
  80. // an ASSERT (all caps) in the source, and there is a debug break,
  81. // then it sticks up a sorta friendly assert message box.
  82. // For the debug function below where the break occurs inside,
  83. // we add a nop ASSERT line in here to fake MSDEV to give us
  84. // a friendly message box.
  85. #undef ASSERT
  86. #define ASSERT(f)   DEBUG_BREAK
  87. //
  88. //  Smart debug functions
  89. //
  90. /*----------------------------------------------------------
  91. Purpose: Displays assertion string.
  92. Returns: TRUE to debugbreak
  93. Cond:    --
  94. */
  95. BOOL
  96. CcshellAssertFailedA(
  97.     LPCSTR pszFile,
  98.     int line,
  99.     LPCSTR pszEval,
  100.     BOOL bBreakInside)
  101. {
  102.     BOOL bRet = FALSE;
  103.     LPCSTR psz;
  104.     CHAR ach[256];
  105.     // Strip off path info from filename string, if present.
  106.     //
  107.     for (psz = pszFile + lstrlenA(pszFile); psz != pszFile; psz=CharPrevA(pszFile, psz))
  108.     {
  109.         if ((CharPrevA(pszFile, psz)!= (psz-2)) && *(psz - 1) == '\')
  110.             break;
  111.     }
  112.     wsprintfA(ach, c_szAssertFailed, psz, line, pszEval);
  113.     OutputDebugStringA(ach);
  114.     if (IsFlagSet(g_dwBreakFlags, BF_ONVALIDATE))
  115.     {
  116.         if (bBreakInside)
  117.         {
  118.             // See the hack we have above about redefining ASSERT
  119.             ASSERT(0);
  120.         }
  121.         else
  122.             bRet = TRUE;
  123.     }
  124.     return bRet;
  125. }
  126. /*----------------------------------------------------------
  127. Purpose: Displays assertion string.
  128. Returns: --
  129. Cond:    --
  130. */
  131. BOOL
  132. CcshellAssertFailedW(
  133.     LPCWSTR pszFile,
  134.     int line,
  135.     LPCWSTR pszEval,
  136.     BOOL bBreakInside)
  137. {
  138.     BOOL bRet = FALSE;
  139.     LPCWSTR psz;
  140.     WCHAR ach[256];
  141.     // Strip off path info from filename string, if present.
  142.     //
  143.     for (psz = pszFile + lstrlenW(pszFile); psz && (psz != pszFile); psz=CharPrevW(pszFile, psz))
  144.     {
  145.         if ((CharPrevW(pszFile, psz)!= (psz-2)) && *(psz - 1) == TEXT('\'))
  146.             break;
  147.     }
  148.     // If psz == NULL, CharPrevW failed which implies we are running on Win95.  We can get this
  149.     // if we get an assert in some of the W functions in shlwapi...  Call the A version of assert...
  150.     if (!psz)
  151.     {
  152.         char szFile[MAX_PATH];
  153.         char szEval[256];   // since the total output is thhis size should be enough...
  154.         WideCharToMultiByte(CP_ACP, 0, pszFile, -1, szFile, ARRAYSIZE(szFile), NULL, NULL);
  155.         WideCharToMultiByte(CP_ACP, 0, pszEval, -1, szEval, ARRAYSIZE(szEval), NULL, NULL);
  156.         return CcshellAssertFailedA(szFile, line, szEval, bBreakInside);
  157.     }
  158.     wsprintfW(ach, c_wszAssertFailed, psz, line, pszEval);
  159.     OutputDebugStringW(ach);
  160.     if (IsFlagSet(g_dwBreakFlags, BF_ONVALIDATE))
  161.     {
  162.         if (bBreakInside)
  163.         {
  164.             // See the hack we have above about redefining ASSERT
  165.             ASSERT(0);
  166.         }
  167.         else
  168.             bRet = TRUE;
  169.     }
  170.     return bRet;
  171. }
  172. /*----------------------------------------------------------
  173. Purpose: Keep track of the stack depth for function call trace
  174.          messages.
  175. Returns: --
  176. Cond:    --
  177. */
  178. void
  179. CcshellStackEnter(void)
  180.     {
  181.     if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  182.         {
  183.         DWORD dwDepth;
  184.         dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  185.         TlsSetValue(g_tlsStackDepth, (LPVOID)(dwDepth + 1));
  186.         }
  187.     else
  188.         {
  189.         g_dwHackStackDepth++;
  190.         }
  191.     }
  192. /*----------------------------------------------------------
  193. Purpose: Keep track of the stack depth for functionc all trace
  194.          messages.
  195. Returns: --
  196. Cond:    --
  197. */
  198. void
  199. CcshellStackLeave(void)
  200.     {
  201.     if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  202.         {
  203.         DWORD dwDepth;
  204.         dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  205.         if (EVAL(0 < dwDepth))
  206.             {
  207.             EVAL(TlsSetValue(g_tlsStackDepth, (LPVOID)(dwDepth - 1)));
  208.             }
  209.         }
  210.     else
  211.         {
  212.         if (EVAL(0 < g_dwHackStackDepth))
  213.             {
  214.             g_dwHackStackDepth--;
  215.             }
  216.         }
  217.     }
  218. /*----------------------------------------------------------
  219. Purpose: Return the stack depth.
  220. Returns: see above
  221. Cond:    --
  222. */
  223. static
  224. DWORD
  225. CcshellGetStackDepth(void)
  226.     {
  227.     DWORD dwDepth;
  228.     if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  229.         {
  230.         dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  231.         }
  232.     else
  233.         {
  234.         dwDepth = g_dwHackStackDepth;
  235.         }
  236.     return dwDepth;
  237.     }
  238. /*----------------------------------------------------------
  239. Purpose: This function converts a multi-byte string to a
  240.          wide-char string.
  241.          If pszBuf is non-NULL and the converted string can fit in
  242.          pszBuf, then *ppszWide will point to the given buffer.
  243.          Otherwise, this function will allocate a buffer that can
  244.          hold the converted string.
  245.          If pszAnsi is NULL, then *ppszWide will be freed.  Note
  246.          that pszBuf must be the same pointer between the call
  247.          that converted the string and the call that frees the
  248.          string.
  249. Returns: TRUE
  250.          FALSE (if out of memory)
  251. Cond:    --
  252. */
  253. BOOL
  254. UnicodeFromAnsi(
  255.     LPWSTR * ppwszWide,
  256.     LPCSTR pszAnsi,           // NULL to clean up
  257.     LPWSTR pwszBuf,
  258.     int cchBuf)
  259.     {
  260.     BOOL bRet;
  261.     // Convert the string?
  262.     if (pszAnsi)
  263.         {
  264.         // Yes; determine the converted string length
  265.         int cch;
  266.         LPWSTR pwsz;
  267.         int cchAnsi = lstrlenA(pszAnsi)+1;
  268.         cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, NULL, 0);
  269.         // String too big, or is there no buffer?
  270.         if (cch > cchBuf || NULL == pwszBuf)
  271.             {
  272.             // Yes; allocate space
  273.             cchBuf = cch + 1;
  274.             pwsz = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cchBuf));
  275.             }
  276.         else
  277.             {
  278.             // No; use the provided buffer
  279.             pwsz = pwszBuf;
  280.             }
  281.         if (pwsz)
  282.             {
  283.             // Convert the string
  284.             cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, pwsz, cchBuf);
  285.             bRet = (0 < cch);
  286.             }
  287.         else
  288.             {
  289.             bRet = FALSE;
  290.             }
  291.         *ppwszWide = pwsz;
  292.         }
  293.     else
  294.         {
  295.         // No; was this buffer allocated?
  296.         if (*ppwszWide && pwszBuf != *ppwszWide)
  297.             {
  298.             // Yes; clean up
  299.             LocalFree((HLOCAL)*ppwszWide);
  300.             *ppwszWide = NULL;
  301.             }
  302.         bRet = TRUE;
  303.         }
  304.     return bRet;
  305.     }
  306. /*----------------------------------------------------------
  307. Purpose: Wide-char version of CcshellAssertMsgA
  308. Returns: --
  309. Cond:    --
  310. */
  311. void
  312. CDECL
  313. CcshellAssertMsgW(
  314.     BOOL f,
  315.     LPCSTR pszMsg, ...)
  316. {
  317.     va_list vArgs;
  318.     if (!f)
  319.     {
  320.         WCHAR * ach = new WCHAR [1024+40];
  321.         if (ach == NULL)
  322.             return;
  323.         lstrcpyW(ach, c_wszAssertMsg);
  324.         int cch = lstrlenW(ach);
  325.         va_start(vArgs, pszMsg);
  326.         // (We convert the string, rather than simply input an
  327.         // LPCWSTR parameter, so the caller doesn't have to wrap
  328.         // all the string constants with the TEXT() macro.)
  329.         LPWSTR  pwsz;
  330.         WCHAR   wszBuf[1024];
  331.         if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  332.         {
  333.             wvsprintfW(&ach[cch], pwsz, vArgs);
  334.             UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  335.         }
  336.         va_end(vArgs);
  337.         OutputDebugStringW(ach);
  338.         OutputDebugStringW(c_wszNewline);
  339.         if (IsFlagSet(g_dwBreakFlags, BF_ONVALIDATE))
  340.             ASSERT(0);
  341.         delete [] ach;
  342.     }
  343. }
  344. /*----------------------------------------------------------
  345. Purpose: Wide-char version of CcshellDebugMsgA.  Note this
  346.          function deliberately takes an ANSI format string
  347.          so our trace messages don't all need to be wrapped
  348.          in TEXT().
  349. Returns: --
  350. Cond:    --
  351. */
  352. void
  353. CDECL
  354. CcshellDebugMsgW(
  355.     DWORD flag,
  356.     LPCSTR pszMsg, ...)         // (this is deliberately CHAR)
  357. {
  358.     va_list vArgs;
  359.     if  (
  360.         (TF_ALWAYS == flag)
  361.         ||
  362.         (IsFlagSet(g_dwTraceFlags, flag) && flag)
  363.         )
  364.     {
  365.         WCHAR * ach = new WCHAR [1024+40];
  366.         if (ach == NULL)
  367.             return;
  368.         SetPrefixStringW(ach, flag);
  369.         int cch = lstrlenW(ach);
  370.         va_start(vArgs, pszMsg);
  371.         // (We convert the string, rather than simply input an
  372.         // LPCWSTR parameter, so the caller doesn't have to wrap
  373.         // all the string constants with the TEXT() macro.)
  374.         LPWSTR  pwsz;
  375.         WCHAR   wszBuf[1024];
  376.         if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  377.         {
  378.             wvsprintfW(&ach[cch], pwsz, vArgs);
  379.             UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  380.         }
  381.         va_end(vArgs);
  382.         OutputDebugStringW(ach);
  383.         OutputDebugStringW(c_wszNewline);
  384.         if (TF_ALWAYS != flag &&
  385.             ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  386.              (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  387.         {
  388.             DEBUG_BREAK;
  389.         }
  390.         delete [] ach;
  391.     }
  392. }
  393. /*----------------------------------------------------------
  394. Purpose: Wide-char version of CcshellFuncMsgA.  Note this
  395.          function deliberately takes an ANSI format string
  396.          so our trace messages don't all need to be wrapped
  397.          in TEXT().
  398. Returns: --
  399. Cond:    --
  400. */
  401. void
  402. CDECL
  403. CcshellFuncMsgW(
  404.     DWORD flag,
  405.     LPCSTR pszMsg, ...)         // (this is deliberately CHAR)
  406. {
  407.     va_list vArgs;
  408.     if  (
  409.         IsFlagSet(g_dwTraceFlags, TF_FUNC)
  410.         &&
  411.         IsFlagSet(g_dwFuncTraceFlags, flag)
  412.         )
  413.     {
  414.         WCHAR * ach = new WCHAR [1024+40];
  415.         if (ach == NULL)
  416.             return;
  417.         // Determine the indentation for trace message based on
  418.         // stack depth.
  419.         LPWSTR  pszLeaderEnd;
  420.         DWORD   dwStackDepth = CcshellGetStackDepth();
  421.         if (dwStackDepth < SIZECHARS(g_szIndentLeader))
  422.             pszLeaderEnd = &g_wszIndentLeader[dwStackDepth];
  423.         else
  424.             pszLeaderEnd = &g_wszIndentLeader[SIZECHARS(g_wszIndentLeader)-1];
  425.         WCHAR chSave = *pszLeaderEnd;
  426.         *pszLeaderEnd = '';
  427.         wsprintfW(ach, L"%s %s", c_wszTrace, g_wszIndentLeader);
  428.         *pszLeaderEnd = chSave;
  429.         // Compose remaining string
  430.         int cch = lstrlenW(ach);
  431.         va_start(vArgs, pszMsg);
  432.         // (We convert the string, rather than simply input an
  433.         // LPCWSTR parameter, so the caller doesn't have to wrap
  434.         // all the string constants with the TEXT() macro.)
  435.         LPWSTR  pwsz;
  436.         WCHAR   wszBuf[1024];
  437.         if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  438.         {
  439.             wvsprintfW(&ach[cch], pwsz, vArgs);
  440.             UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  441.         }
  442.         va_end(vArgs);
  443.         OutputDebugStringW(ach);
  444.         OutputDebugStringW(c_wszNewline);
  445.         delete [] ach;
  446.     }
  447. }
  448. /*----------------------------------------------------------
  449. Purpose: Assert failed message only
  450. Returns: --
  451. Cond:    --
  452. */
  453. void
  454. CDECL
  455. CcshellAssertMsgA(
  456.     BOOL f,
  457.     LPCSTR pszMsg, ...)
  458. {
  459.     va_list vArgs;
  460.     if (!f)
  461.     {
  462.         CHAR * ach = new CHAR [1024+40];
  463.         if (ach == NULL)
  464.             return;
  465.         lstrcpyA(ach, c_szAssertMsg);
  466.         int cch = lstrlenA(ach);
  467.         va_start(vArgs, pszMsg);
  468.         wvsprintfA(&ach[cch], pszMsg, vArgs);
  469.         va_end(vArgs);
  470.         OutputDebugStringA(ach);
  471.         OutputDebugStringA(c_szNewline);
  472.         if (IsFlagSet(g_dwBreakFlags, BF_ONVALIDATE))
  473.             ASSERT(0);
  474.         delete [] ach;
  475.     }
  476. }
  477. /*----------------------------------------------------------
  478. Purpose: Debug spew
  479. Returns: --
  480. Cond:    --
  481. */
  482. void
  483. CDECL
  484. CcshellDebugMsgA(
  485.     DWORD flag,
  486.     LPCSTR pszMsg, ...)
  487. {
  488.     va_list vArgs;
  489.     if  (
  490.         (TF_ALWAYS == flag)
  491.         ||
  492.         (IsFlagSet(g_dwTraceFlags, flag) && flag)
  493.         )
  494.     {
  495.         CHAR * ach = new CHAR [1024+40];
  496.         if (ach == NULL)
  497.             return;
  498.         SetPrefixStringA(ach, flag);
  499.         int cch = lstrlenA(ach);
  500.         va_start(vArgs, pszMsg);
  501.         wvsprintfA(&ach[cch], pszMsg, vArgs);
  502.         va_end(vArgs);
  503.         OutputDebugStringA(ach);
  504.         OutputDebugStringA(c_szNewline);
  505.         if (TF_ALWAYS != flag &&
  506.             ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  507.              (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  508.         {
  509.             DEBUG_BREAK;
  510.         }
  511.         delete [] ach;
  512.     }
  513. }
  514. /*----------------------------------------------------------
  515. Purpose: Debug spew for function trace calls
  516. Returns: --
  517. Cond:    --
  518. */
  519. void
  520. CDECL
  521. CcshellFuncMsgA(
  522.     DWORD flag,
  523.     LPCSTR pszMsg, ...)
  524.     {
  525.     va_list vArgs;
  526.     if  (
  527.         IsFlagSet(g_dwTraceFlags, TF_FUNC)
  528.         &&
  529.         IsFlagSet(g_dwFuncTraceFlags, flag)
  530.         )
  531.     {
  532.         CHAR * ach = new CHAR [1024+40];    // Largest path plus extra
  533.         if (ach == NULL)
  534.             return;
  535.         // Determine the indentation for trace message based on
  536.         // stack depth.
  537.         LPSTR pszLeaderEnd;
  538.         DWORD dwStackDepth = CcshellGetStackDepth();
  539.         if (dwStackDepth < sizeof(g_szIndentLeader))
  540.             pszLeaderEnd = &g_szIndentLeader[dwStackDepth];
  541.         else
  542.             pszLeaderEnd = &g_szIndentLeader[sizeof(g_szIndentLeader)-1];
  543.         CHAR chSave = *pszLeaderEnd;
  544.         *pszLeaderEnd = '';
  545.         wsprintfA(ach, "%s %s", c_szTrace, g_szIndentLeader);
  546.         *pszLeaderEnd = chSave;
  547.         // Compose remaining string
  548.         int cch = lstrlenA(ach);
  549.         va_start(vArgs, pszMsg);
  550.         wvsprintfA(&ach[cch], pszMsg, vArgs);
  551.         va_end(vArgs);
  552.         OutputDebugStringA(ach);
  553.         OutputDebugStringA(c_szNewline);
  554.         delete [] ach;
  555.     }
  556. }
  557. //
  558. //  Debug .ini functions
  559. //
  560. #pragma data_seg(DATASEG_READONLY)
  561. // (c_szCcshellIniFile and c_szCcshellIniSecDebug are declared in debug.h)
  562. extern CHAR const FAR c_szCcshellIniFile[];
  563. extern CHAR const FAR c_szCcshellIniSecDebug[];
  564. // (These are deliberately CHAR)
  565. CHAR const FAR c_szNull[] = "";
  566. CHAR const FAR c_szZero[] = "0";
  567. CHAR const FAR c_szIniKeyBreakFlags[] = "BreakFlags";
  568. CHAR const FAR c_szIniKeyTraceFlags[] = "TraceFlags";
  569. CHAR const FAR c_szIniKeyFuncTraceFlags[] = "FuncTraceFlags";
  570. CHAR const FAR c_szIniKeyDumpFlags[] = "DumpFlags";
  571. CHAR const FAR c_szIniKeyProtoFlags[] = "Prototype";
  572. #pragma data_seg()
  573. // Some of the .ini processing code was pimped from the sync engine.
  574. //
  575. typedef struct _INIKEYHEADER
  576.     {
  577.     LPCTSTR pszSectionName;
  578.     LPCTSTR pszKeyName;
  579.     LPCTSTR pszDefaultRHS;
  580.     } INIKEYHEADER;
  581. typedef struct _BOOLINIKEY
  582.     {
  583.     INIKEYHEADER ikh;
  584.     LPDWORD puStorage;
  585.     DWORD dwFlag;
  586.     } BOOLINIKEY;
  587. typedef struct _INTINIKEY
  588.     {
  589.     INIKEYHEADER ikh;
  590.     LPDWORD puStorage;
  591.     } INTINIKEY;
  592. #define PutIniIntCmp(idsSection, idsKey, nNewValue, nSave) 
  593.     if ((nNewValue) != (nSave)) PutIniInt(idsSection, idsKey, nNewValue)
  594. #define WritePrivateProfileInt(szApp, szKey, i, lpFileName) 
  595.     {CHAR sz[7]; 
  596.     WritePrivateProfileString(szApp, szKey, SzFromInt(sz, i), lpFileName);}
  597. #ifdef BOOL_INI_VALUES
  598. /* Boolean TRUE strings used by IsIniYes() (comparison is case-insensitive) */
  599. static LPCTSTR s_rgpszTrue[] =
  600.     {
  601.     TEXT("1"),
  602.     TEXT("On"),
  603.     TEXT("True"),
  604.     TEXT("Y"),
  605.     TEXT("Yes")
  606.     };
  607. /* Boolean FALSE strings used by IsIniYes() (comparison is case-insensitive) */
  608. static LPCTSTR s_rgpszFalse[] =
  609.     {
  610.     TEXT("0"),
  611.     TEXT("Off"),
  612.     TEXT("False"),
  613.     TEXT("N"),
  614.     TEXT("No")
  615.     };
  616. #endif
  617. #ifdef BOOL_INI_VALUES
  618. /*----------------------------------------------------------
  619. Purpose: Determines whether a string corresponds to a boolean
  620.           TRUE value.
  621. Returns: The boolean value (TRUE or FALSE)
  622. Cond:    --
  623. */
  624. BOOL
  625. PRIVATE
  626. IsIniYes(
  627.     LPCTSTR psz)
  628.     {
  629.     int i;
  630.     BOOL bNotFound = TRUE;
  631.     BOOL bResult;
  632.     Assert(psz);
  633.     /* Is the value TRUE? */
  634.     for (i = 0; i < ARRAYSIZE(s_rgpszTrue); i++)
  635.         {
  636.         if (IsSzEqual(psz, s_rgpszTrue[i]))
  637.             {
  638.             bResult = TRUE;
  639.             bNotFound = FALSE;
  640.             break;
  641.             }
  642.         }
  643.     /* Is the value FALSE? */
  644.     if (bNotFound)
  645.         {
  646.         for (i = 0; i < ARRAYSIZE(s_rgpszFalse); i++)
  647.             {
  648.             if (IsSzEqual(psz, s_rgpszFalse[i]))
  649.                 {
  650.                 bResult = FALSE;
  651.                 bNotFound = FALSE;
  652.                 break;
  653.                 }
  654.             }
  655.         /* Is the value a known string? */
  656.         if (bNotFound)
  657.             {
  658.             /* No.  Whine about it. */
  659.             TraceMsg(TF_WARNING, "IsIniYes() called on unknown Boolean RHS '%s'.", psz);
  660.             bResult = FALSE;
  661.             }
  662.         }
  663.     return bResult;
  664.     }
  665. /*----------------------------------------------------------
  666. Purpose: Process keys with boolean RHSs.
  667. Returns: --
  668. Cond:    --
  669. */
  670. void
  671. PRIVATE
  672. ProcessBooleans(void)
  673.     {
  674.     int i;
  675.     for (i = 0; i < ARRAYSIZE(s_rgbik); i++)
  676.         {
  677.         DWORD dwcbKeyLen;
  678.         TCHAR szRHS[MAX_BUF];
  679.         BOOLINIKEY * pbik = &(s_rgbik[i]);
  680.         LPCTSTR lpcszRHS;
  681.         /* Look for key. */
  682.         dwcbKeyLen = GetPrivateProfileString(pbik->ikh.pszSectionName,
  683.                                    pbik->ikh.pszKeyName, TEXT(""), szRHS,
  684.                                    SIZECHARS(szRHS), c_szCcshellIniFile);
  685.         if (dwcbKeyLen)
  686.             lpcszRHS = szRHS;
  687.         else
  688.             lpcszRHS = pbik->ikh.pszDefaultRHS;
  689.         if (IsIniYes(lpcszRHS))
  690.             {
  691.             if (IsFlagClear(*(pbik->puStorage), pbik->dwFlag))
  692.                 TraceMsg(TF_GENERAL, "ProcessIniFile(): %s set in %s![%s].",
  693.                          pbik->ikh.pszKeyName,
  694.                          c_szCcshellIniFile,
  695.                          pbik->ikh.pszSectionName);
  696.             SetFlag(*(pbik->puStorage), pbik->dwFlag);
  697.             }
  698.         else
  699.             {
  700.             if (IsFlagSet(*(pbik->puStorage), pbik->dwFlag))
  701.                 TraceMsg(TF_GENERAL, "ProcessIniFile(): %s cleared in %s![%s].",
  702.                          pbik->ikh.pszKeyName,
  703.                          c_szCcshellIniFile,
  704.                          pbik->ikh.pszSectionName);
  705.             ClearFlag(*(pbik->puStorage), pbik->dwFlag);
  706.             }
  707.         }
  708.     }
  709. #endif
  710. /*----------------------------------------------------------
  711. Purpose: Special verion of atoi.  Supports hexadecimal too.
  712.          If this function returns FALSE, *piRet is set to 0.
  713. Returns: TRUE if the string is a number, or contains a partial number
  714.          FALSE if the string is not a number
  715. Cond:    --
  716. */
  717. static
  718. BOOL
  719. MyStrToIntExA(
  720.     LPCSTR    pszString,
  721.     DWORD     dwFlags,          // STIF_ bitfield
  722.     int FAR * piRet)
  723.     {
  724.     #define IS_DIGIT(ch)    InRange(ch, '0', '9')
  725.     BOOL bRet;
  726.     int n;
  727.     BOOL bNeg = FALSE;
  728.     LPCSTR psz;
  729.     LPCSTR pszAdj;
  730.     // Skip leading whitespace
  731.     //
  732.     for (psz = pszString; *psz == ' ' || *psz == 'n' || *psz == 't'; psz = CharNextA(psz))
  733.         ;
  734.     // Determine possible explicit signage
  735.     //
  736.     if (*psz == '+' || *psz == '-')
  737.         {
  738.         bNeg = (*psz == '+') ? FALSE : TRUE;
  739.         psz++;
  740.         }
  741.     // Or is this hexadecimal?
  742.     //
  743.     pszAdj = CharNextA(psz);
  744.     if ((STIF_SUPPORT_HEX & dwFlags) &&
  745.         *psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
  746.         {
  747.         // Yes
  748.         // (Never allow negative sign with hexadecimal numbers)
  749.         bNeg = FALSE;
  750.         psz = CharNextA(pszAdj);
  751.         pszAdj = psz;
  752.         // Do the conversion
  753.         //
  754.         for (n = 0; ; psz = CharNextA(psz))
  755.             {
  756.             if (IS_DIGIT(*psz))
  757.                 n = 0x10 * n + *psz - '0';
  758.             else
  759.                 {
  760.                 CHAR ch = *psz;
  761.                 int n2;
  762.                 if (ch >= 'a')
  763.                     ch -= 'a' - 'A';
  764.                 n2 = ch - 'A' + 0xA;
  765.                 if (n2 >= 0xA && n2 <= 0xF)
  766.                     n = 0x10 * n + n2;
  767.                 else
  768.                     break;
  769.                 }
  770.             }
  771.         // Return TRUE if there was at least one digit
  772.         bRet = (psz != pszAdj);
  773.         }
  774.     else
  775.         {
  776.         // No
  777.         pszAdj = psz;
  778.         // Do the conversion
  779.         for (n = 0; IS_DIGIT(*psz); psz = CharNextA(psz))
  780.             n = 10 * n + *psz - '0';
  781.         // Return TRUE if there was at least one digit
  782.         bRet = (psz != pszAdj);
  783.         }
  784.     *piRet = bNeg ? -n : n;
  785.     return bRet;
  786.     }
  787. #ifdef UNICODE
  788. /*----------------------------------------------------------
  789. Purpose: This function converts a wide-char string to a multi-byte
  790.          string.
  791.          If pszBuf is non-NULL and the converted string can fit in
  792.          pszBuf, then *ppszAnsi will point to the given buffer.
  793.          Otherwise, this function will allocate a buffer that can
  794.          hold the converted string.
  795.          If pszWide is NULL, then *ppszAnsi will be freed.  Note
  796.          that pszBuf must be the same pointer between the call
  797.          that converted the string and the call that frees the
  798.          string.
  799. Returns: TRUE
  800.          FALSE (if out of memory)
  801. Cond:    --
  802. */
  803. static
  804. BOOL
  805. MyAnsiFromUnicode(
  806.     LPSTR * ppszAnsi,
  807.     LPCWSTR pwszWide,        // NULL to clean up
  808.     LPSTR pszBuf,
  809.     int cchBuf)
  810.     {
  811.     BOOL bRet;
  812.     // Convert the string?
  813.     if (pwszWide)
  814.         {
  815.         // Yes; determine the converted string length
  816.         int cch;
  817.         LPSTR psz;
  818.         cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, NULL, 0, NULL, NULL);
  819.         // String too big, or is there no buffer?
  820.         if (cch > cchBuf || NULL == pszBuf)
  821.             {
  822.             // Yes; allocate space
  823.             cchBuf = cch + 1;
  824.             psz = (LPSTR)LocalAlloc(LPTR, CbFromCchA(cchBuf));
  825.             }
  826.         else
  827.             {
  828.             // No; use the provided buffer
  829.             Assert(pszBuf);
  830.             psz = pszBuf;
  831.             }
  832.         if (psz)
  833.             {
  834.             // Convert the string
  835.             cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, psz, cchBuf, NULL, NULL);
  836.             bRet = (0 < cch);
  837.             }
  838.         else
  839.             {
  840.             bRet = FALSE;
  841.             }
  842.         *ppszAnsi = psz;
  843.         }
  844.     else
  845.         {
  846.         // No; was this buffer allocated?
  847.         if (*ppszAnsi && pszBuf != *ppszAnsi)
  848.             {
  849.             // Yes; clean up
  850.             LocalFree((HLOCAL)*ppszAnsi);
  851.             *ppszAnsi = NULL;
  852.             }
  853.         bRet = TRUE;
  854.         }
  855.     return bRet;
  856.     }
  857. /*----------------------------------------------------------
  858. Purpose: Wide-char wrapper for StrToIntExA.
  859. Returns: see StrToIntExA
  860. Cond:    --
  861. */
  862. static
  863. BOOL
  864. MyStrToIntExW(
  865.     LPCWSTR   pwszString,
  866.     DWORD     dwFlags,          // STIF_ bitfield
  867.     int FAR * piRet)
  868.     {
  869.     // Most strings will simply use this temporary buffer, but AnsiFromUnicode
  870.     // will allocate a buffer if the supplied string is bigger.
  871.     CHAR szBuf[MAX_PATH];
  872.     LPSTR pszString;
  873.     BOOL bRet = MyAnsiFromUnicode(&pszString, pwszString, szBuf, SIZECHARS(szBuf));
  874.     if (bRet)
  875.         {
  876.         bRet = MyStrToIntExA(pszString, dwFlags, piRet);
  877.         MyAnsiFromUnicode(&pszString, NULL, szBuf, 0);
  878.         }
  879.     return bRet;
  880.     }
  881. #endif // UNICODE
  882. #ifdef UNICODE
  883. #define MyStrToIntEx        MyStrToIntExW
  884. #else
  885. #define MyStrToIntEx        MyStrToIntExA
  886. #endif
  887. /*----------------------------------------------------------
  888. Purpose: This function reads a .ini file to determine the debug
  889.          flags to set.  The .ini file and section are specified
  890.          by the following manifest constants:
  891.                 SZ_DEBUGINI
  892.                 SZ_DEBUGSECTION
  893.          The debug variables that are set by this function are
  894.          g_dwDumpFlags, g_dwTraceFlags, g_dwBreakFlags, and
  895.          g_dwFuncTraceFlags, g_dwPrototype.
  896. Returns: TRUE if initialization is successful
  897. Cond:    --
  898. */
  899. BOOL
  900. PUBLIC
  901. CcshellGetDebugFlags(void)
  902.     {
  903.     CHAR szRHS[MAX_PATH];
  904.     int val;
  905.     // BUGBUG (scotth): Yes, COMCTL32 exports StrToIntEx, but I
  906.     //  don't want to cause a dependency delta and force everyone
  907.     //  to get a new comctl32 just because they built debug.
  908.     //  So use a local version of StrToIntEx.
  909.     // Trace Flags
  910.     GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  911.                             c_szIniKeyTraceFlags,
  912.                             c_szNull,
  913.                             szRHS,
  914.                             SIZECHARS(szRHS),
  915.                             c_szCcshellIniFile);
  916.     if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  917.         g_dwTraceFlags = (DWORD)val;
  918.     TraceMsgA(TF_GENERAL, "CcshellGetDebugFlags(): %s set to %#08x.",
  919.              c_szIniKeyTraceFlags, g_dwTraceFlags);
  920.     // Function trace Flags
  921.     GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  922.                             c_szIniKeyFuncTraceFlags,
  923.                             c_szNull,
  924.                             szRHS,
  925.                             SIZECHARS(szRHS),
  926.                             c_szCcshellIniFile);
  927.     if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  928.         g_dwFuncTraceFlags = (DWORD)val;
  929.     TraceMsgA(TF_GENERAL, "CcshellGetDebugFlags(): %s set to %#08x.",
  930.              c_szIniKeyFuncTraceFlags, g_dwFuncTraceFlags);
  931.     // Dump Flags
  932.     GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  933.                             c_szIniKeyDumpFlags,
  934.                             c_szNull,
  935.                             szRHS,
  936.                             SIZECHARS(szRHS),
  937.                             c_szCcshellIniFile);
  938.     if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  939.         g_dwDumpFlags = (DWORD)val;
  940.     TraceMsgA(TF_GENERAL, "CcshellGetDebugFlags(): %s set to %#08x.",
  941.              c_szIniKeyDumpFlags, g_dwDumpFlags);
  942.     // Break Flags
  943.     GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  944.                             c_szIniKeyBreakFlags,
  945.                             c_szNull,
  946.                             szRHS,
  947.                             SIZECHARS(szRHS),
  948.                             c_szCcshellIniFile);
  949.     if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  950.         g_dwBreakFlags = (DWORD)val;
  951.     TraceMsgA(TF_GENERAL, "CcshellGetDebugFlags(): %s set to %#08x.",
  952.              c_szIniKeyBreakFlags, g_dwBreakFlags);
  953.     // Prototype Flags
  954.     GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  955.                             c_szIniKeyProtoFlags,
  956.                             c_szNull,
  957.                             szRHS,
  958.                             SIZECHARS(szRHS),
  959.                             c_szCcshellIniFile);
  960.     if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  961.         g_dwPrototype = (DWORD)val;
  962.     TraceMsgA(TF_GENERAL, "CcshellGetDebugFlags(): %s set to %#08x.",
  963.              c_szIniKeyProtoFlags, g_dwPrototype);
  964.     return TRUE;
  965.     }
  966. struct GUIDLOOKUP
  967. {
  968.     GUID    rguid;
  969.     LPCTSTR szDescription;
  970. };
  971. GUIDLOOKUP c_guidlookup[] =
  972. {
  973.     {
  974.         {0x4C96BE40L, 0x915C, 0x11CF, {0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37}},
  975.         TEXT("SID_STopLevelBrowser")
  976.     },
  977.     {
  978.         {0xF77459A0, 0xBF9A, 0x11CF, {0xBA, 0x4E, 0x00, 0xC0, 0x4F, 0xD7, 0x08, 0x16}},
  979.         TEXT("IMimeInfo")
  980.     },
  981.     {
  982.         {0x3050f216, 0x98b5, 0x11cf, {0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b}},
  983.         TEXT("IHTMLDialog")
  984.     }, 
  985.     {
  986.         {0x86D52E11, 0x94A8, 0x11d0, {0x82, 0xAF, 0x00, 0xC0, 0x4F, 0xD5, 0xAE, 0x38}},
  987.         TEXT("ITargetFrame2")
  988.     }, 
  989.     
  990. };
  991. /////////////////////////////////////////////////////////////////////////////
  992. // DebugIIDName, DebugCLSIDName
  993. //
  994. // These functions convert an IID or CLSID to a string name for debugging
  995. // purposes (e.g. IID_IUnknown is converted to "IUnknown").
  996. /////////////////////////////////////////////////////////////////////////////
  997. LPCTSTR DebugGUIDName
  998. (
  999.     REFGUID rguid,
  1000.     LPCTSTR szKey,
  1001.     LPTSTR  pchName
  1002. )
  1003. {
  1004.     OLECHAR achIID[100];        // interface ID (e.g. "{nnn-nnn-...}")  
  1005.     TCHAR   ach[150];
  1006.     // In case of error, clear <pchName>
  1007.     pchName[0] = 0;
  1008.     // Convert <rguid> to a string (e.g. "{nnn-nnn-...}")
  1009.     StringFromGUID2(rguid, achIID, sizeof(achIID));
  1010.     wsprintf(ach, TEXT("%hs\%ls"), szKey, (LPOLESTR) achIID);
  1011.     // Look up <achIID> in the registration database
  1012. #ifdef UNICODE
  1013.     TCHAR pchNameTemp[300];
  1014.     LONG cchNameTemp;
  1015.     cchNameTemp = MAX_PATH;
  1016.     if (RegQueryValue(  HKEY_CLASSES_ROOT,
  1017.                         ach,
  1018.                         pchNameTemp,
  1019.                         &cchNameTemp) != ERROR_SUCCESS)
  1020.     {
  1021.         for (int i = 0; i < ARRAYSIZE(c_guidlookup); i++)
  1022.         {
  1023.             if (InlineIsEqualGUID(c_guidlookup[i].rguid, rguid))
  1024.             {
  1025.                 wsprintf(pchNameTemp, TEXT("%s"), c_guidlookup[i].szDescription);
  1026.                 break;
  1027.             }
  1028.         }
  1029.         // If <achIID> isn't in the registration database, use <achIID> itself
  1030.         if (i == ARRAYSIZE(c_guidlookup))
  1031.             wsprintf(pchNameTemp, TEXT("%ls"), (LPOLESTR) achIID);
  1032.     }
  1033.     wcstombs(pchName, pchNameTemp, MAX_PATH);
  1034. #else
  1035.     LONG cchNameTemp;
  1036.     cchNameTemp = MAX_PATH;
  1037.     if (RegQueryValue(  HKEY_CLASSES_ROOT,
  1038.                         ach,
  1039.                         pchName,
  1040.                         &cchNameTemp) != ERROR_SUCCESS)
  1041.     {
  1042.         for (int i = 0; i < ARRAYSIZE(c_guidlookup); i++)
  1043.         {
  1044.             if (InlineIsEqualGUID(c_guidlookup[i].rguid, rguid))
  1045.             {
  1046.                 wsprintf(pchName, TEXT("%s"), c_guidlookup[i].szDescription);
  1047.                 break;
  1048.             }
  1049.         }
  1050.         // If <achIID> isn't in the registration database, use <achIID> itself
  1051.         if (i == ARRAYSIZE(c_guidlookup))
  1052.             wsprintf(pchName, TEXT("%ls"), (LPOLESTR) achIID);
  1053.     }
  1054. #endif  // UNICODE
  1055.     return pchName;
  1056. }
  1057. /////////////////////////////////////////////////////////////////////////////
  1058. // DebugIIDName
  1059. /////////////////////////////////////////////////////////////////////////////
  1060. LPCTSTR DebugIIDName
  1061. (
  1062.     REFIID  riid,
  1063.     LPTSTR  pchName
  1064. )
  1065. {
  1066.     return DebugGUIDName(riid, TEXT("Interface"), pchName);
  1067. }
  1068. /////////////////////////////////////////////////////////////////////////////
  1069. // DebugCLSIDName
  1070. /////////////////////////////////////////////////////////////////////////////
  1071. LPCTSTR DebugCLSIDName
  1072. (
  1073.     REFCLSID    rclsid,
  1074.     LPTSTR      pchName
  1075. )
  1076. {
  1077.     return DebugGUIDName(rclsid, TEXT("Clsid"), pchName);
  1078. }
  1079. #endif // DEBUG