2185 lines
57 KiB
C
2185 lines
57 KiB
C
//
|
|
// Debug squirty functions
|
|
//
|
|
#include "stock.h"
|
|
#pragma hdrstop
|
|
|
|
#include "shellp.h"
|
|
|
|
#include <platform.h> // LINE_SEPARATOR_STR and friends
|
|
#include <winbase.h> // for GetModuleFileNameA
|
|
|
|
|
|
#define DM_DEBUG 0
|
|
|
|
#ifdef DEBUG
|
|
|
|
// --------------------------------------------------------------------------
|
|
// AttachUserModeDebugger
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Returns: <none>
|
|
//
|
|
// Purpose: Attaches a user-mode debugger to the current process if one
|
|
// is not already attached. This allows the ASSERT/RIP/TRACE to
|
|
// be debugged in a user-mode debugger rather than ending up in
|
|
// the kernel debugger which is sometimes limiting.
|
|
//
|
|
// History: 2000-08-21 vtan created
|
|
// --------------------------------------------------------------------------
|
|
|
|
void AttachUserModeDebugger (void)
|
|
|
|
{
|
|
// Win95 does not support IsDebuggerPresent so we must
|
|
// GetProcAddress for it below or we would fail to load.
|
|
// In fact, we punt completely on Win9x
|
|
if (GetVersion() & 0x80000000) return;
|
|
|
|
__try
|
|
{
|
|
typedef BOOL (WINAPI *ISDEBUGGERPRESENT)();
|
|
ISDEBUGGERPRESENT _IsDebuggerPresent = (ISDEBUGGERPRESENT)GetProcAddress(GetModuleHandleA("KERNEL32"), "IsDebuggerPresent");
|
|
if (_IsDebuggerPresent && !_IsDebuggerPresent())
|
|
{
|
|
HKEY hKeyAEDebug;
|
|
TCHAR szDebugger[MAX_PATH];
|
|
|
|
szDebugger[0] = TEXT('\0');
|
|
|
|
// Read HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AEDebug\Debugger
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AEDebug"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKeyAEDebug))
|
|
{
|
|
DWORD dwDebuggerSize;
|
|
|
|
dwDebuggerSize = sizeof(szDebugger);
|
|
(LONG)RegQueryValueEx(hKeyAEDebug,
|
|
TEXT("Debugger"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&szDebugger,
|
|
&dwDebuggerSize);
|
|
(LONG)RegCloseKey(hKeyAEDebug);
|
|
}
|
|
|
|
if (szDebugger[0] != TEXT('\0'))
|
|
{
|
|
static const TCHAR s_szDrWatsonImageName[] = TEXT("drwtsn32");
|
|
|
|
// We cannot use wnsprintf (comctl32 cannot link to shlwapi)
|
|
// so we need to make this buffer big enough that the wsprintf
|
|
// cannot overflow. We use a sprintf format string of size MAX_PATH
|
|
// and insert two integers, so 64 should be plenty of space.
|
|
TCHAR szCommandLine[MAX_PATH + 64];
|
|
|
|
// Duplicate the string to szCommandLine and NULL terminate
|
|
// to compare directly for "drwtsn32" which we do NOT want
|
|
// attached to the process.
|
|
|
|
lstrcpy(szCommandLine, szDebugger);
|
|
szCommandLine[ARRAYSIZE(s_szDrWatsonImageName) - 1] = TEXT('\0');
|
|
if (lstrcmpi(szCommandLine, s_szDrWatsonImageName) != 0)
|
|
{
|
|
HANDLE hEvent;
|
|
SECURITY_ATTRIBUTES securityAttributes;
|
|
|
|
// Create an unnamed event which is passed to the debugger as an
|
|
// inherited handle. It will signal this handle to release this
|
|
// thread when the debugger is completely attached to the process.
|
|
|
|
securityAttributes.nLength = sizeof(securityAttributes);
|
|
securityAttributes.lpSecurityDescriptor = NULL;
|
|
securityAttributes.bInheritHandle = TRUE;
|
|
hEvent = CreateEvent(&securityAttributes, TRUE, FALSE, NULL);
|
|
if (hEvent != NULL)
|
|
{
|
|
STARTUPINFO startupInfo;
|
|
PROCESS_INFORMATION processInformation;
|
|
|
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
ZeroMemory(&processInformation, sizeof(processInformation));
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
|
|
// Now formulate the command line to pass to the debugger.
|
|
// It's in the form "cdb -p %ld -e %ld -gGx".
|
|
|
|
wsprintf(szCommandLine, szDebugger, GetCurrentProcessId(), hEvent);
|
|
if (CreateProcess(NULL,
|
|
szCommandLine,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&startupInfo,
|
|
&processInformation) != FALSE)
|
|
{
|
|
|
|
// Now wait for the event. Give a generous 10 seconds
|
|
// for the debugger to signal this event. If the debugger
|
|
// doesn't then continue execution. Close all handles.
|
|
|
|
(DWORD)WaitForSingleObject(hEvent, INFINITE);
|
|
(BOOL)CloseHandle(processInformation.hProcess);
|
|
(BOOL)CloseHandle(processInformation.hThread);
|
|
}
|
|
(BOOL)CloseHandle(hEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputDebugStringA("drwtsn32 detected in AEDebug. Dropping to KD\r\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__except (EXCEPTION_CONTINUE_EXECUTION)
|
|
{
|
|
}
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
// UnicodeFromAnsi is used by shlwapi so it should not be inside the ifdef debug
|
|
//
|
|
/*----------------------------------------------------------
|
|
Purpose: This function converts a multi-byte string to a
|
|
wide-char string.
|
|
|
|
If pszBuf is non-NULL and the converted string can fit in
|
|
pszBuf, then *ppszWide will point to the given buffer.
|
|
Otherwise, this function will allocate a buffer that can
|
|
hold the converted string.
|
|
|
|
If pszAnsi is NULL, then *ppszWide will be freed. Note
|
|
that pszBuf must be the same pointer between the call
|
|
that converted the string and the call that frees the
|
|
string.
|
|
|
|
Returns: TRUE
|
|
FALSE (if out of memory)
|
|
|
|
*/
|
|
BOOL
|
|
UnicodeFromAnsi(
|
|
LPWSTR * ppwszWide,
|
|
LPCSTR pszAnsi, // NULL to clean up
|
|
LPWSTR pwszBuf,
|
|
int cchBuf)
|
|
{
|
|
BOOL bRet;
|
|
|
|
// Convert the string?
|
|
if (pszAnsi)
|
|
{
|
|
// Yes; determine the converted string length
|
|
int cch;
|
|
LPWSTR pwsz;
|
|
int cchAnsi = lstrlenA(pszAnsi)+1;
|
|
|
|
cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, NULL, 0);
|
|
|
|
// String too big, or is there no buffer?
|
|
if (cch > cchBuf || NULL == pwszBuf)
|
|
{
|
|
// Yes; allocate space
|
|
cchBuf = cch + 1;
|
|
pwsz = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cchBuf));
|
|
}
|
|
else
|
|
{
|
|
// No; use the provided buffer
|
|
pwsz = pwszBuf;
|
|
}
|
|
|
|
if (pwsz)
|
|
{
|
|
// Convert the string
|
|
cch = MultiByteToWideChar(CP_ACP, 0, pszAnsi, cchAnsi, pwsz, cchBuf);
|
|
bRet = (0 < cch);
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
*ppwszWide = pwsz;
|
|
}
|
|
else
|
|
{
|
|
// No; was this buffer allocated?
|
|
if (*ppwszWide && pwszBuf != *ppwszWide)
|
|
{
|
|
// Yes; clean up
|
|
LocalFree((HLOCAL)*ppwszWide);
|
|
*ppwszWide = NULL;
|
|
}
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#if 0 // nobody seems to use this one any more
|
|
/*----------------------------------------------------------
|
|
Purpose: This function converts a wide-char string to a multi-byte
|
|
string.
|
|
|
|
If pszBuf is non-NULL and the converted string can fit in
|
|
pszBuf, then *ppszAnsi will point to the given buffer.
|
|
Otherwise, this function will allocate a buffer that can
|
|
hold the converted string.
|
|
|
|
If pszWide is NULL, then *ppszAnsi will be freed. Note
|
|
that pszBuf must be the same pointer between the call
|
|
that converted the string and the call that frees the
|
|
string.
|
|
|
|
Returns: TRUE
|
|
FALSE (if out of memory)
|
|
|
|
Cond: --
|
|
*/
|
|
BOOL
|
|
AnsiFromUnicode(
|
|
LPSTR * ppszAnsi,
|
|
LPCWSTR pwszWide, // NULL to clean up
|
|
LPSTR pszBuf,
|
|
int cchBuf)
|
|
{
|
|
BOOL bRet;
|
|
|
|
// Convert the string?
|
|
if (pwszWide)
|
|
{
|
|
// Yes; determine the converted string length
|
|
int cch;
|
|
LPSTR psz;
|
|
|
|
cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, NULL, 0, NULL, NULL);
|
|
|
|
// String too big, or is there no buffer?
|
|
if (cch > cchBuf || NULL == pszBuf)
|
|
{
|
|
// Yes; allocate space
|
|
cchBuf = cch + 1;
|
|
psz = (LPSTR)LocalAlloc(LPTR, CbFromCchA(cchBuf));
|
|
}
|
|
else
|
|
{
|
|
// No; use the provided buffer
|
|
ASSERT(pszBuf);
|
|
psz = pszBuf;
|
|
}
|
|
|
|
if (psz)
|
|
{
|
|
// Convert the string
|
|
cch = WideCharToMultiByte(CP_ACP, 0, pwszWide, -1, psz, cchBuf, NULL, NULL);
|
|
bRet = (0 < cch);
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
*ppszAnsi = psz;
|
|
}
|
|
else
|
|
{
|
|
// No; was this buffer allocated?
|
|
if (*ppszAnsi && pszBuf != *ppszAnsi)
|
|
{
|
|
// Yes; clean up
|
|
LocalFree((HLOCAL)*ppszAnsi);
|
|
*ppszAnsi = NULL;
|
|
}
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
#endif // 0
|
|
|
|
|
|
#if defined(DEBUG) || defined(PRODUCT_PROF)
|
|
// (c_szCcshellIniFile and c_szCcshellIniSecDebug are declared in debug.h)
|
|
extern CHAR const FAR c_szCcshellIniFile[];
|
|
extern CHAR const FAR c_szCcshellIniSecDebug[];
|
|
HANDLE g_hDebugOutputFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
void ShellDebugAppendToDebugFileA(LPCSTR pszOutputString)
|
|
{
|
|
if (g_hDebugOutputFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD cbWrite = lstrlenA(pszOutputString);
|
|
WriteFile(g_hDebugOutputFile, pszOutputString, cbWrite, &cbWrite, NULL);
|
|
}
|
|
}
|
|
|
|
void ShellDebugAppendToDebugFileW(LPCWSTR pszOutputString)
|
|
{
|
|
if (g_hDebugOutputFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
char szBuf[500];
|
|
|
|
DWORD cbWrite = WideCharToMultiByte(CP_ACP, 0, pszOutputString, -1, szBuf, ARRAYSIZE(szBuf), NULL, NULL);
|
|
|
|
WriteFile(g_hDebugOutputFile, szBuf, cbWrite, &cbWrite, NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We cannot link to shlwapi, because comctl32 cannot link to shlwapi.
|
|
// Duplicate some functions here so unicode stuff can run on Win95 platforms.
|
|
//
|
|
VOID MyOutputDebugStringWrapW(LPCWSTR lpOutputString)
|
|
{
|
|
if (staticIsOS(OS_NT))
|
|
{
|
|
OutputDebugStringW(lpOutputString);
|
|
ShellDebugAppendToDebugFileW(lpOutputString);
|
|
}
|
|
else
|
|
{
|
|
char szBuf[500];
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, szBuf, ARRAYSIZE(szBuf), NULL, NULL);
|
|
|
|
OutputDebugStringA(szBuf);
|
|
ShellDebugAppendToDebugFileA(szBuf);
|
|
}
|
|
}
|
|
#define OutputDebugStringW MyOutputDebugStringWrapW
|
|
|
|
VOID MyOutputDebugStringWrapA(LPCSTR lpOutputString)
|
|
{
|
|
OutputDebugStringA(lpOutputString);
|
|
ShellDebugAppendToDebugFileA(lpOutputString);
|
|
}
|
|
|
|
#define OutputDebugStringA MyOutputDebugStringWrapA
|
|
|
|
LPWSTR MyCharPrevWrapW(LPCWSTR lpszStart, LPCWSTR lpszCurrent)
|
|
{
|
|
if (lpszCurrent == lpszStart)
|
|
{
|
|
return (LPWSTR) lpszStart;
|
|
}
|
|
else
|
|
{
|
|
return (LPWSTR) lpszCurrent - 1;
|
|
}
|
|
}
|
|
#define CharPrevW MyCharPrevWrapW
|
|
|
|
int MywvsprintfWrapW(LPWSTR pwszOut, LPCWSTR pwszFormat, va_list arglist)
|
|
{
|
|
if (staticIsOS(OS_NT))
|
|
{
|
|
return wvsprintfW(pwszOut, pwszFormat, arglist);
|
|
}
|
|
else
|
|
{
|
|
char szFormat[500];
|
|
char szOut[1024+40]; // this is how big our ach buffers are
|
|
int iRet;
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pwszFormat, -1, szFormat, ARRAYSIZE(szFormat), NULL, NULL);
|
|
|
|
iRet = wvsprintfA(szOut, szFormat, arglist);
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, szOut, -1, pwszOut, 1024+40);
|
|
|
|
return iRet;
|
|
}
|
|
}
|
|
|
|
#define wvsprintfW MywvsprintfWrapW
|
|
|
|
int MywsprintfWrapW(LPWSTR pwszOut, LPCWSTR pwszFormat, ...)
|
|
{
|
|
int iRet;
|
|
|
|
va_list ArgList;
|
|
va_start(ArgList, pwszFormat);
|
|
|
|
iRet = MywvsprintfWrapW(pwszOut, pwszFormat, ArgList);
|
|
|
|
va_end(ArgList);
|
|
|
|
return iRet;
|
|
}
|
|
#define wsprintfW MywsprintfWrapW
|
|
|
|
LPWSTR lstrcpyWrapW(LPWSTR pszDst, LPCWSTR pszSrc)
|
|
{
|
|
while((*pszDst++ = *pszSrc++));
|
|
|
|
return pszDst;
|
|
}
|
|
#define lstrcpyW lstrcpyWrapW
|
|
|
|
LPWSTR lstrcatWrapW(LPWSTR pszDst, LPCWSTR pszSrc)
|
|
{
|
|
return lstrcpyWrapW(pszDst + lstrlenW(pszDst), pszSrc);
|
|
}
|
|
#define lstrcatW lstrcatWrapW
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Special verion of atoi. Supports hexadecimal too.
|
|
|
|
If this function returns FALSE, *piRet is set to 0.
|
|
|
|
Returns: TRUE if the string is a number, or contains a partial number
|
|
FALSE if the string is not a number
|
|
|
|
Cond: --
|
|
*/
|
|
static
|
|
BOOL
|
|
MyStrToIntExA(
|
|
LPCSTR pszString,
|
|
DWORD dwFlags, // STIF_ bitfield
|
|
LONGLONG FAR * piRet)
|
|
{
|
|
#define IS_DIGIT(ch) InRange(ch, '0', '9')
|
|
|
|
BOOL bRet;
|
|
LONGLONG n;
|
|
BOOL bNeg = FALSE;
|
|
LPCSTR psz;
|
|
LPCSTR pszAdj;
|
|
|
|
// Skip leading whitespace
|
|
//
|
|
for (psz = pszString; *psz == ' ' || *psz == '\n' || *psz == '\t'; psz = CharNextA(psz))
|
|
;
|
|
|
|
// Determine possible explicit signage
|
|
//
|
|
if (*psz == '+' || *psz == '-')
|
|
{
|
|
bNeg = (*psz == '+') ? FALSE : TRUE;
|
|
psz++;
|
|
}
|
|
|
|
// Or is this hexadecimal?
|
|
//
|
|
pszAdj = CharNextA(psz);
|
|
if ((STIF_SUPPORT_HEX & dwFlags) &&
|
|
*psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
|
|
{
|
|
// Yes
|
|
|
|
// (Never allow negative sign with hexadecimal numbers)
|
|
bNeg = FALSE;
|
|
psz = CharNextA(pszAdj);
|
|
|
|
pszAdj = psz;
|
|
|
|
// Do the conversion
|
|
//
|
|
for (n = 0; ; psz = CharNextA(psz))
|
|
{
|
|
if (IS_DIGIT(*psz))
|
|
n = 0x10 * n + *psz - '0';
|
|
else
|
|
{
|
|
CHAR ch = *psz;
|
|
int n2;
|
|
|
|
if (ch >= 'a')
|
|
ch -= 'a' - 'A';
|
|
|
|
n2 = ch - 'A' + 0xA;
|
|
if (n2 >= 0xA && n2 <= 0xF)
|
|
n = 0x10 * n + n2;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return TRUE if there was at least one digit
|
|
bRet = (psz != pszAdj);
|
|
}
|
|
else
|
|
{
|
|
// No
|
|
pszAdj = psz;
|
|
|
|
// Do the conversion
|
|
for (n = 0; IS_DIGIT(*psz); psz = CharNextA(psz))
|
|
n = 10 * n + *psz - '0';
|
|
|
|
// Return TRUE if there was at least one digit
|
|
bRet = (psz != pszAdj);
|
|
}
|
|
|
|
*piRet = bNeg ? -n : n;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
EXTERN_C g_bUseNewLeakDetection = FALSE;
|
|
|
|
DWORD g_dwDumpFlags = 0; // DF_*
|
|
|
|
#ifdef FULL_DEBUG
|
|
ULONGLONG g_qwTraceFlags = TF_ERROR | TF_WARNING; // TF_*
|
|
DWORD g_dwBreakFlags = BF_ASSERT | BF_ONERRORMSG; // BF_*
|
|
#else
|
|
ULONGLONG g_qwTraceFlags = TF_ERROR; // TF_*
|
|
DWORD g_dwBreakFlags = BF_ASSERT; // BF_*
|
|
#endif
|
|
|
|
DWORD g_dwPrototype = 0;
|
|
DWORD g_dwFuncTraceFlags = 0; // FTF_*
|
|
|
|
// TLS slot used to store depth for CcshellFuncMsg indentation
|
|
|
|
static DWORD g_tlsStackDepth = TLS_OUT_OF_INDEXES;
|
|
|
|
// Hack stack depth counter used when g_tlsStackDepth is not available
|
|
|
|
static DWORD g_dwHackStackDepth = 0;
|
|
|
|
static char g_szIndentLeader[] = " ";
|
|
|
|
static WCHAR g_wszIndentLeader[] = L" ";
|
|
|
|
|
|
static CHAR const FAR c_szNewline[] = LINE_SEPARATOR_STR; // (Deliberately CHAR)
|
|
static WCHAR const FAR c_wszNewline[] = TEXTW(LINE_SEPARATOR_STR);
|
|
|
|
extern CHAR const FAR c_szTrace[]; // (Deliberately CHAR)
|
|
extern CHAR const FAR c_szErrorDbg[]; // (Deliberately CHAR)
|
|
extern CHAR const FAR c_szWarningDbg[]; // (Deliberately CHAR)
|
|
extern WCHAR const FAR c_wszTrace[];
|
|
extern WCHAR const FAR c_wszErrorDbg[];
|
|
extern WCHAR const FAR c_wszWarningDbg[];
|
|
|
|
extern const CHAR FAR c_szAssertMsg[];
|
|
extern CHAR const FAR c_szAssertFailed[];
|
|
extern const WCHAR FAR c_wszAssertMsg[];
|
|
extern WCHAR const FAR c_wszAssertFailed[];
|
|
|
|
extern CHAR const FAR c_szRip[];
|
|
extern CHAR const FAR c_szRipNoFn[];
|
|
extern CHAR const FAR c_szRipMsg[];
|
|
extern WCHAR const FAR c_wszRip[];
|
|
extern WCHAR const FAR c_wszRipNoFn[];
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Adds one of the following prefix strings to pszBuf:
|
|
"t MODULE "
|
|
"err MODULE "
|
|
"wrn MODULE "
|
|
|
|
Returns the count of characters written.
|
|
*/
|
|
int
|
|
SetPrefixStringA(
|
|
OUT LPSTR pszBuf,
|
|
IN ULONGLONG qwFlags)
|
|
{
|
|
if (TF_ALWAYS == qwFlags)
|
|
lstrcpyA(pszBuf, c_szTrace);
|
|
else if (IsFlagSet(qwFlags, TF_WARNING))
|
|
lstrcpyA(pszBuf, c_szWarningDbg);
|
|
else if (IsFlagSet(qwFlags, TF_ERROR))
|
|
lstrcpyA(pszBuf, c_szErrorDbg);
|
|
else
|
|
lstrcpyA(pszBuf, c_szTrace);
|
|
return lstrlenA(pszBuf);
|
|
}
|
|
|
|
|
|
int
|
|
SetPrefixStringW(
|
|
OUT LPWSTR pszBuf,
|
|
IN ULONGLONG qwFlags)
|
|
{
|
|
if (TF_ALWAYS == qwFlags)
|
|
lstrcpyW(pszBuf, c_wszTrace);
|
|
else if (IsFlagSet(qwFlags, TF_WARNING))
|
|
lstrcpyW(pszBuf, c_wszWarningDbg);
|
|
else if (IsFlagSet(qwFlags, TF_ERROR))
|
|
lstrcpyW(pszBuf, c_wszErrorDbg);
|
|
else
|
|
lstrcpyW(pszBuf, c_wszTrace);
|
|
return lstrlenW(pszBuf);
|
|
}
|
|
|
|
|
|
static
|
|
LPCSTR
|
|
_PathFindFileNameA(
|
|
LPCSTR pPath)
|
|
{
|
|
LPCSTR pT;
|
|
|
|
for (pT = pPath; *pPath; pPath = CharNextA(pPath)) {
|
|
if ((pPath[0] == '\\' || pPath[0] == ':' || pPath[0] == '/')
|
|
&& pPath[1] && pPath[1] != '\\' && pPath[1] != '/')
|
|
pT = pPath + 1;
|
|
}
|
|
|
|
return pT;
|
|
}
|
|
|
|
|
|
static
|
|
LPCWSTR
|
|
_PathFindFileNameW(
|
|
LPCWSTR pPath)
|
|
{
|
|
LPCWSTR pT;
|
|
|
|
for (pT = pPath; *pPath; pPath++) {
|
|
if ((pPath[0] == TEXTW('\\') || pPath[0] == TEXTW(':') || pPath[0] == TEXTW('/'))
|
|
&& pPath[1] && pPath[1] != TEXTW('\\') && pPath[1] != TEXTW('/'))
|
|
pT = pPath + 1;
|
|
}
|
|
|
|
return pT;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Returns TRUE if this process is a shell-related process.
|
|
*/
|
|
BOOL _IsShellProcess()
|
|
{
|
|
CHAR szModuleName[MAX_PATH];
|
|
|
|
if (GetModuleFileNameA(NULL, szModuleName, sizeof(CHAR) * MAX_PATH) > 0 )
|
|
{
|
|
if (StrStrIA(szModuleName, "explorer.exe") ||
|
|
StrStrIA(szModuleName, "iexplore.exe") ||
|
|
StrStrIA(szModuleName, "rundll32.exe") ||
|
|
StrStrIA(szModuleName, "mshtmpad.exe"))
|
|
{
|
|
// yes, the exe is a shell one
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// not a normal shell executable
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// FEATURE (scotth): Use the Ccshell functions. _AssertMsg and
|
|
// _DebugMsg are obsolete. They will be removed once all the
|
|
// components don't have TEXT() wrapping their debug strings anymore.
|
|
|
|
|
|
void
|
|
WINCAPI
|
|
_AssertMsgA(
|
|
BOOL f,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40];
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
int cch;
|
|
|
|
lstrcpyA(ach, c_szAssertMsg);
|
|
cch = lstrlenA(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
WINCAPI
|
|
_AssertMsgW(
|
|
BOOL f,
|
|
LPCWSTR pszMsg, ...)
|
|
{
|
|
WCHAR ach[1024+40];
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
int cch;
|
|
|
|
lstrcpyW(ach, c_wszAssertMsg);
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
wvsprintfW(&ach[cch], pszMsg, vArgs);
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
|
|
OutputDebugStringW(c_wszNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
_AssertStrLenW(
|
|
LPCWSTR pszStr,
|
|
int iLen)
|
|
{
|
|
if (pszStr && iLen < lstrlenW(pszStr))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
|
|
void
|
|
_AssertStrLenA(
|
|
LPCSTR pszStr,
|
|
int iLen)
|
|
{
|
|
if (pszStr && iLen < lstrlenA(pszStr))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
|
|
void
|
|
WINCAPI
|
|
_DebugMsgA(
|
|
ULONGLONG flag,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[5*MAX_PATH+40]; // Handles 5*largest path + slop for message
|
|
va_list vArgs;
|
|
|
|
if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, flag);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
try
|
|
{
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
OutputDebugString(TEXT("CCSHELL: DebugMsg exception: "));
|
|
OutputDebugStringA(pszMsg);
|
|
}
|
|
__endexcept
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (TF_ALWAYS != flag &&
|
|
((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
|
|
(flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
WINCAPI
|
|
_DebugMsgW(
|
|
ULONGLONG flag,
|
|
LPCWSTR pszMsg, ...)
|
|
{
|
|
WCHAR ach[5*MAX_PATH+40]; // Handles 5*largest path + slop for message
|
|
va_list vArgs;
|
|
|
|
if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
|
|
{
|
|
int cch;
|
|
|
|
SetPrefixStringW(ach, flag);
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
try
|
|
{
|
|
wvsprintfW(&ach[cch], pszMsg, vArgs);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
OutputDebugString(TEXT("CCSHELL: DebugMsg exception: "));
|
|
OutputDebugStringW(pszMsg);
|
|
}
|
|
__endexcept
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
OutputDebugStringW(c_wszNewline);
|
|
|
|
if (TF_ALWAYS != flag &&
|
|
((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
|
|
(flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Smart debug functions
|
|
//
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Displays assertion string.
|
|
|
|
Returns: TRUE to debugbreak
|
|
*/
|
|
BOOL
|
|
CcshellAssertFailedA(
|
|
LPCSTR pszFile,
|
|
int line,
|
|
LPCSTR pszEval,
|
|
BOOL bBreakInside)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPCSTR psz;
|
|
CHAR ach[256];
|
|
|
|
psz = _PathFindFileNameA(pszFile);
|
|
wsprintfA(ach, c_szAssertFailed, psz, line, pszEval);
|
|
OutputDebugStringA(ach);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
if (bBreakInside)
|
|
{
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
}
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Displays assertion string.
|
|
|
|
*/
|
|
BOOL
|
|
CcshellAssertFailedW(
|
|
LPCWSTR pszFile,
|
|
int line,
|
|
LPCWSTR pszEval,
|
|
BOOL bBreakInside)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPCWSTR psz;
|
|
WCHAR ach[1024]; // Some callers use more than 256
|
|
|
|
psz = _PathFindFileNameW(pszFile);
|
|
|
|
// If psz == NULL, CharPrevW failed which implies we are running on Win95. We can get this
|
|
// if we get an assert in some of the W functions in shlwapi... Call the A version of assert...
|
|
if (!psz)
|
|
{
|
|
char szFile[MAX_PATH];
|
|
char szEval[256]; // since the total output is thhis size should be enough...
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pszFile, -1, szFile, ARRAYSIZE(szFile), NULL, NULL);
|
|
WideCharToMultiByte(CP_ACP, 0, pszEval, -1, szEval, ARRAYSIZE(szEval), NULL, NULL);
|
|
return CcshellAssertFailedA(szFile, line, szEval, bBreakInside);
|
|
}
|
|
|
|
wsprintfW(ach, c_wszAssertFailed, psz, line, pszEval);
|
|
OutputDebugStringW(ach);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
if (bBreakInside)
|
|
{
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
}
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Displays a RIP string.
|
|
|
|
Returns: TRUE to debugbreak
|
|
*/
|
|
BOOL
|
|
CcshellRipA(
|
|
LPCSTR pszFile,
|
|
int line,
|
|
LPCSTR pszEval,
|
|
BOOL bBreakInside)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPCSTR psz;
|
|
CHAR ach[256];
|
|
|
|
psz = _PathFindFileNameA(pszFile);
|
|
wsprintfA(ach, c_szRipNoFn, psz, line, pszEval);
|
|
OutputDebugStringA(ach);
|
|
|
|
if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
|
|
{
|
|
if (bBreakInside)
|
|
{
|
|
// !!! RIP !!!! RIP !!!! RIP !!!
|
|
|
|
// MSDEV USERS: This is not the real RIP. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! RIP !!!! RIP !!!! RIP !!!
|
|
}
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Displays a RIP string.
|
|
|
|
*/
|
|
BOOL
|
|
CcshellRipW(
|
|
LPCWSTR pszFile,
|
|
int line,
|
|
LPCWSTR pszEval,
|
|
BOOL bBreakInside)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPCWSTR psz;
|
|
WCHAR ach[256];
|
|
|
|
psz = _PathFindFileNameW(pszFile);
|
|
|
|
// If psz == NULL, CharPrevW failed which implies we are running on Win95.
|
|
// We can get this if we get an assert in some of the W functions in
|
|
// shlwapi... Call the A version of assert...
|
|
if (!psz)
|
|
{
|
|
char szFile[MAX_PATH];
|
|
char szEval[256]; // since the total output is thhis size should be enough...
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, pszFile, -1, szFile, ARRAYSIZE(szFile), NULL, NULL);
|
|
WideCharToMultiByte(CP_ACP, 0, pszEval, -1, szEval, ARRAYSIZE(szEval), NULL, NULL);
|
|
return CcshellRipA(szFile, line, szEval, bBreakInside);
|
|
}
|
|
|
|
wsprintfW(ach, c_wszRipNoFn, psz, line, pszEval);
|
|
OutputDebugStringW(ach);
|
|
|
|
if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
|
|
{
|
|
if (bBreakInside)
|
|
{
|
|
// !!! RIP !!!! RIP !!!! RIP !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! RIP !!!! RIP !!!! RIP !!!
|
|
}
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL
|
|
WINCAPI
|
|
CcshellRipMsgA(
|
|
BOOL f,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40];
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
OutputDebugStringA(c_szRipMsg);
|
|
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfA(ach, pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
WINCAPI
|
|
CcshellRipMsgW(
|
|
BOOL f,
|
|
LPCSTR pszMsg, ...) // (this is deliberately CHAR)
|
|
{
|
|
WCHAR ach[1024+40];
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
LPWSTR pwsz;
|
|
WCHAR wszBuf[128];
|
|
OutputDebugStringA(c_szRipMsg);
|
|
|
|
// (We convert the string, rather than simply input an
|
|
// LPCWSTR parameter, so the caller doesn't have to wrap
|
|
// all the string constants with the TEXT() macro.)
|
|
|
|
ach[0] = L'\0'; // In case this fails
|
|
if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
|
|
{
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfW(ach, pwsz, vArgs);
|
|
va_end(vArgs);
|
|
UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
|
|
}
|
|
|
|
OutputDebugStringW(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (_IsShellProcess() || IsFlagSet(g_dwBreakFlags, BF_RIP))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Keep track of the stack depth for function call trace
|
|
messages.
|
|
|
|
*/
|
|
void
|
|
CcshellStackEnter(void)
|
|
{
|
|
if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
|
|
{
|
|
DWORD dwDepth;
|
|
|
|
dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
|
|
|
|
TlsSetValue(g_tlsStackDepth, (LPVOID)(ULONG_PTR)(dwDepth + 1));
|
|
}
|
|
else
|
|
{
|
|
g_dwHackStackDepth++;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Keep track of the stack depth for functionc all trace
|
|
messages.
|
|
|
|
*/
|
|
void
|
|
CcshellStackLeave(void)
|
|
{
|
|
if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
|
|
{
|
|
DWORD dwDepth;
|
|
|
|
dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
|
|
|
|
if (EVAL(0 < dwDepth))
|
|
{
|
|
EVAL(TlsSetValue(g_tlsStackDepth, (LPVOID)(ULONG_PTR)(dwDepth - 1)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (EVAL(0 < g_dwHackStackDepth))
|
|
{
|
|
g_dwHackStackDepth--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Return the stack depth.
|
|
|
|
*/
|
|
static
|
|
DWORD
|
|
CcshellGetStackDepth(void)
|
|
{
|
|
DWORD dwDepth;
|
|
|
|
if (TLS_OUT_OF_INDEXES != g_tlsStackDepth)
|
|
{
|
|
dwDepth = PtrToUlong(TlsGetValue(g_tlsStackDepth));
|
|
}
|
|
else
|
|
{
|
|
dwDepth = g_dwHackStackDepth;
|
|
}
|
|
|
|
return dwDepth;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Wide-char version of CcshellAssertMsgA
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellAssertMsgW(
|
|
BOOL f,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
WCHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
int cch;
|
|
WCHAR wszBuf[1024];
|
|
LPWSTR pwsz;
|
|
|
|
lstrcpyW(ach, c_wszAssertMsg);
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
// (We convert the string, rather than simply input an
|
|
// LPCWSTR parameter, so the caller doesn't have to wrap
|
|
// all the string constants with the TEXT() macro.)
|
|
|
|
if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
|
|
{
|
|
wvsprintfW(&ach[cch], pwsz, vArgs);
|
|
UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
|
|
}
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
OutputDebugStringW(c_wszNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Wide-char version of CcshellDebugMsgA. Note this
|
|
function deliberately takes an ANSI format string
|
|
so our trace messages don't all need to be wrapped
|
|
in TEXT().
|
|
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellDebugMsgW(
|
|
ULONGLONG flag,
|
|
LPCSTR pszMsg, ...) // (this is deliberately CHAR)
|
|
{
|
|
WCHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
|
|
{
|
|
int cch;
|
|
WCHAR wszBuf[1024];
|
|
LPWSTR pwsz;
|
|
|
|
SetPrefixStringW(ach, flag);
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
// (We convert the string, rather than simply input an
|
|
// LPCWSTR parameter, so the caller doesn't have to wrap
|
|
// all the string constants with the TEXT() macro.)
|
|
|
|
if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
|
|
{
|
|
wvsprintfW(&ach[cch], pwsz, vArgs);
|
|
UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
|
|
}
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
OutputDebugStringW(c_wszNewline);
|
|
|
|
if (TF_ALWAYS != flag &&
|
|
((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
|
|
(flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Since the ATL code does not pass in a flag parameter,
|
|
we'll hardcode and check for TF_ATL.
|
|
*/
|
|
void CDECL ShellAtlTraceW(LPCWSTR pszMsg, ...)
|
|
{
|
|
WCHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (g_qwTraceFlags & TF_ATL)
|
|
{
|
|
int cch;
|
|
|
|
SetPrefixStringW(ach, TF_ATL);
|
|
lstrcatW(ach, L"(ATL) ");
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfW(&ach[cch], pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Wide-char version of CcshellFuncMsgA. Note this
|
|
function deliberately takes an ANSI format string
|
|
so our trace messages don't all need to be wrapped
|
|
in TEXT().
|
|
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellFuncMsgW(
|
|
ULONGLONG flag,
|
|
LPCSTR pszMsg, ...) // (this is deliberately CHAR)
|
|
{
|
|
WCHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (IsFlagSet(g_qwTraceFlags, TF_FUNC) &&
|
|
IsFlagSet(g_dwFuncTraceFlags, flag))
|
|
{
|
|
int cch;
|
|
WCHAR wszBuf[1024];
|
|
LPWSTR pwsz;
|
|
DWORD dwStackDepth;
|
|
LPWSTR pszLeaderEnd;
|
|
WCHAR chSave;
|
|
|
|
// Determine the indentation for trace message based on
|
|
// stack depth.
|
|
|
|
dwStackDepth = CcshellGetStackDepth();
|
|
|
|
if (dwStackDepth < SIZECHARS(g_szIndentLeader))
|
|
{
|
|
pszLeaderEnd = &g_wszIndentLeader[dwStackDepth];
|
|
}
|
|
else
|
|
{
|
|
pszLeaderEnd = &g_wszIndentLeader[SIZECHARS(g_wszIndentLeader)-1];
|
|
}
|
|
|
|
chSave = *pszLeaderEnd;
|
|
*pszLeaderEnd = '\0';
|
|
|
|
wsprintfW(ach, L"%s %s", c_wszTrace, g_wszIndentLeader);
|
|
*pszLeaderEnd = chSave;
|
|
|
|
// Compose remaining string
|
|
|
|
cch = lstrlenW(ach);
|
|
va_start(vArgs, pszMsg);
|
|
|
|
// (We convert the string, rather than simply input an
|
|
// LPCWSTR parameter, so the caller doesn't have to wrap
|
|
// all the string constants with the TEXT() macro.)
|
|
|
|
if (UnicodeFromAnsi(&pwsz, pszMsg, wszBuf, SIZECHARS(wszBuf)))
|
|
{
|
|
wvsprintfW(&ach[cch], pwsz, vArgs);
|
|
UnicodeFromAnsi(&pwsz, NULL, wszBuf, 0);
|
|
}
|
|
|
|
va_end(vArgs);
|
|
OutputDebugStringW(ach);
|
|
OutputDebugStringW(c_wszNewline);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Assert failed message only
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellAssertMsgA(
|
|
BOOL f,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (!f)
|
|
{
|
|
int cch;
|
|
|
|
lstrcpyA(ach, c_szAssertMsg);
|
|
cch = lstrlenA(ach);
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_ASSERT))
|
|
{
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! ASSERT !!!! ASSERT !!!! ASSERT !!!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Debug spew
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellDebugMsgA(
|
|
ULONGLONG flag,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (TF_ALWAYS == flag || (IsFlagSet(g_qwTraceFlags, flag) && flag))
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, flag);
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (TF_ALWAYS != flag &&
|
|
((flag & TF_ERROR) && IsFlagSet(g_dwBreakFlags, BF_ONERRORMSG) ||
|
|
(flag & TF_WARNING) && IsFlagSet(g_dwBreakFlags, BF_ONWARNMSG)))
|
|
{
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Since the ATL code does not pass in a flag parameter,
|
|
we'll hardcode and check for TF_ATL.
|
|
*/
|
|
void CDECL ShellAtlTraceA(LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (g_qwTraceFlags & TF_ATL)
|
|
{
|
|
int cch;
|
|
|
|
SetPrefixStringA(ach, TF_ATL);
|
|
lstrcatA(ach, "(ATL) ");
|
|
cch = lstrlenA(ach);
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Debug spew for function trace calls
|
|
*/
|
|
void
|
|
CDECL
|
|
CcshellFuncMsgA(
|
|
ULONGLONG flag,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
va_list vArgs;
|
|
|
|
if (IsFlagSet(g_qwTraceFlags, TF_FUNC) &&
|
|
IsFlagSet(g_dwFuncTraceFlags, flag))
|
|
{
|
|
int cch;
|
|
DWORD dwStackDepth;
|
|
LPSTR pszLeaderEnd;
|
|
CHAR chSave;
|
|
|
|
// Determine the indentation for trace message based on
|
|
// stack depth.
|
|
|
|
dwStackDepth = CcshellGetStackDepth();
|
|
|
|
if (dwStackDepth < sizeof(g_szIndentLeader))
|
|
{
|
|
pszLeaderEnd = &g_szIndentLeader[dwStackDepth];
|
|
}
|
|
else
|
|
{
|
|
pszLeaderEnd = &g_szIndentLeader[sizeof(g_szIndentLeader)-1];
|
|
}
|
|
|
|
chSave = *pszLeaderEnd;
|
|
*pszLeaderEnd = '\0';
|
|
|
|
wsprintfA(ach, "%s %s", c_szTrace, g_szIndentLeader);
|
|
*pszLeaderEnd = chSave;
|
|
|
|
// Compose remaining string
|
|
|
|
cch = lstrlenA(ach);
|
|
va_start(vArgs, pszMsg);
|
|
wvsprintfA(&ach[cch], pszMsg, vArgs);
|
|
va_end(vArgs);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Spews a trace message if hrTest is a failure code.
|
|
*/
|
|
HRESULT
|
|
TraceHR(
|
|
HRESULT hrTest,
|
|
LPCSTR pszExpr,
|
|
LPCSTR pszFile,
|
|
int iLine)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
|
|
if (g_qwTraceFlags & TF_WARNING &&
|
|
FAILED(hrTest))
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, TF_WARNING);
|
|
wsprintfA(&ach[cch], "THR: Failure of \"%s\" at %s, line %d (%#08lx)",
|
|
pszExpr, _PathFindFileNameA(pszFile), iLine, hrTest);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_THR))
|
|
{
|
|
// !!! THR !!!! THR !!!! THR !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! THR !!!! THR !!!! THR !!!
|
|
}
|
|
}
|
|
return hrTest;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Spews a trace message if bTest is false.
|
|
*/
|
|
BOOL
|
|
TraceBool(
|
|
BOOL bTest,
|
|
LPCSTR pszExpr,
|
|
LPCSTR pszFile,
|
|
int iLine)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
|
|
if (g_qwTraceFlags & TF_WARNING && !bTest)
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, TF_WARNING);
|
|
wsprintfA(&ach[cch], "TBOOL: Failure of \"%s\" at %s, line %d",
|
|
pszExpr, _PathFindFileNameA(pszFile), iLine);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_THR))
|
|
{
|
|
// !!! TBOOL !!!! TBOOL !!!! TBOOL !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! TBOOL !!!! TBOOL !!!! TBOOL !!!
|
|
}
|
|
}
|
|
return bTest;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Spews a trace message if iTest is -1.
|
|
*/
|
|
int
|
|
TraceInt(
|
|
int iTest,
|
|
LPCSTR pszExpr,
|
|
LPCSTR pszFile,
|
|
int iLine)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
|
|
if (g_qwTraceFlags & TF_WARNING && -1 == iTest)
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, TF_WARNING);
|
|
wsprintfA(&ach[cch], "TINT: Failure of \"%s\" at %s, line %d",
|
|
pszExpr, _PathFindFileNameA(pszFile), iLine);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_THR))
|
|
{
|
|
// !!! TINT !!!! TINT !!!! TINT !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! TINT !!!! TINT !!!! TINT !!!
|
|
}
|
|
}
|
|
return iTest;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Spews a trace message if pvTest is NULL.
|
|
*/
|
|
LPVOID
|
|
TracePtr(
|
|
LPVOID pvTest,
|
|
LPCSTR pszExpr,
|
|
LPCSTR pszFile,
|
|
int iLine)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
|
|
if (g_qwTraceFlags & TF_WARNING && NULL == pvTest)
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, TF_WARNING);
|
|
wsprintfA(&ach[cch], "TPTR: Failure of \"%s\" at %s, line %d",
|
|
pszExpr, _PathFindFileNameA(pszFile), iLine);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_THR))
|
|
{
|
|
// !!! TPTR !!!! TPTR !!!! TPTR !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! TPTR !!!! TPTR !!!! TPTR !!!
|
|
}
|
|
}
|
|
return pvTest;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Spews a trace message if dwTest is a Win32 failure code.
|
|
*/
|
|
DWORD
|
|
TraceWin32(
|
|
DWORD dwTest,
|
|
LPCSTR pszExpr,
|
|
LPCSTR pszFile,
|
|
int iLine)
|
|
{
|
|
CHAR ach[1024+40]; // Largest path plus extra
|
|
|
|
if (g_qwTraceFlags & TF_WARNING &&
|
|
ERROR_SUCCESS != dwTest)
|
|
{
|
|
int cch;
|
|
|
|
cch = SetPrefixStringA(ach, TF_WARNING);
|
|
wsprintfA(&ach[cch], "TW32: Failure of \"%s\" at %s, line %d (%#08lx)",
|
|
pszExpr, _PathFindFileNameA(pszFile), iLine, dwTest);
|
|
OutputDebugStringA(ach);
|
|
OutputDebugStringA(c_szNewline);
|
|
|
|
if (IsFlagSet(g_dwBreakFlags, BF_THR))
|
|
{
|
|
// !!! THR !!!! THR !!!! THR !!!
|
|
|
|
// MSDEV USERS: This is not the real assert. Hit
|
|
// Shift-F11 to jump back to the caller.
|
|
DEBUG_BREAK; // ASSERT
|
|
|
|
// !!! THR !!!! THR !!!! THR !!!
|
|
}
|
|
}
|
|
return dwTest;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Debug .ini functions
|
|
//
|
|
|
|
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
// (These are deliberately CHAR)
|
|
CHAR const FAR c_szNull[] = "";
|
|
CHAR const FAR c_szZero[] = "0";
|
|
CHAR const FAR c_szIniKeyBreakFlags[] = "BreakFlags";
|
|
CHAR const FAR c_szIniKeyTraceFlags[] = "TraceFlags";
|
|
CHAR const FAR c_szIniKeyFuncTraceFlags[] = "FuncTraceFlags";
|
|
CHAR const FAR c_szIniKeyDumpFlags[] = "DumpFlags";
|
|
CHAR const FAR c_szIniKeyProtoFlags[] = "Prototype";
|
|
|
|
#pragma data_seg()
|
|
|
|
|
|
// Some of the .ini processing code was pimped from the sync engine.
|
|
//
|
|
|
|
typedef struct _INIKEYHEADER
|
|
{
|
|
LPCTSTR pszSectionName;
|
|
LPCTSTR pszKeyName;
|
|
LPCTSTR pszDefaultRHS;
|
|
} INIKEYHEADER;
|
|
|
|
typedef struct _BOOLINIKEY
|
|
{
|
|
INIKEYHEADER ikh;
|
|
LPDWORD puStorage;
|
|
DWORD dwFlag;
|
|
} BOOLINIKEY;
|
|
|
|
typedef struct _INTINIKEY
|
|
{
|
|
INIKEYHEADER ikh;
|
|
LPDWORD puStorage;
|
|
} INTINIKEY;
|
|
|
|
|
|
#define PutIniIntCmp(idsSection, idsKey, nNewValue, nSave) \
|
|
if ((nNewValue) != (nSave)) PutIniInt(idsSection, idsKey, nNewValue)
|
|
|
|
#define WritePrivateProfileInt(szApp, szKey, i, lpFileName) \
|
|
{CHAR sz[7]; \
|
|
WritePrivateProfileString(szApp, szKey, SzFromInt(sz, i), lpFileName);}
|
|
|
|
|
|
#ifdef BOOL_INI_VALUES
|
|
/* Boolean TRUE strings used by IsIniYes() (comparison is case-insensitive) */
|
|
|
|
static LPCTSTR s_rgpszTrue[] =
|
|
{
|
|
TEXT("1"),
|
|
TEXT("On"),
|
|
TEXT("True"),
|
|
TEXT("Y"),
|
|
TEXT("Yes")
|
|
};
|
|
|
|
/* Boolean FALSE strings used by IsIniYes() (comparison is case-insensitive) */
|
|
|
|
static LPCTSTR s_rgpszFalse[] =
|
|
{
|
|
TEXT("0"),
|
|
TEXT("Off"),
|
|
TEXT("False"),
|
|
TEXT("N"),
|
|
TEXT("No")
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef BOOL_INI_VALUES
|
|
/*----------------------------------------------------------
|
|
Purpose: Determines whether a string corresponds to a boolean
|
|
TRUE value.
|
|
Returns: The boolean value (TRUE or FALSE)
|
|
*/
|
|
BOOL
|
|
PRIVATE
|
|
IsIniYes(
|
|
LPCTSTR psz)
|
|
{
|
|
int i;
|
|
BOOL bNotFound = TRUE;
|
|
BOOL bResult;
|
|
|
|
ASSERT(psz);
|
|
|
|
/* Is the value TRUE? */
|
|
|
|
for (i = 0; i < ARRAYSIZE(s_rgpszTrue); i++)
|
|
{
|
|
if (IsSzEqual(psz, s_rgpszTrue[i]))
|
|
{
|
|
bResult = TRUE;
|
|
bNotFound = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Is the value FALSE? */
|
|
|
|
if (bNotFound)
|
|
{
|
|
for (i = 0; i < ARRAYSIZE(s_rgpszFalse); i++)
|
|
{
|
|
if (IsSzEqual(psz, s_rgpszFalse[i]))
|
|
{
|
|
bResult = FALSE;
|
|
bNotFound = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Is the value a known string? */
|
|
|
|
if (bNotFound)
|
|
{
|
|
/* No. Whine about it. */
|
|
|
|
TraceMsg(TF_WARNING, "IsIniYes() called on unknown Boolean RHS '%s'.", psz);
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Process keys with boolean RHSs.
|
|
*/
|
|
void
|
|
PRIVATE
|
|
ProcessBooleans(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAYSIZE(s_rgbik); i++)
|
|
{
|
|
DWORD dwcbKeyLen;
|
|
TCHAR szRHS[MAX_BUF];
|
|
BOOLINIKEY * pbik = &(s_rgbik[i]);
|
|
LPCTSTR lpcszRHS;
|
|
|
|
/* Look for key. */
|
|
|
|
dwcbKeyLen = GetPrivateProfileString(pbik->ikh.pszSectionName,
|
|
pbik->ikh.pszKeyName, TEXT(""), szRHS,
|
|
SIZECHARS(szRHS), c_szCcshellIniFile);
|
|
|
|
if (dwcbKeyLen)
|
|
lpcszRHS = szRHS;
|
|
else
|
|
lpcszRHS = pbik->ikh.pszDefaultRHS;
|
|
|
|
if (IsIniYes(lpcszRHS))
|
|
{
|
|
if (IsFlagClear(*(pbik->puStorage), pbik->dwFlag))
|
|
TraceMsg(TF_GENERAL, "ProcessIniFile(): %s set in %s![%s].",
|
|
pbik->ikh.pszKeyName,
|
|
c_szCcshellIniFile,
|
|
pbik->ikh.pszSectionName);
|
|
|
|
SetFlag(*(pbik->puStorage), pbik->dwFlag);
|
|
}
|
|
else
|
|
{
|
|
if (IsFlagSet(*(pbik->puStorage), pbik->dwFlag))
|
|
TraceMsg(TF_GENERAL, "ProcessIniFile(): %s cleared in %s![%s].",
|
|
pbik->ikh.pszKeyName,
|
|
c_szCcshellIniFile,
|
|
pbik->ikh.pszSectionName);
|
|
|
|
ClearFlag(*(pbik->puStorage), pbik->dwFlag);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: This function reads a .ini file to determine the debug
|
|
flags to set. The .ini file and section are specified
|
|
by the following manifest constants:
|
|
|
|
SZ_DEBUGINI
|
|
SZ_DEBUGSECTION
|
|
|
|
The debug variables that are set by this function are
|
|
g_dwDumpFlags, g_qwTraceFlags, g_dwBreakFlags, and
|
|
g_dwFuncTraceFlags, g_dwPrototype.
|
|
|
|
Returns: TRUE if initialization is successful
|
|
*/
|
|
BOOL
|
|
PUBLIC
|
|
CcshellGetDebugFlags(void)
|
|
{
|
|
CHAR szRHS[MAX_PATH];
|
|
ULONGLONG val;
|
|
|
|
// (scotth): Yes, COMCTL32 exports StrToIntEx, but I
|
|
// don't want to cause a dependency delta and force everyone
|
|
// to get a new comctl32 just because they built debug.
|
|
// So use a local version of StrToIntEx.
|
|
|
|
// Trace Flags
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
c_szIniKeyTraceFlags,
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_qwTraceFlags = val;
|
|
#ifdef FULL_DEBUG
|
|
else
|
|
g_qwTraceFlags = 3; // default to TF_ERROR and TF_WARNING trace messages
|
|
#endif
|
|
|
|
TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#16I64x.",
|
|
c_szIniKeyTraceFlags, g_qwTraceFlags);
|
|
|
|
// Function trace Flags
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
c_szIniKeyFuncTraceFlags,
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_dwFuncTraceFlags = (DWORD)val;
|
|
|
|
TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
|
|
c_szIniKeyFuncTraceFlags, g_dwFuncTraceFlags);
|
|
|
|
// Dump Flags
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
c_szIniKeyDumpFlags,
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_dwDumpFlags = (DWORD)val;
|
|
|
|
TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
|
|
c_szIniKeyDumpFlags, g_dwDumpFlags);
|
|
|
|
// Break Flags
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
c_szIniKeyBreakFlags,
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_dwBreakFlags = (DWORD)val;
|
|
#ifdef FULL_DEBUG
|
|
else
|
|
g_dwBreakFlags = 5; // default to break on ASSERT and TF_ERROR
|
|
#endif
|
|
|
|
TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
|
|
c_szIniKeyBreakFlags, g_dwBreakFlags);
|
|
|
|
// Prototype Flags
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
c_szIniKeyProtoFlags,
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_dwPrototype = (DWORD)val;
|
|
|
|
// Are we using the new leak detection from shelldbg.dll?
|
|
GetPrivateProfileStringA("ShellDbg",
|
|
"NewLeakDetection",
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_bUseNewLeakDetection = BOOLIFY(val);
|
|
|
|
TraceMsgA(DM_DEBUG, "CcshellGetDebugFlags(): %s set to %#08x.",
|
|
c_szIniKeyProtoFlags, g_dwPrototype);
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
"DebugOutputFile",
|
|
c_szNull,
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
if (szRHS[0] != TEXT('\0'))
|
|
{
|
|
g_hDebugOutputFile = CreateFileA(szRHS, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Function to call in allocspy.dll (GetShellMallocSpy)
|
|
typedef BOOL (__stdcall *pfnGSMS) (IShellMallocSpy **ppout);
|
|
|
|
STDAPI_(void) IMSAddToList(BOOL bAdd, void*pv, SIZE_T cb)
|
|
{
|
|
static BOOL bDontTry=FALSE;
|
|
static IShellMallocSpy *pms=NULL;
|
|
|
|
if (!bDontTry && pms == NULL)
|
|
{
|
|
pfnGSMS pfnGetShellMallocSpy;
|
|
HMODULE hmod;
|
|
|
|
bDontTry = TRUE; // assume failure
|
|
if (hmod = LoadLibraryA("ALLOCSPY.DLL"))
|
|
{
|
|
pfnGetShellMallocSpy = (pfnGSMS) GetProcAddress(hmod, "GetShellMallocSpy");
|
|
|
|
pfnGetShellMallocSpy(&pms);
|
|
}
|
|
}
|
|
if (bDontTry)
|
|
return;
|
|
|
|
if (bAdd)
|
|
pms->lpVtbl->AddToList(pms, pv, cb);
|
|
else
|
|
pms->lpVtbl->RemoveFromList(pms, pv);
|
|
}
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
#ifdef PRODUCT_PROF
|
|
|
|
DWORD g_dwProfileCAP = 0;
|
|
|
|
BOOL PUBLIC CcshellGetDebugFlags(void)
|
|
{
|
|
CHAR szRHS[MAX_PATH];
|
|
LONGLONG val;
|
|
|
|
GetPrivateProfileStringA(c_szCcshellIniSecDebug,
|
|
"Profile",
|
|
"",
|
|
szRHS,
|
|
SIZECHARS(szRHS),
|
|
c_szCcshellIniFile);
|
|
|
|
if (MyStrToIntExA(szRHS, STIF_SUPPORT_HEX, &val))
|
|
g_dwProfileCAP = (DWORD)val;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif // PRODUCT_PROF
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// turn on path whacking for full-debug builds
|
|
#ifdef FULL_DEBUG
|
|
static BOOL g_fWhackPathBuffers = TRUE;
|
|
#else
|
|
static BOOL g_fWhackPathBuffers = FALSE;
|
|
#endif
|
|
|
|
void DEBUGWhackPathBufferA(LPSTR psz, UINT cch)
|
|
{
|
|
if (g_fWhackPathBuffers)
|
|
{
|
|
if (psz && IS_VALID_WRITE_BUFFER(psz, char, cch))
|
|
{
|
|
FillMemory(psz, cch * sizeof(char), 0xFE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DEBUGWhackPathBufferW(LPWSTR psz, UINT cch)
|
|
{
|
|
if (g_fWhackPathBuffers)
|
|
{
|
|
if (psz && IS_VALID_WRITE_BUFFER(psz, WCHAR, cch))
|
|
{
|
|
FillMemory(psz, cch * sizeof(WCHAR), 0xFE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DEBUGWhackPathStringA(LPSTR psz, UINT cch)
|
|
{
|
|
if (g_fWhackPathBuffers)
|
|
{
|
|
if (psz && IS_VALID_WRITE_BUFFER(psz, char, cch) && IS_VALID_STRING_PTRA(psz, -1))
|
|
{
|
|
UINT len = lstrlenA(psz);
|
|
|
|
if (len >= cch)
|
|
{
|
|
TraceMsg(TF_WARNING, "DEBUGWhackPathStringA: caller of caller passed strange Path string (strlen > buffer size)");
|
|
}
|
|
else
|
|
{
|
|
FillMemory(psz+len+1, (cch-len-1) * sizeof(char), 0xFE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DEBUGWhackPathStringW(LPWSTR psz, UINT cch)
|
|
{
|
|
if (g_fWhackPathBuffers)
|
|
{
|
|
if (psz && IS_VALID_WRITE_BUFFER(psz, WCHAR, cch) && IS_VALID_STRING_PTRW(psz, -1))
|
|
{
|
|
UINT len = lstrlenW(psz);
|
|
|
|
if (len >= cch)
|
|
{
|
|
TraceMsg(TF_WARNING, "DEBUGWhackPathStringW: caller of caller passed strange Path string (strlen > buffer size)");
|
|
}
|
|
else
|
|
{
|
|
FillMemory(psz+len+1, (cch-len-1) * sizeof(WCHAR), 0xFE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // DEBUG
|