windows-nt/Source/XPSP1/NT/printscan/wia/common/stirt/cassert.c
2020-09-26 16:20:57 +08:00

722 lines
19 KiB
C

/*****************************************************************************
*
* CAssert.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* Assertions and debug output routines
* Based on Raymond's assertion code as it looks quite useful
*
*
* Contents:
*
* DebugOutPtszV
* AssertPtszPtszLn
* ArgsPalPszV
* EnterDbgflPszPal
* ExitDbgflPalHresPpv
*
*****************************************************************************/
/*
#include <windows.h>
#include <windowsx.h>
#include <objbase.h>
#include <regstr.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <stdio.h>
#include <stilog.h>
#include <stiregi.h>
#include <sti.h>
#include <stierr.h>
#include <stiusd.h>
#include "wia.h"
#include "stipriv.h"
#include "stiapi.h"
#include "stirc.h"
#include "debug.h"
*/
#include "sticomm.h"
#ifdef MAXDEBUG
/*****************************************************************************
*
* WarnPszV
*
* Display a message, suitable for framing.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
CHAR c_szPrefix[] = "STI: ";
#pragma END_CONST_DATA
void EXTERNAL
WarnPszV(LPCSTR psz, ...)
{
va_list ap;
CHAR sz[1024];
lstrcpyA(sz, c_szPrefix);
va_start(ap, psz);
wvsprintfA(sz + cA(c_szPrefix) - 1, psz, ap);
va_end(ap);
lstrcatA(sz, "\r\n");
OutputDebugStringA(sz);
}
#endif
#ifdef DEBUG
/*****************************************************************************
*
* Globals
*
*****************************************************************************/
DBGFL DbgflCur;
TCHAR g_tszLogFile[MAX_PATH] = {'\0'};
/*****************************************************************************
*
* Set current trace parameters
*
* This routine is not thread safe
*
*****************************************************************************/
VOID InitializeDebuggingSupport(VOID)
{
HKEY hStiSettingsKey;
DWORD dwErr;
DWORD dwType;
CHAR szLogFile[MAX_PATH];
UINT cbBuffer ;
DBGFL dwFlags = 0;
dwErr = OSUtil_RegOpenKeyExW(HKEY_LOCAL_MACHINE,
REGSTR_PATH_STICONTROL_W,
0L,KEY_READ,
&hStiSettingsKey
);
if (NOERROR != dwErr) {
return;
}
cbBuffer = MAX_PATH;
ZeroX(szLogFile);
dwErr = RegQueryValueExA( hStiSettingsKey,
REGSTR_VAL_DEBUG_FILE_A,
NULL,
&dwType,
(LPBYTE)szLogFile,
&cbBuffer );
if( ( dwErr == NO_ERROR ) || ( dwErr == ERROR_MORE_DATA ) ) {
if( ( dwType != REG_SZ ) &&
( dwType != REG_MULTI_SZ ) &&
( dwType != REG_EXPAND_SZ ) ) {
dwErr = ERROR_FILE_NOT_FOUND;
}
else {
SetDebugLogFileA(szLogFile);
}
}
dwFlags = ReadRegistryDwordW(hStiSettingsKey,
REGSTR_VAL_DEBUG_FLAGS_W,
0L);
SetCurrentDebugFlags(dwFlags ) ;
RegCloseKey(hStiSettingsKey);
return ;
}
DBGFL SetCurrentDebugFlags(DBGFL NewFlags) {
DBGFL OldFlags = DbgflCur;
DbgflCur = NewFlags;
return OldFlags;
}
VOID SetDebugLogFileA(CHAR *pszLogFileName)
{
if (!pszLogFileName || !*pszLogFileName) {
*g_tszLogFile = '\0';
return;
}
lstrcpyA((CHAR*)g_tszLogFile,pszLogFileName);
return;
}
/*****************************************************************************
*
* DebugOutPtsz
*
* Writes a message to the debugger and maybe a log file.
*
*****************************************************************************/
void INTERNAL
DebugOutPtsz(LPCTSTR ptsz)
{
DWORD cbWritten;
OutputDebugString(ptsz);
if (g_tszLogFile[0]) {
HANDLE h = CreateFile(g_tszLogFile, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (h != INVALID_HANDLE_VALUE) {
#ifdef UNICODE
CHAR szBuf[1024];
#endif
SetFilePointer(h, 0, 0, FILE_END);
#ifdef UNICODE
WriteFile(h, szBuf, UToA(szBuf, cA(szBuf), ptsz),&cbWritten,NULL);
#else
WriteFile(h, ptsz, cbCtch(lstrlen(ptsz)),&cbWritten,NULL);
#endif
CloseHandle(h);
}
}
}
/*****************************************************************************
*
* DebugOutPtszA
*
* DebugOut an ANSI message to the debugger and maybe a log file.
*
*****************************************************************************/
#ifdef UNICODE
void INTERNAL
DebugOutPtszA(LPCSTR psz)
{
OutputDebugStringA(psz);
if (g_tszLogFile[0]) {
HANDLE h = CreateFile(g_tszLogFile, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (h != INVALID_HANDLE_VALUE) {
_lwrite((HFILE)(LONG_PTR)h, psz, cbCch(lstrlenA(psz)));
CloseHandle(h);
}
}
}
#else
#define DebugOutPtszA DebugOutPtsz
#endif
/*****************************************************************************
*
* DebugOutPtszV
*
* DebugOut a message with a trailing crlf.
*
*****************************************************************************/
void EXTERNAL
DebugOutPtszV(DBGFL Dbgfl, LPCTSTR ptsz, ...)
{
if (Dbgfl == 0 || (Dbgfl & DbgflCur)) {
va_list ap;
TCHAR tsz[1024];
va_start(ap, ptsz);
wvsprintf(tsz, ptsz, ap);
va_end(ap);
lstrcat(tsz, TEXT("\r\n"));
DebugOutPtsz(tsz);
}
}
/*****************************************************************************
*
* AssertPtszPtszLn
*
* Something bad happened.
*
*****************************************************************************/
int EXTERNAL
AssertPtszPtszLn(LPCTSTR ptszExpr, LPCTSTR ptszFile, int iLine)
{
DebugOutPtszV(DbgFlAlways, TEXT("Assertion failed: `%s' at %s(%d)"),
ptszExpr, ptszFile, iLine);
DebugBreak();
return 0;
}
/*****************************************************************************
*
* Procedure call tracing is gross because of the C preprocessor.
*
* Oh, if only we had support for m4...
*
*****************************************************************************/
/*****************************************************************************
*
* ArgsPszV
*
* Collect arguments to a procedure.
*
* psz -> ASCIIZ format string
* ... = argument list
*
* The characters in the format string are listed in EmitPal.
*
*****************************************************************************/
void EXTERNAL
ArgsPalPszV(PARGLIST pal, LPCSTR psz, ...)
{
va_list ap;
va_start(ap, psz);
if (psz) {
PPV ppv;
pal->pszFormat = psz;
for (ppv = pal->rgpv; *psz; psz++) {
*ppv++ = va_arg(ap, PV);
}
} else {
pal->pszFormat = "";
}
}
/*****************************************************************************
*
* EmitPal
*
* OutputDebugString the information, given a pal. No trailing
* carriage return is emitted.
*
* pal -> place where info was saved
*
* Format characters:
*
* p - 32-bit flat pointer
* x - 32-bit hex integer
* s - TCHAR string
* A - ANSI string
* W - UNICODE string
* G - GUID
* u - unsigned integer
* C - clipboard format
*
*****************************************************************************/
void INTERNAL
EmitPal(PARGLIST pal)
{
char sz[MAX_PATH];
int i;
DebugOutPtszA(pal->pszProc);
DebugOutPtsz(TEXT("("));
for (i = 0; pal->pszFormat[i]; i++) {
if (i) {
DebugOutPtsz(TEXT(", "));
}
switch (pal->pszFormat[i]) {
case 'p': /* 32-bit flat pointer */
case 'x': /* 32-bit hex */
wsprintfA(sz, "%08x", pal->rgpv[i]);
DebugOutPtszA(sz);
break;
case 's': /* TCHAR string */
if (pal->rgpv[i]) {
DebugOutPtsz(pal->rgpv[i]);
}
break;
case 'A': /* ANSI string */
if (pal->rgpv[i]) {
DebugOutPtszA(pal->rgpv[i]);
}
break;
case 'W': /* UNICODE string */
#ifdef UNICODE
OutputDebugStringW(pal->rgpv[i]);
#else
WideCharToMultiByte(CP_ACP, 0, pal->rgpv[i], -1, sz, cA(sz), 0, 0);
DebugOutPtszA(sz);
#endif
break;
#ifndef _WIN64
//
// Ignore this option on SunDown
//
case 'G': /* GUID */
wsprintfA(sz, "%08x",
HIWORD(pal->rgpv[i]) ? *(LPDWORD)pal->rgpv[i]
: (DWORD)pal->rgpv[i]);
DebugOutPtszA(sz);
break;
case 'C':
if (GetClipboardFormatNameA((UINT)pal->rgpv[i], sz, cA(sz))) {
} else {
wsprintfA(sz, "[%04x]", pal->rgpv[i]);
}
DebugOutPtszA(sz);
break;
#endif
case 'u': /* 32-bit unsigned decimal */
wsprintfA(sz, "%u", pal->rgpv[i]);
DebugOutPtszA(sz);
break;
default: AssertF(0); /* Invalid */
}
}
DebugOutPtsz(TEXT(")"));
}
/*****************************************************************************
*
* EnterDbgflPtsz
*
* Mark entry to a procedure. Arguments were already collected by
* ArgsPszV.
*
* Dbgfl -> DebugOuty flags
* pszProc -> procedure name
* pal -> place to save the name and get the format/args
*
*****************************************************************************/
void EXTERNAL
EnterDbgflPszPal(DBGFL Dbgfl, LPCSTR pszProc, PARGLIST pal)
{
pal->pszProc = pszProc;
if (Dbgfl == 0 || (Dbgfl & DbgflCur)) {
EmitPal(pal);
DebugOutPtsz(TEXT("\r\n"));
}
}
/*****************************************************************************
*
* ExitDbgflPalHresPpv
*
* Mark exit from a procedure.
*
* pal -> argument list
* hres -> exit result
* ppv -> optional OUT pointer;
* ppvDword means that hres is a dword
* ppvBool means that hres is a boolean
* ppvVoid means that hres is nothing at all
*
*****************************************************************************/
void EXTERNAL
ExitDbgflPalHresPpv(DBGFL Dbgfl, PARGLIST pal, HRESULT hres, PPV ppvObj)
{
BOOL fInternalError;
DWORD le = GetLastError();
fInternalError = 0;
if (ppvObj == ppvVoid) {
} else if (ppvObj == ppvBool) {
if (hres == 0) {
Dbgfl |= DbgFlError;
}
} else {
if (FAILED(hres)) {
if (fLimpFF(ppvObj && !IsBadWritePtr(ppvObj, cbX(*ppvObj)),
*ppvObj == 0)) {
} else {
fInternalError = 1;
}
Dbgfl |= DbgFlError;
}
}
if (Dbgfl == 0 || (Dbgfl & DbgflCur) || fInternalError) {
EmitPal(pal);
DebugOutPtsz(TEXT(" -> "));
if (ppvObj != ppvVoid) {
TCHAR tszBuf[32];
wsprintf(tszBuf, TEXT("%08x"), hres);
DebugOutPtsz(tszBuf);
if (HIWORD(PtrToLong(ppvObj))) {
wsprintf(tszBuf, TEXT(" [%08x]"), *ppvObj);
DebugOutPtsz(tszBuf);
} else if (ppvObj == ppvDword) {
wsprintf(tszBuf, TEXT(" [%08x]"), hres);
DebugOutPtsz(tszBuf);
} else if (ppvObj == ppvBool) {
wsprintf(tszBuf, hres ? TEXT(" OK ") :
TEXT(" le=[%d]"), le);
DebugOutPtsz(tszBuf);
}
}
DebugOutPtsz(TEXT("\r\n"));
AssertF(!fInternalError);
}
/*
* This redundant test prevents a breakpoint on SetLastError()
* from being hit constantly.
*/
if (le != GetLastError()) {
SetLastError(le);
}
}
#endif
#ifdef MAXDEBUG
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | Random |
*
* Returns a pseudorandom dword. The value doesn't need to be
* statistically wonderful.
*
* @returns
* A not very random dword.
*
*****************************************************************************/
DWORD s_dwRandom = 1; /* Random number seed */
DWORD INLINE
Random(void)
{
s_dwRandom = s_dwRandom * 214013 + 2531011;
return s_dwRandom;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | ScrambleBuf |
*
* Fill a buffer with garbage. Used in RDEBUG to make sure
* the caller is not relying on buffer data.
*
* Note: If the buffer is not a multiple of dwords in size,
* the leftover bytes are not touched.
*
* @parm OUT LPVOID | pv |
*
* The buffer to be scrambled.
*
* @parm UINT | cb |
*
* The size of the buffer.
*
*****************************************************************************/
void EXTERNAL
ScrambleBuf(LPVOID pv, UINT cb)
{
UINT idw;
UINT cdw = cb / 4;
LPDWORD pdw = pv;
for (idw = 0; idw < cdw; idw++) {
pdw[idw] = Random();
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | ScrambleBit |
*
* Randomly set or clear a bit.
*
* @parm OUT LPDWORD | pdw |
*
* The dword whose bit is to be set randomly.
*
* @parm UINT | flMask |
*
* Mask for the bits to scramble.
*
*****************************************************************************/
void EXTERNAL ScrambleBit(LPDWORD pdw, DWORD flMask)
{
*pdw ^= (*pdw ^ Random()) & flMask;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | Callback_CompareContexts |
*
* Check if two <t CONTEXT> structures are substantially the same
* to the extent required by the Win32 calling convention.
*
* This is necessary because lots of applications pass
* incorrectly prototyped functions as callbacks. Others will
* write callback functions that trash registers that are
* supposed to be nonvolatile. Yuck!
*
* NOTE! Platform-dependent code!
*
* @parm LPCONTEXT | pctx1 |
*
* Context structure before we call the callback.
*
* @parm LPCONTEXT | pctx2 |
*
* Context structure after we call the callback.
*
* @returns
*
* Nonzero if the two contexts are substantially the same.
*
*****************************************************************************/
BOOL INLINE
Callback_CompareContexts(LPCONTEXT pctx1, LPCONTEXT pctx2)
{
#if defined(_X86_)
return pctx1->Esp == pctx2->Esp; /* Stack pointer */
#if 0
/*
* Can't test these registers because Win95 doesn't preserve
* them properly. GetThreadContext() stashes what happens to
* be in the registers when you finally reach the bowels of
* kernel, at which point who knows what they contain...
*/
pctx1->Ebx == pctx2->Ebx && /* Nonvolatile registers */
pctx1->Esi == pctx2->Esi &&
pctx1->Edi == pctx2->Edi &&
pctx1->Ebp == pctx2->Ebp;
#endif
#elif defined(_ALPHA_)
return pctx1->IntSp == pctx2->IntSp && /* Stack pointer */
pctx1->IntS0 == pctx2->IntS0 && /* Nonvolatile registers */
pctx1->IntS1 == pctx2->IntS1 &&
pctx1->IntS2 == pctx2->IntS2 &&
pctx1->IntS3 == pctx2->IntS3 &&
pctx1->IntS4 == pctx2->IntS4 &&
pctx1->IntS5 == pctx2->IntS5 &&
pctx1->IntFp == pctx2->IntFp;
#elif defined(_MIPS_)
#pragma message("I hope this is correct for MIPS")
return pctx1->IntSp == pctx2->IntSp && /* Stack pointer */
pctx1->IntS0 == pctx2->IntS0 && /* Nonvolatile registers */
pctx1->IntS1 == pctx2->IntS1 &&
pctx1->IntS2 == pctx2->IntS2 &&
pctx1->IntS3 == pctx2->IntS3 &&
pctx1->IntS4 == pctx2->IntS4 &&
pctx1->IntS6 == pctx2->IntS6 &&
pctx1->IntS7 == pctx2->IntS7 &&
pctx1->IntS8 == pctx2->IntS8;
#elif defined(_PPC_)
#pragma message("I don't know what the PPC calling conventions are")
/* Just check the stack register */
return pctx1->Gpr1 == pctx2->Gpr1;
#else
#pragma message("I don't know what the calling conventions are for this platform")
return 1;
#endif
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | Callback |
*
* Perform a callback the paranoid way, checking that the
* application used the correct calling convention and preserved
* all nonvolatile registers.
*
* NOTE! Platform-dependent code!
*
* @parm STICALLBACKPROC | pfn |
*
* Procedure to call back.
*
* @parm PV | pv1 |
*
* First parameter to callback.
*
* @parm PV | pv2 |
*
* Second parameter to callback.
*
* @returns
*
* Whatever the callback returns.
*
*****************************************************************************/
BOOL EXTERNAL
Callback(STICALLBACKPROC pfn, PV pv1, PV pv2)
{
CONTEXT ctxPre; /* Thread context before call */
CONTEXT ctxPost; /* Thread context after call */
volatile BOOL fRc; /* To prevent compiler from enregistering */
/* Get state of registers before the callback */
ctxPre.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
GetThreadContext(GetCurrentThread(), &ctxPre);
fRc = pfn(pv1, pv2);
ctxPost.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
if (GetThreadContext(GetCurrentThread(), &ctxPost) &&
!Callback_CompareContexts(&ctxPre, &ctxPost)) {
RPF("STI: Incorrectly prototyped callback! Crash soon!");
ValidationException();
}
return fRc;
}
#endif