Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2184 lines
57 KiB

  1. //
  2. // Debug squirty functions
  3. //
  4. #include "stock.h"
  5. #pragma hdrstop
  6. #include "shellp.h"
  7. #include <platform.h> // LINE_SEPARATOR_STR and friends
  8. #include <winbase.h> // for GetModuleFileNameA
  9. #define DM_DEBUG 0
  10. #ifdef DEBUG
  11. // --------------------------------------------------------------------------
  12. // AttachUserModeDebugger
  13. //
  14. // Arguments: <none>
  15. //
  16. // Returns: <none>
  17. //
  18. // Purpose: Attaches a user-mode debugger to the current process if one
  19. // is not already attached. This allows the ASSERT/RIP/TRACE to
  20. // be debugged in a user-mode debugger rather than ending up in
  21. // the kernel debugger which is sometimes limiting.
  22. //
  23. // History: 2000-08-21 vtan created
  24. // --------------------------------------------------------------------------
  25. void AttachUserModeDebugger (void)
  26. {
  27. // Win95 does not support IsDebuggerPresent so we must
  28. // GetProcAddress for it below or we would fail to load.
  29. // In fact, we punt completely on Win9x
  30. if (GetVersion() & 0x80000000) return;
  31. __try
  32. {
  33. typedef BOOL (WINAPI *ISDEBUGGERPRESENT)();
  34. ISDEBUGGERPRESENT _IsDebuggerPresent = (ISDEBUGGERPRESENT)GetProcAddress(GetModuleHandleA("KERNEL32"), "IsDebuggerPresent");
  35. if (_IsDebuggerPresent && !_IsDebuggerPresent())
  36. {
  37. HKEY hKeyAEDebug;
  38. TCHAR szDebugger[MAX_PATH];
  39. szDebugger[0] = TEXT('\0');
  40. // Read HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AEDebug\Debugger
  41. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  42. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AEDebug"),
  43. 0,
  44. KEY_QUERY_VALUE,
  45. &hKeyAEDebug))
  46. {
  47. DWORD dwDebuggerSize;
  48. dwDebuggerSize = sizeof(szDebugger);
  49. (LONG)RegQueryValueEx(hKeyAEDebug,
  50. TEXT("Debugger"),
  51. NULL,
  52. NULL,
  53. (LPBYTE)&szDebugger,
  54. &dwDebuggerSize);
  55. (LONG)RegCloseKey(hKeyAEDebug);
  56. }
  57. if (szDebugger[0] != TEXT('\0'))
  58. {
  59. static const TCHAR s_szDrWatsonImageName[] = TEXT("drwtsn32");
  60. // We cannot use wnsprintf (comctl32 cannot link to shlwapi)
  61. // so we need to make this buffer big enough that the wsprintf
  62. // cannot overflow. We use a sprintf format string of size MAX_PATH
  63. // and insert two integers, so 64 should be plenty of space.
  64. TCHAR szCommandLine[MAX_PATH + 64];
  65. // Duplicate the string to szCommandLine and NULL terminate
  66. // to compare directly for "drwtsn32" which we do NOT want
  67. // attached to the process.
  68. lstrcpy(szCommandLine, szDebugger);
  69. szCommandLine[ARRAYSIZE(s_szDrWatsonImageName) - 1] = TEXT('\0');
  70. if (lstrcmpi(szCommandLine, s_szDrWatsonImageName) != 0)
  71. {
  72. HANDLE hEvent;
  73. SECURITY_ATTRIBUTES securityAttributes;
  74. // Create an unnamed event which is passed to the debugger as an
  75. // inherited handle. It will signal this handle to release this
  76. // thread when the debugger is completely attached to the process.
  77. securityAttributes.nLength = sizeof(securityAttributes);
  78. securityAttributes.lpSecurityDescriptor = NULL;
  79. securityAttributes.bInheritHandle = TRUE;
  80. hEvent = CreateEvent(&securityAttributes, TRUE, FALSE, NULL);
  81. if (hEvent != NULL)
  82. {
  83. STARTUPINFO startupInfo;
  84. PROCESS_INFORMATION processInformation;
  85. ZeroMemory(&startupInfo, sizeof(startupInfo));
  86. ZeroMemory(&processInformation, sizeof(processInformation));
  87. startupInfo.cb = sizeof(startupInfo);
  88. // Now formulate the command line to pass to the debugger.
  89. // It's in the form "cdb -p %ld -e %ld -gGx".
  90. wsprintf(szCommandLine, szDebugger, GetCurrentProcessId(), hEvent);
  91. if (CreateProcess(NULL,
  92. szCommandLine,
  93. NULL,
  94. NULL,
  95. TRUE,
  96. 0,
  97. NULL,
  98. NULL,
  99. &startupInfo,
  100. &processInformation) != FALSE)
  101. {
  102. // Now wait for the event. Give a generous 10 seconds
  103. // for the debugger to signal this event. If the debugger
  104. // doesn't then continue execution. Close all handles.
  105. (DWORD)WaitForSingleObject(hEvent, INFINITE);
  106. (BOOL)CloseHandle(processInformation.hProcess);
  107. (BOOL)CloseHandle(processInformation.hThread);
  108. }
  109. (BOOL)CloseHandle(hEvent);
  110. }
  111. }
  112. else
  113. {
  114. OutputDebugStringA("drwtsn32 detected in AEDebug. Dropping to KD\r\n");
  115. }
  116. }
  117. }
  118. }
  119. __except (EXCEPTION_CONTINUE_EXECUTION)
  120. {
  121. }
  122. }
  123. #endif /* DEBUG */
  124. // UnicodeFromAnsi is used by shlwapi so it should not be inside the ifdef debug
  125. //
  126. /*----------------------------------------------------------
  127. Purpose: This function converts a multi-byte string to a
  128. wide-char string.
  129. If pszBuf is non-NULL and the converted string can fit in
  130. pszBuf, then *ppszWide will point to the given buffer.
  131. Otherwise, this function will allocate a buffer that can
  132. hold the converted string.
  133. If pszAnsi is NULL, then *ppszWide will be freed. Note
  134. that pszBuf must be the same pointer between the call
  135. that converted the string and the call that frees the
  136. string.
  137. Returns: TRUE
  138. FALSE (if out of memory)
  139. */
  140. BOOL
  141. UnicodeFromAnsi(
  142. LPWSTR * ppwszWide,
  143. LPCSTR pszAnsi, // NULL to clean up
  144. LPWSTR pwszBuf,
  145. int cchBuf)
  146. {
  147. BOOL bRet;
  148. // Convert the string?
  149. if (pszAnsi)
  150. {
  151. // Yes; determine the converted string length
  152. int cch;
  153. LPWSTR pwsz;
  154. int cchAnsi = lstrlenA(pszAnsi)+1;
  155. cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, NULL, 0);
  156. // String too big, or is there no buffer?
  157. if (cch > cchBuf || NULL == pwszBuf)
  158. {
  159. // Yes; allocate space
  160. cchBuf = cch + 1;
  161. pwsz = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cchBuf));
  162. }
  163. else
  164. {
  165. // No; use the provided buffer
  166. pwsz = pwszBuf;
  167. }
  168. if (pwsz)
  169. {
  170. // Convert the string
  171. cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, pwsz, cchBuf);
  172. bRet = (0 < cch);
  173. }
  174. else
  175. {
  176. bRet = FALSE;
  177. }
  178. *ppwszWide = pwsz;
  179. }
  180. else
  181. {
  182. // No; was this buffer allocated?
  183. if (*ppwszWide && pwszBuf != *ppwszWide)
  184. {
  185. // Yes; clean up
  186. LocalFree((HLOCAL)*ppwszWide);
  187. *ppwszWide = NULL;
  188. }
  189. bRet = TRUE;
  190. }
  191. return bRet;
  192. }
  193. #if 0 // nobody seems to use this one any more
  194. /*----------------------------------------------------------
  195. Purpose: This function converts a wide-char string to a multi-byte
  196. string.
  197. If pszBuf is non-NULL and the converted string can fit in
  198. pszBuf, then *ppszAnsi will point to the given buffer.
  199. Otherwise, this function will allocate a buffer that can
  200. hold the converted string.
  201. If pszWide is NULL, then *ppszAnsi will be freed. Note
  202. that pszBuf must be the same pointer between the call
  203. that converted the string and the call that frees the
  204. string.
  205. Returns: TRUE
  206. FALSE (if out of memory)
  207. Cond: --
  208. */
  209. BOOL
  210. AnsiFromUnicode(
  211. LPSTR * ppszAnsi,
  212. LPCWSTR pwszWide, // NULL to clean up
  213. LPSTR pszBuf,
  214. int cchBuf)
  215. {
  216. BOOL bRet;
  217. // Convert the string?
  218. if (pwszWide)
  219. {
  220. // Yes; determine the converted string length
  221. int cch;
  222. LPSTR psz;
  223. cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, NULL, 0, NULL, NULL);
  224. // String too big, or is there no buffer?
  225. if (cch > cchBuf || NULL == pszBuf)
  226. {
  227. // Yes; allocate space
  228. cchBuf = cch + 1;
  229. psz = (LPSTR)LocalAlloc(LPTR, CbFromCchA(cchBuf));
  230. }
  231. else
  232. {
  233. // No; use the provided buffer
  234. ASSERT(pszBuf);
  235. psz = pszBuf;
  236. }
  237. if (psz)
  238. {
  239. // Convert the string
  240. cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, psz, cchBuf, NULL, NULL);
  241. bRet = (0 < cch);
  242. }
  243. else
  244. {
  245. bRet = FALSE;
  246. }
  247. *ppszAnsi = psz;
  248. }
  249. else
  250. {
  251. // No; was this buffer allocated?
  252. if (*ppszAnsi && pszBuf != *ppszAnsi)
  253. {
  254. // Yes; clean up
  255. LocalFree((HLOCAL)*ppszAnsi);
  256. *ppszAnsi = NULL;
  257. }
  258. bRet = TRUE;
  259. }
  260. return bRet;
  261. }
  262. #endif // 0
  263. #if defined(DEBUG) || defined(PRODUCT_PROF)
  264. // (c_szCcshellIniFile and c_szCcshellIniSecDebug are declared in debug.h)
  265. extern CHAR const FAR c_szCcshellIniFile[];
  266. extern CHAR const FAR c_szCcshellIniSecDebug[];
  267. HANDLE g_hDebugOutputFile = INVALID_HANDLE_VALUE;
  268. void ShellDebugAppendToDebugFileA(LPCSTR pszOutputString)
  269. {
  270. if (g_hDebugOutputFile != INVALID_HANDLE_VALUE)
  271. {
  272. DWORD cbWrite = lstrlenA(pszOutputString);
  273. WriteFile(g_hDebugOutputFile, pszOutputString, cbWrite, &cbWrite, NULL);
  274. }
  275. }
  276. void ShellDebugAppendToDebugFileW(LPCWSTR pszOutputString)
  277. {
  278. if (g_hDebugOutputFile != INVALID_HANDLE_VALUE)
  279. {
  280. char szBuf[500];
  281. DWORD cbWrite = WideCharToMultiByte(CP_ACP, 0, pszOutputString, -1, szBuf, ARRAYSIZE(szBuf), NULL, NULL);
  282. WriteFile(g_hDebugOutputFile, szBuf, cbWrite, &cbWrite, NULL);
  283. }
  284. }
  285. //
  286. // We cannot link to shlwapi, because comctl32 cannot link to shlwapi.
  287. // Duplicate some functions here so unicode stuff can run on Win95 platforms.
  288. //
  289. VOID MyOutputDebugStringWrapW(LPCWSTR lpOutputString)
  290. {
  291. if (staticIsOS(OS_NT))
  292. {
  293. OutputDebugStringW(lpOutputString);
  294. ShellDebugAppendToDebugFileW(lpOutputString);
  295. }
  296. else
  297. {
  298. char szBuf[500];
  299. WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, szBuf, ARRAYSIZE(szBuf), NULL, NULL);
  300. OutputDebugStringA(szBuf);
  301. ShellDebugAppendToDebugFileA(szBuf);
  302. }
  303. }
  304. #define OutputDebugStringW MyOutputDebugStringWrapW
  305. VOID MyOutputDebugStringWrapA(LPCSTR lpOutputString)
  306. {
  307. OutputDebugStringA(lpOutputString);
  308. ShellDebugAppendToDebugFileA(lpOutputString);
  309. }
  310. #define OutputDebugStringA MyOutputDebugStringWrapA
  311. LPWSTR MyCharPrevWrapW(LPCWSTR lpszStart, LPCWSTR lpszCurrent)
  312. {
  313. if (lpszCurrent == lpszStart)
  314. {
  315. return (LPWSTR) lpszStart;
  316. }
  317. else
  318. {
  319. return (LPWSTR) lpszCurrent - 1;
  320. }
  321. }
  322. #define CharPrevW MyCharPrevWrapW
  323. int MywvsprintfWrapW(LPWSTR pwszOut, LPCWSTR pwszFormat, va_list arglist)
  324. {
  325. if (staticIsOS(OS_NT))
  326. {
  327. return wvsprintfW(pwszOut, pwszFormat, arglist);
  328. }
  329. else
  330. {
  331. char szFormat[500];
  332. char szOut[1024+40]; // this is how big our ach buffers are
  333. int iRet;
  334. WideCharToMultiByte(CP_ACP, 0, pwszFormat, -1, szFormat, ARRAYSIZE(szFormat), NULL, NULL);
  335. iRet = wvsprintfA(szOut, szFormat, arglist);
  336. MultiByteToWideChar(CP_ACP, 0, szOut, -1, pwszOut, 1024+40);
  337. return iRet;
  338. }
  339. }
  340. #define wvsprintfW MywvsprintfWrapW
  341. int MywsprintfWrapW(LPWSTR pwszOut, LPCWSTR pwszFormat, ...)
  342. {
  343. int iRet;
  344. va_list ArgList;
  345. va_start(ArgList, pwszFormat);
  346. iRet = MywvsprintfWrapW(pwszOut, pwszFormat, ArgList);
  347. va_end(ArgList);
  348. return iRet;
  349. }
  350. #define wsprintfW MywsprintfWrapW
  351. LPWSTR lstrcpyWrapW(LPWSTR pszDst, LPCWSTR pszSrc)
  352. {
  353. while((*pszDst++ = *pszSrc++));
  354. return pszDst;
  355. }
  356. #define lstrcpyW lstrcpyWrapW
  357. LPWSTR lstrcatWrapW(LPWSTR pszDst, LPCWSTR pszSrc)
  358. {
  359. return lstrcpyWrapW(pszDst + lstrlenW(pszDst), pszSrc);
  360. }
  361. #define lstrcatW lstrcatWrapW
  362. /*----------------------------------------------------------
  363. Purpose: Special verion of atoi. Supports hexadecimal too.
  364. If this function returns FALSE, *piRet is set to 0.
  365. Returns: TRUE if the string is a number, or contains a partial number
  366. FALSE if the string is not a number
  367. Cond: --
  368. */
  369. static
  370. BOOL
  371. MyStrToIntExA(
  372. LPCSTR pszString,
  373. DWORD dwFlags, // STIF_ bitfield
  374. LONGLONG FAR * piRet)
  375. {
  376. #define IS_DIGIT(ch) InRange(ch, '0', '9')
  377. BOOL bRet;
  378. LONGLONG n;
  379. BOOL bNeg = FALSE;
  380. LPCSTR psz;
  381. LPCSTR pszAdj;
  382. // Skip leading whitespace
  383. //
  384. for (psz = pszString; *psz == ' ' || *psz == '\n' || *psz == '\t'; psz = CharNextA(psz))
  385. ;
  386. // Determine possible explicit signage
  387. //
  388. if (*psz == '+' || *psz == '-')
  389. {
  390. bNeg = (*psz == '+') ? FALSE : TRUE;
  391. psz++;
  392. }
  393. // Or is this hexadecimal?
  394. //
  395. pszAdj = CharNextA(psz);
  396. if ((STIF_SUPPORT_HEX & dwFlags) &&
  397. *psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
  398. {
  399. // Yes
  400. // (Never allow negative sign with hexadecimal numbers)
  401. bNeg = FALSE;
  402. psz = CharNextA(pszAdj);
  403. pszAdj = psz;
  404. // Do the conversion
  405. //
  406. for (n = 0; ; psz = CharNextA(psz))
  407. {
  408. if (IS_DIGIT(*psz))
  409. n = 0x10 * n + *psz - '0';
  410. else
  411. {
  412. CHAR ch = *psz;
  413. int n2;
  414. if (ch >= 'a')
  415. ch -= 'a' - 'A';
  416. n2 = ch - 'A' + 0xA;
  417. if (n2 >= 0xA && n2 <= 0xF)
  418. n = 0x10 * n + n2;
  419. else
  420. break;
  421. }
  422. }
  423. // Return TRUE if there was at least one digit
  424. bRet = (psz != pszAdj);
  425. }
  426. else
  427. {
  428. // No
  429. pszAdj = psz;
  430. // Do the conversion
  431. for (n = 0; IS_DIGIT(*psz); psz = CharNextA(psz))
  432. n = 10 * n + *psz - '0';
  433. // Return TRUE if there was at least one digit
  434. bRet = (psz != pszAdj);
  435. }
  436. *piRet = bNeg ? -n : n;
  437. return bRet;
  438. }
  439. #endif
  440. #ifdef DEBUG
  441. EXTERN_C g_bUseNewLeakDetection = FALSE;
  442. DWORD g_dwDumpFlags = 0; // DF_*
  443. #ifdef FULL_DEBUG
  444. ULONGLONG g_qwTraceFlags = TF_ERROR | TF_WARNING; // TF_*
  445. DWORD g_dwBreakFlags = BF_ASSERT | BF_ONERRORMSG; // BF_*
  446. #else
  447. ULONGLONG g_qwTraceFlags = TF_ERROR; // TF_*
  448. DWORD g_dwBreakFlags = BF_ASSERT; // BF_*
  449. #endif
  450. DWORD g_dwPrototype = 0;
  451. DWORD g_dwFuncTraceFlags = 0; // FTF_*
  452. // TLS slot used to store depth for CcshellFuncMsg indentation
  453. static DWORD g_tlsStackDepth = TLS_OUT_OF_INDEXES;
  454. // Hack stack depth counter used when g_tlsStackDepth is not available
  455. static DWORD g_dwHackStackDepth = 0;
  456. static char g_szIndentLeader[] = " ";
  457. static WCHAR g_wszIndentLeader[] = L" ";
  458. static CHAR const FAR c_szNewline[] = LINE_SEPARATOR_STR; // (Deliberately CHAR)
  459. static WCHAR const FAR c_wszNewline[] = TEXTW(LINE_SEPARATOR_STR);
  460. extern CHAR const FAR c_szTrace[]; // (Deliberately CHAR)
  461. extern CHAR const FAR c_szErrorDbg[]; // (Deliberately CHAR)
  462. extern CHAR const FAR c_szWarningDbg[]; // (Deliberately CHAR)
  463. extern WCHAR const FAR c_wszTrace[];
  464. extern WCHAR const FAR c_wszErrorDbg[];
  465. extern WCHAR const FAR c_wszWarningDbg[];
  466. extern const CHAR FAR c_szAssertMsg[];
  467. extern CHAR const FAR c_szAssertFailed[];
  468. extern const WCHAR FAR c_wszAssertMsg[];
  469. extern WCHAR const FAR c_wszAssertFailed[];
  470. extern CHAR const FAR c_szRip[];
  471. extern CHAR const FAR c_szRipNoFn[];
  472. extern CHAR const FAR c_szRipMsg[];
  473. extern WCHAR const FAR c_wszRip[];
  474. extern WCHAR const FAR c_wszRipNoFn[];
  475. /*-------------------------------------------------------------------------
  476. Purpose: Adds one of the following prefix strings to pszBuf:
  477. "t MODULE "
  478. "err MODULE "
  479. "wrn MODULE "
  480. Returns the count of characters written.
  481. */
  482. int
  483. SetPrefixStringA(
  484. OUT LPSTR pszBuf,
  485. IN ULONGLONG qwFlags)
  486. {
  487. if (TF_ALWAYS == qwFlags)
  488. lstrcpyA(pszBuf, c_szTrace);
  489. else if (IsFlagSet(qwFlags, TF_WARNING))
  490. lstrcpyA(pszBuf, c_szWarningDbg);
  491. else if (IsFlagSet(qwFlags, TF_ERROR))
  492. lstrcpyA(pszBuf, c_szErrorDbg);
  493. else
  494. lstrcpyA(pszBuf, c_szTrace);
  495. return lstrlenA(pszBuf);
  496. }
  497. int
  498. SetPrefixStringW(
  499. OUT LPWSTR pszBuf,
  500. IN ULONGLONG qwFlags)
  501. {
  502. if (TF_ALWAYS == qwFlags)
  503. lstrcpyW(pszBuf, c_wszTrace);
  504. else if (IsFlagSet(qwFlags, TF_WARNING))
  505. lstrcpyW(pszBuf, c_wszWarningDbg);
  506. else if (IsFlagSet(qwFlags, TF_ERROR))
  507. lstrcpyW(pszBuf, c_wszErrorDbg);
  508. else
  509. lstrcpyW(pszBuf, c_wszTrace);
  510. return lstrlenW(pszBuf);
  511. }
  512. static
  513. LPCSTR
  514. _PathFindFileNameA(
  515. LPCSTR pPath)
  516. {
  517. LPCSTR pT;
  518. for (pT = pPath; *pPath; pPath = CharNextA(pPath)) {
  519. if ((pPath[0] == '\\' || pPath[0] == ':' || pPath[0] == '/')
  520. && pPath[1] && pPath[1] != '\\' && pPath[1] != '/')
  521. pT = pPath + 1;
  522. }
  523. return pT;
  524. }
  525. static
  526. LPCWSTR
  527. _PathFindFileNameW(
  528. LPCWSTR pPath)
  529. {
  530. LPCWSTR pT;
  531. for (pT = pPath; *pPath; pPath++) {
  532. if ((pPath[0] == TEXTW('\\') || pPath[0] == TEXTW(':') || pPath[0] == TEXTW('/'))
  533. && pPath[1] && pPath[1] != TEXTW('\\') && pPath[1] != TEXTW('/'))
  534. pT = pPath + 1;
  535. }
  536. return pT;
  537. }
  538. /*-------------------------------------------------------------------------
  539. Purpose: Returns TRUE if this process is a shell-related process.
  540. */
  541. BOOL _IsShellProcess()
  542. {
  543. CHAR szModuleName[MAX_PATH];
  544. if (GetModuleFileNameA(NULL, szModuleName, sizeof(CHAR) * MAX_PATH) > 0 )
  545. {
  546. if (StrStrIA(szModuleName, "explorer.exe") ||
  547. StrStrIA(szModuleName, "iexplore.exe") ||
  548. StrStrIA(szModuleName, "rundll32.exe") ||
  549. StrStrIA(szModuleName, "mshtmpad.exe"))
  550. {
  551. // yes, the exe is a shell one
  552. return TRUE;
  553. }
  554. }
  555. // not a normal shell executable
  556. return FALSE;
  557. }
  558. // FEATURE (scotth): Use the Ccshell functions. _AssertMsg and
  559. // _DebugMsg are obsolete. They will be removed once all the
  560. // components don't have TEXT() wrapping their debug strings anymore.
  561. void
  562. WINCAPI
  563. _AssertMsgA(
  564. BOOL f,
  565. LPCSTR pszMsg, ...)
  566. {
  567. CHAR ach[1024+40];
  568. va_list vArgs;
  569. if (!f)
  570. {
  571. int cch;
  572. lstrcpyA(ach, c_szAssertMsg);
  573. cch = lstrlenA(ach);
  574. va_start(vArgs, pszMsg);
  575. wvsprintfA(&ach[cch], pszMsg, vArgs);
  576. va_end(vArgs);
  577. OutputDebugStringA(ach);
  578. OutputDebugStringA(c_szNewline);
  579. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  580. {
  581. // MSDEV USERS: This is not the real assert. Hit
  582. // Shift-F11 to jump back to the caller.
  583. DEBUG_BREAK; // ASSERT
  584. }
  585. }
  586. }
  587. void
  588. WINCAPI
  589. _AssertMsgW(
  590. BOOL f,
  591. LPCWSTR pszMsg, ...)
  592. {
  593. WCHAR ach[1024+40];
  594. va_list vArgs;
  595. if (!f)
  596. {
  597. int cch;
  598. lstrcpyW(ach, c_wszAssertMsg);
  599. cch = lstrlenW(ach);
  600. va_start(vArgs, pszMsg);
  601. wvsprintfW(&ach[cch], pszMsg, vArgs);
  602. va_end(vArgs);
  603. OutputDebugStringW(ach);
  604. OutputDebugStringW(c_wszNewline);
  605. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  606. {
  607. // MSDEV USERS: This is not the real assert. Hit
  608. // Shift-F11 to jump back to the caller.
  609. DEBUG_BREAK; // ASSERT
  610. }
  611. }
  612. }
  613. void
  614. _AssertStrLenW(
  615. LPCWSTR pszStr,
  616. int iLen)
  617. {
  618. if (pszStr && iLen < lstrlenW(pszStr))
  619. {
  620. // MSDEV USERS: This is not the real assert. Hit
  621. // Shift-F11 to jump back to the caller.
  622. DEBUG_BREAK; // ASSERT
  623. }
  624. }
  625. void
  626. _AssertStrLenA(
  627. LPCSTR pszStr,
  628. int iLen)
  629. {
  630. if (pszStr && iLen < lstrlenA(pszStr))
  631. {
  632. // MSDEV USERS: This is not the real assert. Hit
  633. // Shift-F11 to jump back to the caller.
  634. DEBUG_BREAK; // ASSERT
  635. }
  636. }
  637. void
  638. WINCAPI
  639. _DebugMsgA(
  640. ULONGLONG flag,
  641. LPCSTR pszMsg, ...)
  642. {
  643. CHAR ach[5*MAX_PATH+40]; // Handles 5*largest path + slop for message
  644. va_list vArgs;
  645. if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
  646. {
  647. int cch;
  648. cch = SetPrefixStringA(ach, flag);
  649. va_start(vArgs, pszMsg);
  650. try
  651. {
  652. wvsprintfA(&ach[cch], pszMsg, vArgs);
  653. }
  654. except(EXCEPTION_EXECUTE_HANDLER)
  655. {
  656. OutputDebugString(TEXT("CCSHELL: DebugMsg exception: "));
  657. OutputDebugStringA(pszMsg);
  658. }
  659. __endexcept
  660. va_end(vArgs);
  661. OutputDebugStringA(ach);
  662. OutputDebugStringA(c_szNewline);
  663. if (TF_ALWAYS != flag &&
  664. ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  665. (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  666. {
  667. // MSDEV USERS: This is not the real assert. Hit
  668. // Shift-F11 to jump back to the caller.
  669. DEBUG_BREAK; // ASSERT
  670. }
  671. }
  672. }
  673. void
  674. WINCAPI
  675. _DebugMsgW(
  676. ULONGLONG flag,
  677. LPCWSTR pszMsg, ...)
  678. {
  679. WCHAR ach[5*MAX_PATH+40]; // Handles 5*largest path + slop for message
  680. va_list vArgs;
  681. if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
  682. {
  683. int cch;
  684. SetPrefixStringW(ach, flag);
  685. cch = lstrlenW(ach);
  686. va_start(vArgs, pszMsg);
  687. try
  688. {
  689. wvsprintfW(&ach[cch], pszMsg, vArgs);
  690. }
  691. except(EXCEPTION_EXECUTE_HANDLER)
  692. {
  693. OutputDebugString(TEXT("CCSHELL: DebugMsg exception: "));
  694. OutputDebugStringW(pszMsg);
  695. }
  696. __endexcept
  697. va_end(vArgs);
  698. OutputDebugStringW(ach);
  699. OutputDebugStringW(c_wszNewline);
  700. if (TF_ALWAYS != flag &&
  701. ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  702. (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  703. {
  704. // MSDEV USERS: This is not the real assert. Hit
  705. // Shift-F11 to jump back to the caller.
  706. DEBUG_BREAK; // ASSERT
  707. }
  708. }
  709. }
  710. //
  711. // Smart debug functions
  712. //
  713. /*----------------------------------------------------------
  714. Purpose: Displays assertion string.
  715. Returns: TRUE to debugbreak
  716. */
  717. BOOL
  718. CcshellAssertFailedA(
  719. LPCSTR pszFile,
  720. int line,
  721. LPCSTR pszEval,
  722. BOOL bBreakInside)
  723. {
  724. BOOL bRet = FALSE;
  725. LPCSTR psz;
  726. CHAR ach[256];
  727. psz = _PathFindFileNameA(pszFile);
  728. wsprintfA(ach, c_szAssertFailed, psz, line, pszEval);
  729. OutputDebugStringA(ach);
  730. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  731. {
  732. if (bBreakInside)
  733. {
  734. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  735. // MSDEV USERS: This is not the real assert. Hit
  736. // Shift-F11 to jump back to the caller.
  737. DEBUG_BREAK; // ASSERT
  738. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  739. }
  740. else
  741. bRet = TRUE;
  742. }
  743. return bRet;
  744. }
  745. /*----------------------------------------------------------
  746. Purpose: Displays assertion string.
  747. */
  748. BOOL
  749. CcshellAssertFailedW(
  750. LPCWSTR pszFile,
  751. int line,
  752. LPCWSTR pszEval,
  753. BOOL bBreakInside)
  754. {
  755. BOOL bRet = FALSE;
  756. LPCWSTR psz;
  757. WCHAR ach[1024]; // Some callers use more than 256
  758. psz = _PathFindFileNameW(pszFile);
  759. // If psz == NULL, CharPrevW failed which implies we are running on Win95. We can get this
  760. // if we get an assert in some of the W functions in shlwapi... Call the A version of assert...
  761. if (!psz)
  762. {
  763. char szFile[MAX_PATH];
  764. char szEval[256]; // since the total output is thhis size should be enough...
  765. WideCharToMultiByte(CP_ACP, 0, pszFile, -1, szFile, ARRAYSIZE(szFile), NULL, NULL);
  766. WideCharToMultiByte(CP_ACP, 0, pszEval, -1, szEval, ARRAYSIZE(szEval), NULL, NULL);
  767. return CcshellAssertFailedA(szFile, line, szEval, bBreakInside);
  768. }
  769. wsprintfW(ach, c_wszAssertFailed, psz, line, pszEval);
  770. OutputDebugStringW(ach);
  771. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  772. {
  773. if (bBreakInside)
  774. {
  775. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  776. // MSDEV USERS: This is not the real assert. Hit
  777. // Shift-F11 to jump back to the caller.
  778. DEBUG_BREAK; // ASSERT
  779. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  780. }
  781. else
  782. bRet = TRUE;
  783. }
  784. return bRet;
  785. }
  786. /*----------------------------------------------------------
  787. Purpose: Displays a RIP string.
  788. Returns: TRUE to debugbreak
  789. */
  790. BOOL
  791. CcshellRipA(
  792. LPCSTR pszFile,
  793. int line,
  794. LPCSTR pszEval,
  795. BOOL bBreakInside)
  796. {
  797. BOOL bRet = FALSE;
  798. LPCSTR psz;
  799. CHAR ach[256];
  800. psz = _PathFindFileNameA(pszFile);
  801. wsprintfA(ach, c_szRipNoFn, psz, line, pszEval);
  802. OutputDebugStringA(ach);
  803. if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
  804. {
  805. if (bBreakInside)
  806. {
  807. // !!! RIP !!!! RIP !!!! RIP !!!
  808. // MSDEV USERS: This is not the real RIP. Hit
  809. // Shift-F11 to jump back to the caller.
  810. DEBUG_BREAK; // ASSERT
  811. // !!! RIP !!!! RIP !!!! RIP !!!
  812. }
  813. else
  814. bRet = TRUE;
  815. }
  816. return bRet;
  817. }
  818. /*----------------------------------------------------------
  819. Purpose: Displays a RIP string.
  820. */
  821. BOOL
  822. CcshellRipW(
  823. LPCWSTR pszFile,
  824. int line,
  825. LPCWSTR pszEval,
  826. BOOL bBreakInside)
  827. {
  828. BOOL bRet = FALSE;
  829. LPCWSTR psz;
  830. WCHAR ach[256];
  831. psz = _PathFindFileNameW(pszFile);
  832. // If psz == NULL, CharPrevW failed which implies we are running on Win95.
  833. // We can get this if we get an assert in some of the W functions in
  834. // shlwapi... Call the A version of assert...
  835. if (!psz)
  836. {
  837. char szFile[MAX_PATH];
  838. char szEval[256]; // since the total output is thhis size should be enough...
  839. WideCharToMultiByte(CP_ACP, 0, pszFile, -1, szFile, ARRAYSIZE(szFile), NULL, NULL);
  840. WideCharToMultiByte(CP_ACP, 0, pszEval, -1, szEval, ARRAYSIZE(szEval), NULL, NULL);
  841. return CcshellRipA(szFile, line, szEval, bBreakInside);
  842. }
  843. wsprintfW(ach, c_wszRipNoFn, psz, line, pszEval);
  844. OutputDebugStringW(ach);
  845. if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
  846. {
  847. if (bBreakInside)
  848. {
  849. // !!! RIP !!!! RIP !!!! RIP !!!
  850. // MSDEV USERS: This is not the real assert. Hit
  851. // Shift-F11 to jump back to the caller.
  852. DEBUG_BREAK; // ASSERT
  853. // !!! RIP !!!! RIP !!!! RIP !!!
  854. }
  855. else
  856. bRet = TRUE;
  857. }
  858. return bRet;
  859. }
  860. BOOL
  861. WINCAPI
  862. CcshellRipMsgA(
  863. BOOL f,
  864. LPCSTR pszMsg, ...)
  865. {
  866. CHAR ach[1024+40];
  867. va_list vArgs;
  868. if (!f)
  869. {
  870. OutputDebugStringA(c_szRipMsg);
  871. va_start(vArgs, pszMsg);
  872. wvsprintfA(ach, pszMsg, vArgs);
  873. va_end(vArgs);
  874. OutputDebugStringA(ach);
  875. OutputDebugStringA(c_szNewline);
  876. if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
  877. {
  878. // MSDEV USERS: This is not the real assert. Hit
  879. // Shift-F11 to jump back to the caller.
  880. DEBUG_BREAK; // ASSERT
  881. }
  882. }
  883. return FALSE;
  884. }
  885. BOOL
  886. WINCAPI
  887. CcshellRipMsgW(
  888. BOOL f,
  889. LPCSTR pszMsg, ...) // (this is deliberately CHAR)
  890. {
  891. WCHAR ach[1024+40];
  892. va_list vArgs;
  893. if (!f)
  894. {
  895. LPWSTR pwsz;
  896. WCHAR wszBuf[128];
  897. OutputDebugStringA(c_szRipMsg);
  898. // (We convert the string, rather than simply input an
  899. // LPCWSTR parameter, so the caller doesn't have to wrap
  900. // all the string constants with the TEXT() macro.)
  901. ach[0] = L'\0'; // In case this fails
  902. if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  903. {
  904. va_start(vArgs, pszMsg);
  905. wvsprintfW(ach, pwsz, vArgs);
  906. va_end(vArgs);
  907. UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  908. }
  909. OutputDebugStringW(ach);
  910. OutputDebugStringA(c_szNewline);
  911. if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
  912. {
  913. // MSDEV USERS: This is not the real assert. Hit
  914. // Shift-F11 to jump back to the caller.
  915. DEBUG_BREAK; // ASSERT
  916. }
  917. }
  918. return FALSE;
  919. }
  920. /*----------------------------------------------------------
  921. Purpose: Keep track of the stack depth for function call trace
  922. messages.
  923. */
  924. void
  925. CcshellStackEnter(void)
  926. {
  927. if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  928. {
  929. DWORD dwDepth;
  930. dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  931. TlsSetValue(g_tlsStackDepth, (LPVOID)(ULONG_PTR)(dwDepth + 1));
  932. }
  933. else
  934. {
  935. g_dwHackStackDepth++;
  936. }
  937. }
  938. /*----------------------------------------------------------
  939. Purpose: Keep track of the stack depth for functionc all trace
  940. messages.
  941. */
  942. void
  943. CcshellStackLeave(void)
  944. {
  945. if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  946. {
  947. DWORD dwDepth;
  948. dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  949. if (EVAL(0 < dwDepth))
  950. {
  951. EVAL(TlsSetValue(g_tlsStackDepth, (LPVOID)(ULONG_PTR)(dwDepth - 1)));
  952. }
  953. }
  954. else
  955. {
  956. if (EVAL(0 < g_dwHackStackDepth))
  957. {
  958. g_dwHackStackDepth--;
  959. }
  960. }
  961. }
  962. /*----------------------------------------------------------
  963. Purpose: Return the stack depth.
  964. */
  965. static
  966. DWORD
  967. CcshellGetStackDepth(void)
  968. {
  969. DWORD dwDepth;
  970. if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
  971. {
  972. dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
  973. }
  974. else
  975. {
  976. dwDepth = g_dwHackStackDepth;
  977. }
  978. return dwDepth;
  979. }
  980. /*----------------------------------------------------------
  981. Purpose: Wide-char version of CcshellAssertMsgA
  982. */
  983. void
  984. CDECL
  985. CcshellAssertMsgW(
  986. BOOL f,
  987. LPCSTR pszMsg, ...)
  988. {
  989. WCHAR ach[1024+40]; // Largest path plus extra
  990. va_list vArgs;
  991. if (!f)
  992. {
  993. int cch;
  994. WCHAR wszBuf[1024];
  995. LPWSTR pwsz;
  996. lstrcpyW(ach, c_wszAssertMsg);
  997. cch = lstrlenW(ach);
  998. va_start(vArgs, pszMsg);
  999. // (We convert the string, rather than simply input an
  1000. // LPCWSTR parameter, so the caller doesn't have to wrap
  1001. // all the string constants with the TEXT() macro.)
  1002. if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  1003. {
  1004. wvsprintfW(&ach[cch], pwsz, vArgs);
  1005. UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  1006. }
  1007. va_end(vArgs);
  1008. OutputDebugStringW(ach);
  1009. OutputDebugStringW(c_wszNewline);
  1010. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  1011. {
  1012. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  1013. // MSDEV USERS: This is not the real assert. Hit
  1014. // Shift-F11 to jump back to the caller.
  1015. DEBUG_BREAK; // ASSERT
  1016. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  1017. }
  1018. }
  1019. }
  1020. /*----------------------------------------------------------
  1021. Purpose: Wide-char version of CcshellDebugMsgA. Note this
  1022. function deliberately takes an ANSI format string
  1023. so our trace messages don't all need to be wrapped
  1024. in TEXT().
  1025. */
  1026. void
  1027. CDECL
  1028. CcshellDebugMsgW(
  1029. ULONGLONG flag,
  1030. LPCSTR pszMsg, ...) // (this is deliberately CHAR)
  1031. {
  1032. WCHAR ach[1024+40]; // Largest path plus extra
  1033. va_list vArgs;
  1034. if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
  1035. {
  1036. int cch;
  1037. WCHAR wszBuf[1024];
  1038. LPWSTR pwsz;
  1039. SetPrefixStringW(ach, flag);
  1040. cch = lstrlenW(ach);
  1041. va_start(vArgs, pszMsg);
  1042. // (We convert the string, rather than simply input an
  1043. // LPCWSTR parameter, so the caller doesn't have to wrap
  1044. // all the string constants with the TEXT() macro.)
  1045. if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  1046. {
  1047. wvsprintfW(&ach[cch], pwsz, vArgs);
  1048. UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  1049. }
  1050. va_end(vArgs);
  1051. OutputDebugStringW(ach);
  1052. OutputDebugStringW(c_wszNewline);
  1053. if (TF_ALWAYS != flag &&
  1054. ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  1055. (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  1056. {
  1057. // MSDEV USERS: This is not the real assert. Hit
  1058. // Shift-F11 to jump back to the caller.
  1059. DEBUG_BREAK; // ASSERT
  1060. }
  1061. }
  1062. }
  1063. /*-------------------------------------------------------------------------
  1064. Purpose: Since the ATL code does not pass in a flag parameter,
  1065. we'll hardcode and check for TF_ATL.
  1066. */
  1067. void CDECL ShellAtlTraceW(LPCWSTR pszMsg, ...)
  1068. {
  1069. WCHAR ach[1024+40]; // Largest path plus extra
  1070. va_list vArgs;
  1071. if (g_qwTraceFlags & TF_ATL)
  1072. {
  1073. int cch;
  1074. SetPrefixStringW(ach, TF_ATL);
  1075. lstrcatW(ach, L"(ATL) ");
  1076. cch = lstrlenW(ach);
  1077. va_start(vArgs, pszMsg);
  1078. wvsprintfW(&ach[cch], pszMsg, vArgs);
  1079. va_end(vArgs);
  1080. OutputDebugStringW(ach);
  1081. }
  1082. }
  1083. /*----------------------------------------------------------
  1084. Purpose: Wide-char version of CcshellFuncMsgA. Note this
  1085. function deliberately takes an ANSI format string
  1086. so our trace messages don't all need to be wrapped
  1087. in TEXT().
  1088. */
  1089. void
  1090. CDECL
  1091. CcshellFuncMsgW(
  1092. ULONGLONG flag,
  1093. LPCSTR pszMsg, ...) // (this is deliberately CHAR)
  1094. {
  1095. WCHAR ach[1024+40]; // Largest path plus extra
  1096. va_list vArgs;
  1097. if (IsFlagSet(g_qwTraceFlags, TF_FUNC) &&
  1098. IsFlagSet(g_dwFuncTraceFlags, flag))
  1099. {
  1100. int cch;
  1101. WCHAR wszBuf[1024];
  1102. LPWSTR pwsz;
  1103. DWORD dwStackDepth;
  1104. LPWSTR pszLeaderEnd;
  1105. WCHAR chSave;
  1106. // Determine the indentation for trace message based on
  1107. // stack depth.
  1108. dwStackDepth = CcshellGetStackDepth();
  1109. if (dwStackDepth < SIZECHARS(g_szIndentLeader))
  1110. {
  1111. pszLeaderEnd = &g_wszIndentLeader[dwStackDepth];
  1112. }
  1113. else
  1114. {
  1115. pszLeaderEnd = &g_wszIndentLeader[SIZECHARS(g_wszIndentLeader)-1];
  1116. }
  1117. chSave = *pszLeaderEnd;
  1118. *pszLeaderEnd = '\0';
  1119. wsprintfW(ach, L"%s %s", c_wszTrace, g_wszIndentLeader);
  1120. *pszLeaderEnd = chSave;
  1121. // Compose remaining string
  1122. cch = lstrlenW(ach);
  1123. va_start(vArgs, pszMsg);
  1124. // (We convert the string, rather than simply input an
  1125. // LPCWSTR parameter, so the caller doesn't have to wrap
  1126. // all the string constants with the TEXT() macro.)
  1127. if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
  1128. {
  1129. wvsprintfW(&ach[cch], pwsz, vArgs);
  1130. UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
  1131. }
  1132. va_end(vArgs);
  1133. OutputDebugStringW(ach);
  1134. OutputDebugStringW(c_wszNewline);
  1135. }
  1136. }
  1137. /*----------------------------------------------------------
  1138. Purpose: Assert failed message only
  1139. */
  1140. void
  1141. CDECL
  1142. CcshellAssertMsgA(
  1143. BOOL f,
  1144. LPCSTR pszMsg, ...)
  1145. {
  1146. CHAR ach[1024+40]; // Largest path plus extra
  1147. va_list vArgs;
  1148. if (!f)
  1149. {
  1150. int cch;
  1151. lstrcpyA(ach, c_szAssertMsg);
  1152. cch = lstrlenA(ach);
  1153. va_start(vArgs, pszMsg);
  1154. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1155. va_end(vArgs);
  1156. OutputDebugStringA(ach);
  1157. OutputDebugStringA(c_szNewline);
  1158. if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
  1159. {
  1160. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  1161. // MSDEV USERS: This is not the real assert. Hit
  1162. // Shift-F11 to jump back to the caller.
  1163. DEBUG_BREAK; // ASSERT
  1164. // !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
  1165. }
  1166. }
  1167. }
  1168. /*----------------------------------------------------------
  1169. Purpose: Debug spew
  1170. */
  1171. void
  1172. CDECL
  1173. CcshellDebugMsgA(
  1174. ULONGLONG flag,
  1175. LPCSTR pszMsg, ...)
  1176. {
  1177. CHAR ach[1024+40]; // Largest path plus extra
  1178. va_list vArgs;
  1179. if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
  1180. {
  1181. int cch;
  1182. cch = SetPrefixStringA(ach, flag);
  1183. va_start(vArgs, pszMsg);
  1184. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1185. va_end(vArgs);
  1186. OutputDebugStringA(ach);
  1187. OutputDebugStringA(c_szNewline);
  1188. if (TF_ALWAYS != flag &&
  1189. ((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
  1190. (flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
  1191. {
  1192. // MSDEV USERS: This is not the real assert. Hit
  1193. // Shift-F11 to jump back to the caller.
  1194. DEBUG_BREAK; // ASSERT
  1195. }
  1196. }
  1197. }
  1198. /*-------------------------------------------------------------------------
  1199. Purpose: Since the ATL code does not pass in a flag parameter,
  1200. we'll hardcode and check for TF_ATL.
  1201. */
  1202. void CDECL ShellAtlTraceA(LPCSTR pszMsg, ...)
  1203. {
  1204. CHAR ach[1024+40]; // Largest path plus extra
  1205. va_list vArgs;
  1206. if (g_qwTraceFlags & TF_ATL)
  1207. {
  1208. int cch;
  1209. SetPrefixStringA(ach, TF_ATL);
  1210. lstrcatA(ach, "(ATL) ");
  1211. cch = lstrlenA(ach);
  1212. va_start(vArgs, pszMsg);
  1213. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1214. va_end(vArgs);
  1215. OutputDebugStringA(ach);
  1216. }
  1217. }
  1218. /*----------------------------------------------------------
  1219. Purpose: Debug spew for function trace calls
  1220. */
  1221. void
  1222. CDECL
  1223. CcshellFuncMsgA(
  1224. ULONGLONG flag,
  1225. LPCSTR pszMsg, ...)
  1226. {
  1227. CHAR ach[1024+40]; // Largest path plus extra
  1228. va_list vArgs;
  1229. if (IsFlagSet(g_qwTraceFlags, TF_FUNC) &&
  1230. IsFlagSet(g_dwFuncTraceFlags, flag))
  1231. {
  1232. int cch;
  1233. DWORD dwStackDepth;
  1234. LPSTR pszLeaderEnd;
  1235. CHAR chSave;
  1236. // Determine the indentation for trace message based on
  1237. // stack depth.
  1238. dwStackDepth = CcshellGetStackDepth();
  1239. if (dwStackDepth < sizeof(g_szIndentLeader))
  1240. {
  1241. pszLeaderEnd = &g_szIndentLeader[dwStackDepth];
  1242. }
  1243. else
  1244. {
  1245. pszLeaderEnd = &g_szIndentLeader[sizeof(g_szIndentLeader)-1];
  1246. }
  1247. chSave = *pszLeaderEnd;
  1248. *pszLeaderEnd = '\0';
  1249. wsprintfA(ach, "%s %s", c_szTrace, g_szIndentLeader);
  1250. *pszLeaderEnd = chSave;
  1251. // Compose remaining string
  1252. cch = lstrlenA(ach);
  1253. va_start(vArgs, pszMsg);
  1254. wvsprintfA(&ach[cch], pszMsg, vArgs);
  1255. va_end(vArgs);
  1256. OutputDebugStringA(ach);
  1257. OutputDebugStringA(c_szNewline);
  1258. }
  1259. }
  1260. /*-------------------------------------------------------------------------
  1261. Purpose: Spews a trace message if hrTest is a failure code.
  1262. */
  1263. HRESULT
  1264. TraceHR(
  1265. HRESULT hrTest,
  1266. LPCSTR pszExpr,
  1267. LPCSTR pszFile,
  1268. int iLine)
  1269. {
  1270. CHAR ach[1024+40]; // Largest path plus extra
  1271. if (g_qwTraceFlags & TF_WARNING &&
  1272. FAILED(hrTest))
  1273. {
  1274. int cch;
  1275. cch = SetPrefixStringA(ach, TF_WARNING);
  1276. wsprintfA(&ach[cch], "THR: Failure of \"%s\" at %s, line %d (%#08lx)",
  1277. pszExpr, _PathFindFileNameA(pszFile), iLine, hrTest);
  1278. OutputDebugStringA(ach);
  1279. OutputDebugStringA(c_szNewline);
  1280. if (IsFlagSet(g_dwBreakFlags, BF_THR))
  1281. {
  1282. // !!! THR !!!! THR !!!! THR !!!
  1283. // MSDEV USERS: This is not the real assert. Hit
  1284. // Shift-F11 to jump back to the caller.
  1285. DEBUG_BREAK; // ASSERT
  1286. // !!! THR !!!! THR !!!! THR !!!
  1287. }
  1288. }
  1289. return hrTest;
  1290. }
  1291. /*-------------------------------------------------------------------------
  1292. Purpose: Spews a trace message if bTest is false.
  1293. */
  1294. BOOL
  1295. TraceBool(
  1296. BOOL bTest,
  1297. LPCSTR pszExpr,
  1298. LPCSTR pszFile,
  1299. int iLine)
  1300. {
  1301. CHAR ach[1024+40]; // Largest path plus extra
  1302. if (g_qwTraceFlags & TF_WARNING && !bTest)
  1303. {
  1304. int cch;
  1305. cch = SetPrefixStringA(ach, TF_WARNING);
  1306. wsprintfA(&ach[cch], "TBOOL: Failure of \"%s\" at %s, line %d",
  1307. pszExpr, _PathFindFileNameA(pszFile), iLine);
  1308. OutputDebugStringA(ach);
  1309. OutputDebugStringA(c_szNewline);
  1310. if (IsFlagSet(g_dwBreakFlags, BF_THR))
  1311. {
  1312. // !!! TBOOL !!!! TBOOL !!!! TBOOL !!!
  1313. // MSDEV USERS: This is not the real assert. Hit
  1314. // Shift-F11 to jump back to the caller.
  1315. DEBUG_BREAK; // ASSERT
  1316. // !!! TBOOL !!!! TBOOL !!!! TBOOL !!!
  1317. }
  1318. }
  1319. return bTest;
  1320. }
  1321. /*-------------------------------------------------------------------------
  1322. Purpose: Spews a trace message if iTest is -1.
  1323. */
  1324. int
  1325. TraceInt(
  1326. int iTest,
  1327. LPCSTR pszExpr,
  1328. LPCSTR pszFile,
  1329. int iLine)
  1330. {
  1331. CHAR ach[1024+40]; // Largest path plus extra
  1332. if (g_qwTraceFlags & TF_WARNING && -1 == iTest)
  1333. {
  1334. int cch;
  1335. cch = SetPrefixStringA(ach, TF_WARNING);
  1336. wsprintfA(&ach[cch], "TINT: Failure of \"%s\" at %s, line %d",
  1337. pszExpr, _PathFindFileNameA(pszFile), iLine);
  1338. OutputDebugStringA(ach);
  1339. OutputDebugStringA(c_szNewline);
  1340. if (IsFlagSet(g_dwBreakFlags, BF_THR))
  1341. {
  1342. // !!! TINT !!!! TINT !!!! TINT !!!
  1343. // MSDEV USERS: This is not the real assert. Hit
  1344. // Shift-F11 to jump back to the caller.
  1345. DEBUG_BREAK; // ASSERT
  1346. // !!! TINT !!!! TINT !!!! TINT !!!
  1347. }
  1348. }
  1349. return iTest;
  1350. }
  1351. /*-------------------------------------------------------------------------
  1352. Purpose: Spews a trace message if pvTest is NULL.
  1353. */
  1354. LPVOID
  1355. TracePtr(
  1356. LPVOID pvTest,
  1357. LPCSTR pszExpr,
  1358. LPCSTR pszFile,
  1359. int iLine)
  1360. {
  1361. CHAR ach[1024+40]; // Largest path plus extra
  1362. if (g_qwTraceFlags & TF_WARNING && NULL == pvTest)
  1363. {
  1364. int cch;
  1365. cch = SetPrefixStringA(ach, TF_WARNING);
  1366. wsprintfA(&ach[cch], "TPTR: Failure of \"%s\" at %s, line %d",
  1367. pszExpr, _PathFindFileNameA(pszFile), iLine);
  1368. OutputDebugStringA(ach);
  1369. OutputDebugStringA(c_szNewline);
  1370. if (IsFlagSet(g_dwBreakFlags, BF_THR))
  1371. {
  1372. // !!! TPTR !!!! TPTR !!!! TPTR !!!
  1373. // MSDEV USERS: This is not the real assert. Hit
  1374. // Shift-F11 to jump back to the caller.
  1375. DEBUG_BREAK; // ASSERT
  1376. // !!! TPTR !!!! TPTR !!!! TPTR !!!
  1377. }
  1378. }
  1379. return pvTest;
  1380. }
  1381. /*-------------------------------------------------------------------------
  1382. Purpose: Spews a trace message if dwTest is a Win32 failure code.
  1383. */
  1384. DWORD
  1385. TraceWin32(
  1386. DWORD dwTest,
  1387. LPCSTR pszExpr,
  1388. LPCSTR pszFile,
  1389. int iLine)
  1390. {
  1391. CHAR ach[1024+40]; // Largest path plus extra
  1392. if (g_qwTraceFlags & TF_WARNING &&
  1393. ERROR_SUCCESS != dwTest)
  1394. {
  1395. int cch;
  1396. cch = SetPrefixStringA(ach, TF_WARNING);
  1397. wsprintfA(&ach[cch], "TW32: Failure of \"%s\" at %s, line %d (%#08lx)",
  1398. pszExpr, _PathFindFileNameA(pszFile), iLine, dwTest);
  1399. OutputDebugStringA(ach);
  1400. OutputDebugStringA(c_szNewline);
  1401. if (IsFlagSet(g_dwBreakFlags, BF_THR))
  1402. {
  1403. // !!! THR !!!! THR !!!! THR !!!
  1404. // MSDEV USERS: This is not the real assert. Hit
  1405. // Shift-F11 to jump back to the caller.
  1406. DEBUG_BREAK; // ASSERT
  1407. // !!! THR !!!! THR !!!! THR !!!
  1408. }
  1409. }
  1410. return dwTest;
  1411. }
  1412. //
  1413. // Debug .ini functions
  1414. //
  1415. #pragma data_seg(DATASEG_READONLY)
  1416. // (These are deliberately CHAR)
  1417. CHAR const FAR c_szNull[] = "";
  1418. CHAR const FAR c_szZero[] = "0";
  1419. CHAR const FAR c_szIniKeyBreakFlags[] = "BreakFlags";
  1420. CHAR const FAR c_szIniKeyTraceFlags[] = "TraceFlags";
  1421. CHAR const FAR c_szIniKeyFuncTraceFlags[] = "FuncTraceFlags";
  1422. CHAR const FAR c_szIniKeyDumpFlags[] = "DumpFlags";
  1423. CHAR const FAR c_szIniKeyProtoFlags[] = "Prototype";
  1424. #pragma data_seg()
  1425. // Some of the .ini processing code was pimped from the sync engine.
  1426. //
  1427. typedef struct _INIKEYHEADER
  1428. {
  1429. LPCTSTR pszSectionName;
  1430. LPCTSTR pszKeyName;
  1431. LPCTSTR pszDefaultRHS;
  1432. } INIKEYHEADER;
  1433. typedef struct _BOOLINIKEY
  1434. {
  1435. INIKEYHEADER ikh;
  1436. LPDWORD puStorage;
  1437. DWORD dwFlag;
  1438. } BOOLINIKEY;
  1439. typedef struct _INTINIKEY
  1440. {
  1441. INIKEYHEADER ikh;
  1442. LPDWORD puStorage;
  1443. } INTINIKEY;
  1444. #define PutIniIntCmp(idsSection, idsKey, nNewValue, nSave) \
  1445. if ((nNewValue) != (nSave)) PutIniInt(idsSection, idsKey, nNewValue)
  1446. #define WritePrivateProfileInt(szApp, szKey, i, lpFileName) \
  1447. {CHAR sz[7]; \
  1448. WritePrivateProfileString(szApp, szKey, SzFromInt(sz, i), lpFileName);}
  1449. #ifdef BOOL_INI_VALUES
  1450. /* Boolean TRUE strings used by IsIniYes() (comparison is case-insensitive) */
  1451. static LPCTSTR s_rgpszTrue[] =
  1452. {
  1453. TEXT("1"),
  1454. TEXT("On"),
  1455. TEXT("True"),
  1456. TEXT("Y"),
  1457. TEXT("Yes")
  1458. };
  1459. /* Boolean FALSE strings used by IsIniYes() (comparison is case-insensitive) */
  1460. static LPCTSTR s_rgpszFalse[] =
  1461. {
  1462. TEXT("0"),
  1463. TEXT("Off"),
  1464. TEXT("False"),
  1465. TEXT("N"),
  1466. TEXT("No")
  1467. };
  1468. #endif
  1469. #ifdef BOOL_INI_VALUES
  1470. /*----------------------------------------------------------
  1471. Purpose: Determines whether a string corresponds to a boolean
  1472. TRUE value.
  1473. Returns: The boolean value (TRUE or FALSE)
  1474. */
  1475. BOOL
  1476. PRIVATE
  1477. IsIniYes(
  1478. LPCTSTR psz)
  1479. {
  1480. int i;
  1481. BOOL bNotFound = TRUE;
  1482. BOOL bResult;
  1483. ASSERT(psz);
  1484. /* Is the value TRUE? */
  1485. for (i = 0; i < ARRAYSIZE(s_rgpszTrue); i++)
  1486. {
  1487. if (IsSzEqual(psz, s_rgpszTrue[i]))
  1488. {
  1489. bResult = TRUE;
  1490. bNotFound = FALSE;
  1491. break;
  1492. }
  1493. }
  1494. /* Is the value FALSE? */
  1495. if (bNotFound)
  1496. {
  1497. for (i = 0; i < ARRAYSIZE(s_rgpszFalse); i++)
  1498. {
  1499. if (IsSzEqual(psz, s_rgpszFalse[i]))
  1500. {
  1501. bResult = FALSE;
  1502. bNotFound = FALSE;
  1503. break;
  1504. }
  1505. }
  1506. /* Is the value a known string? */
  1507. if (bNotFound)
  1508. {
  1509. /* No. Whine about it. */
  1510. TraceMsg(TF_WARNING, "IsIniYes() called on unknown Boolean RHS '%s'.", psz);
  1511. bResult = FALSE;
  1512. }
  1513. }
  1514. return bResult;
  1515. }
  1516. /*----------------------------------------------------------
  1517. Purpose: Process keys with boolean RHSs.
  1518. */
  1519. void
  1520. PRIVATE
  1521. ProcessBooleans(void)
  1522. {
  1523. int i;
  1524. for (i = 0; i < ARRAYSIZE(s_rgbik); i++)
  1525. {
  1526. DWORD dwcbKeyLen;
  1527. TCHAR szRHS[MAX_BUF];
  1528. BOOLINIKEY * pbik = &(s_rgbik[i]);
  1529. LPCTSTR lpcszRHS;
  1530. /* Look for key. */
  1531. dwcbKeyLen = GetPrivateProfileString(pbik->ikh.pszSectionName,
  1532. pbik->ikh.pszKeyName, TEXT(""), szRHS,
  1533. SIZECHARS(szRHS), c_szCcshellIniFile);
  1534. if (dwcbKeyLen)
  1535. lpcszRHS = szRHS;
  1536. else
  1537. lpcszRHS = pbik->ikh.pszDefaultRHS;
  1538. if (IsIniYes(lpcszRHS))
  1539. {
  1540. if (IsFlagClear(*(pbik->puStorage), pbik->dwFlag))
  1541. TraceMsg(TF_GENERAL, "ProcessIniFile(): %s set in %s![%s].",
  1542. pbik->ikh.pszKeyName,
  1543. c_szCcshellIniFile,
  1544. pbik->ikh.pszSectionName);
  1545. SetFlag(*(pbik->puStorage), pbik->dwFlag);
  1546. }
  1547. else
  1548. {
  1549. if (IsFlagSet(*(pbik->puStorage), pbik->dwFlag))
  1550. TraceMsg(TF_GENERAL, "ProcessIniFile(): %s cleared in %s![%s].",
  1551. pbik->ikh.pszKeyName,
  1552. c_szCcshellIniFile,
  1553. pbik->ikh.pszSectionName);
  1554. ClearFlag(*(pbik->puStorage), pbik->dwFlag);
  1555. }
  1556. }
  1557. }
  1558. #endif
  1559. /*----------------------------------------------------------
  1560. Purpose: This function reads a .ini file to determine the debug
  1561. flags to set. The .ini file and section are specified
  1562. by the following manifest constants:
  1563. SZ_DEBUGINI
  1564. SZ_DEBUGSECTION
  1565. The debug variables that are set by this function are
  1566. g_dwDumpFlags, g_qwTraceFlags, g_dwBreakFlags, and
  1567. g_dwFuncTraceFlags, g_dwPrototype.
  1568. Returns: TRUE if initialization is successful
  1569. */
  1570. BOOL
  1571. PUBLIC
  1572. CcshellGetDebugFlags(void)
  1573. {
  1574. CHAR szRHS[MAX_PATH];
  1575. ULONGLONG val;
  1576. // (scotth): Yes, COMCTL32 exports StrToIntEx, but I
  1577. // don't want to cause a dependency delta and force everyone
  1578. // to get a new comctl32 just because they built debug.
  1579. // So use a local version of StrToIntEx.
  1580. // Trace Flags
  1581. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1582. c_szIniKeyTraceFlags,
  1583. c_szNull,
  1584. szRHS,
  1585. SIZECHARS(szRHS),
  1586. c_szCcshellIniFile);
  1587. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1588. g_qwTraceFlags = val;
  1589. #ifdef FULL_DEBUG
  1590. else
  1591. g_qwTraceFlags = 3; // default to TF_ERROR and TF_WARNING trace messages
  1592. #endif
  1593. TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#16I64x.",
  1594. c_szIniKeyTraceFlags, g_qwTraceFlags);
  1595. // Function trace Flags
  1596. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1597. c_szIniKeyFuncTraceFlags,
  1598. c_szNull,
  1599. szRHS,
  1600. SIZECHARS(szRHS),
  1601. c_szCcshellIniFile);
  1602. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1603. g_dwFuncTraceFlags = (DWORD)val;
  1604. TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
  1605. c_szIniKeyFuncTraceFlags, g_dwFuncTraceFlags);
  1606. // Dump Flags
  1607. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1608. c_szIniKeyDumpFlags,
  1609. c_szNull,
  1610. szRHS,
  1611. SIZECHARS(szRHS),
  1612. c_szCcshellIniFile);
  1613. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1614. g_dwDumpFlags = (DWORD)val;
  1615. TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
  1616. c_szIniKeyDumpFlags, g_dwDumpFlags);
  1617. // Break Flags
  1618. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1619. c_szIniKeyBreakFlags,
  1620. c_szNull,
  1621. szRHS,
  1622. SIZECHARS(szRHS),
  1623. c_szCcshellIniFile);
  1624. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1625. g_dwBreakFlags = (DWORD)val;
  1626. #ifdef FULL_DEBUG
  1627. else
  1628. g_dwBreakFlags = 5; // default to break on ASSERT and TF_ERROR
  1629. #endif
  1630. TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
  1631. c_szIniKeyBreakFlags, g_dwBreakFlags);
  1632. // Prototype Flags
  1633. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1634. c_szIniKeyProtoFlags,
  1635. c_szNull,
  1636. szRHS,
  1637. SIZECHARS(szRHS),
  1638. c_szCcshellIniFile);
  1639. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1640. g_dwPrototype = (DWORD)val;
  1641. // Are we using the new leak detection from shelldbg.dll?
  1642. GetPrivateProfileStringA("ShellDbg",
  1643. "NewLeakDetection",
  1644. c_szNull,
  1645. szRHS,
  1646. SIZECHARS(szRHS),
  1647. c_szCcshellIniFile);
  1648. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1649. g_bUseNewLeakDetection = BOOLIFY(val);
  1650. TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
  1651. c_szIniKeyProtoFlags, g_dwPrototype);
  1652. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1653. "DebugOutputFile",
  1654. c_szNull,
  1655. szRHS,
  1656. SIZECHARS(szRHS),
  1657. c_szCcshellIniFile);
  1658. if (szRHS[0] != TEXT('\0'))
  1659. {
  1660. g_hDebugOutputFile = CreateFileA(szRHS, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1661. }
  1662. return TRUE;
  1663. }
  1664. // Function to call in allocspy.dll (GetShellMallocSpy)
  1665. typedef BOOL (__stdcall *pfnGSMS) (IShellMallocSpy **ppout);
  1666. STDAPI_(void) IMSAddToList(BOOL bAdd, void*pv, SIZE_T cb)
  1667. {
  1668. static BOOL bDontTry=FALSE;
  1669. static IShellMallocSpy *pms=NULL;
  1670. if (!bDontTry && pms == NULL)
  1671. {
  1672. pfnGSMS pfnGetShellMallocSpy;
  1673. HMODULE hmod;
  1674. bDontTry = TRUE; // assume failure
  1675. if (hmod = LoadLibraryA("ALLOCSPY.DLL"))
  1676. {
  1677. pfnGetShellMallocSpy = (pfnGSMS) GetProcAddress(hmod, "GetShellMallocSpy");
  1678. pfnGetShellMallocSpy(&pms);
  1679. }
  1680. }
  1681. if (bDontTry)
  1682. return;
  1683. if (bAdd)
  1684. pms->lpVtbl->AddToList(pms, pv, cb);
  1685. else
  1686. pms->lpVtbl->RemoveFromList(pms, pv);
  1687. }
  1688. #endif // DEBUG
  1689. #ifdef PRODUCT_PROF
  1690. DWORD g_dwProfileCAP = 0;
  1691. BOOL PUBLIC CcshellGetDebugFlags(void)
  1692. {
  1693. CHAR szRHS[MAX_PATH];
  1694. LONGLONG val;
  1695. GetPrivateProfileStringA(c_szCcshellIniSecDebug,
  1696. "Profile",
  1697. "",
  1698. szRHS,
  1699. SIZECHARS(szRHS),
  1700. c_szCcshellIniFile);
  1701. if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
  1702. g_dwProfileCAP = (DWORD)val;
  1703. return TRUE;
  1704. }
  1705. #endif // PRODUCT_PROF
  1706. #ifdef DEBUG
  1707. // turn on path whacking for full-debug builds
  1708. #ifdef FULL_DEBUG
  1709. static BOOL g_fWhackPathBuffers = TRUE;
  1710. #else
  1711. static BOOL g_fWhackPathBuffers = FALSE;
  1712. #endif
  1713. void DEBUGWhackPathBufferA(LPSTR psz, UINT cch)
  1714. {
  1715. if (g_fWhackPathBuffers)
  1716. {
  1717. if (psz && IS_VALID_WRITE_BUFFER(psz, char, cch))
  1718. {
  1719. FillMemory(psz, cch * sizeof(char), 0xFE);
  1720. }
  1721. }
  1722. }
  1723. void DEBUGWhackPathBufferW(LPWSTR psz, UINT cch)
  1724. {
  1725. if (g_fWhackPathBuffers)
  1726. {
  1727. if (psz && IS_VALID_WRITE_BUFFER(psz, WCHAR, cch))
  1728. {
  1729. FillMemory(psz, cch * sizeof(WCHAR), 0xFE);
  1730. }
  1731. }
  1732. }
  1733. void DEBUGWhackPathStringA(LPSTR psz, UINT cch)
  1734. {
  1735. if (g_fWhackPathBuffers)
  1736. {
  1737. if (psz && IS_VALID_WRITE_BUFFER(psz, char, cch) && IS_VALID_STRING_PTRA(psz, -1))
  1738. {
  1739. UINT len = lstrlenA(psz);
  1740. if (len >= cch)
  1741. {
  1742. TraceMsg(TF_WARNING, "DEBUGWhackPathStringA: caller of caller passed strange Path string (strlen > buffer size)");
  1743. }
  1744. else
  1745. {
  1746. FillMemory(psz+len+1, (cch-len-1) * sizeof(char), 0xFE);
  1747. }
  1748. }
  1749. }
  1750. }
  1751. void DEBUGWhackPathStringW(LPWSTR psz, UINT cch)
  1752. {
  1753. if (g_fWhackPathBuffers)
  1754. {
  1755. if (psz && IS_VALID_WRITE_BUFFER(psz, WCHAR, cch) && IS_VALID_STRING_PTRW(psz, -1))
  1756. {
  1757. UINT len = lstrlenW(psz);
  1758. if (len >= cch)
  1759. {
  1760. TraceMsg(TF_WARNING, "DEBUGWhackPathStringW: caller of caller passed strange Path string (strlen > buffer size)");
  1761. }
  1762. else
  1763. {
  1764. FillMemory(psz+len+1, (cch-len-1) * sizeof(WCHAR), 0xFE);
  1765. }
  1766. }
  1767. }
  1768. }
  1769. #endif // DEBUG