//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1995 - 1999
//
//  File:       config.cpp
//
//  Contents:   ICertConfig IDispatch helper functions
//
//--------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <stdlib.h>
#include "csdisp.h"

#define __dwFILE__	__dwFILE_CERTLIB_CONFIG_CPP__


//+------------------------------------------------------------------------
// ICertConfig dispatch support

// TCHAR szRegKeyConfigClsid[] = wszCLASS_CERTCONFIG TEXT("\\Clsid");

//+------------------------------------
// Reset method:

static OLECHAR *_apszReset[] = {
    TEXT("Reset"),
    TEXT("Index"),
};

//+------------------------------------
// Next method:

static OLECHAR *_apszNext[] = {
    TEXT("Next"),
};

//+------------------------------------
// GetField method:

static OLECHAR *_apszGetField[] = {
    TEXT("GetField"),
    TEXT("strFieldName"),
};

//+------------------------------------
// GetConfig method:

static OLECHAR *_apszGetConfig[] = {
    TEXT("GetConfig"),
    TEXT("Flags"),
};

//+------------------------------------
// SetSharedFolder method:

static OLECHAR *_apszSetSharedFolder[] = {
    TEXT("SetSharedFolder"),
    TEXT("strSharedFolder"),
};

//+------------------------------------
// Dispatch Table:

DISPATCHTABLE s_adtConfig[] =
{
#define CONFIG_RESET		0
    DECLARE_DISPATCH_ENTRY(_apszReset)

#define CONFIG_NEXT		1
    DECLARE_DISPATCH_ENTRY(_apszNext)

#define CONFIG_GETFIELD		2
    DECLARE_DISPATCH_ENTRY(_apszGetField)

#define CONFIG_GETCONFIG	3
    DECLARE_DISPATCH_ENTRY(_apszGetConfig)

#define CONFIG2_SETSHAREDFOLDER	4
    DECLARE_DISPATCH_ENTRY(_apszSetSharedFolder)
};
#define CCONFIGDISPATCH	(ARRAYSIZE(s_adtConfig))
#define CCONFIGDISPATCH_V1	CONFIG2_SETSHAREDFOLDER
#define CCONFIGDISPATCH_V2	CCONFIGDISPATCH


DWORD s_acConfigDispatch[] = {
    CCONFIGDISPATCH_V2,
    CCONFIGDISPATCH_V1,
};

IID const *s_apConfigiid[] = {
    &IID_ICertConfig2,
    &IID_ICertConfig,
};


HRESULT
Config_Init(
    IN DWORD Flags,
    OUT DISPATCHINTERFACE *pdiConfig)
{
    HRESULT hr;

    hr = DispatchSetup2(
		Flags,
		CLSCTX_INPROC_SERVER,
		wszCLASS_CERTCONFIG,
		&CLSID_CCertConfig,
		ARRAYSIZE(s_acConfigDispatch),		// cver
		s_apConfigiid,
		s_acConfigDispatch,
		s_adtConfig,
		pdiConfig);
    _JumpIfError(hr, error, "DispatchSetup2(ICertConfig)");

error:
    return(hr);
}


VOID
Config_Release(
    IN OUT DISPATCHINTERFACE *pdiConfig)
{
    DispatchRelease(pdiConfig);
}


HRESULT
ConfigVerifyVersion(
    IN DISPATCHINTERFACE *pdiConfig,
    IN DWORD RequiredVersion)
{
    HRESULT hr;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    switch (pdiConfig->m_dwVersion)
    {
	case 1:
	    CSASSERT(
		NULL == pdiConfig->pDispatch ||
		CCONFIGDISPATCH_V1 == pdiConfig->m_cDispatchTable);
	    break;

	case 2:
	    CSASSERT(
		NULL == pdiConfig->pDispatch ||
		CCONFIGDISPATCH_V2 == pdiConfig->m_cDispatchTable);
	    break;

	default:
	    hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR);
	    _JumpError(hr, error, "m_dwVersion");
    }
    if (pdiConfig->m_dwVersion < RequiredVersion)
    {
	hr = E_NOTIMPL;
	_JumpError(hr, error, "old interface");
    }
    hr = S_OK;

error:
    return(hr);
}


HRESULT
Config_Reset(
    IN DISPATCHINTERFACE *pdiConfig,
    IN LONG Index,
    OUT LONG *pCount)
{
    HRESULT hr;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    if (NULL != pdiConfig->pDispatch)
    {
	VARIANT avar[1];

	avar[0].vt = VT_I4;
	avar[0].lVal = Index;

	hr = DispatchInvoke(
			pdiConfig,
			CONFIG_RESET,
			ARRAYSIZE(avar),
			avar,
			VT_I4,
			pCount);
	_JumpIfError(hr, error, "Invoke(Reset)");
    }
    else
    {
	hr = ((ICertConfig *) pdiConfig->pUnknown)->Reset(Index, pCount);

	_JumpIfError(hr, error, "ICertConfig::Reset");
    }

error:
    return(hr);
}


HRESULT
Config_Next(
    IN DISPATCHINTERFACE *pdiConfig,
    IN LONG *pIndex)
{
    HRESULT hr;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    if (NULL != pdiConfig->pDispatch)
    {
	hr = DispatchInvoke(
			pdiConfig,
			CONFIG_NEXT,
			0,
			NULL,
			VT_I4,
			pIndex);
	_JumpIfError(hr, error, "Invoke(Next)");
    }
    else
    {
	hr = ((ICertConfig *) pdiConfig->pUnknown)->Next(pIndex);
	if (S_FALSE != hr)
	{
	    _JumpIfError(hr, error, "ICertConfig::Next");
	}
    }

error:
    return(hr);
}


HRESULT
Config_GetField(
    IN DISPATCHINTERFACE *pdiConfig,
    IN WCHAR const *pwszField,
    OUT BSTR *pstr)
{
    HRESULT hr;
    BSTR strField = NULL;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    if (!ConvertWszToBstr(&strField, pwszField, -1))
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "ConvertWszToBstr");
    }
    //wprintf(L"str=%ws, len=%u\n", strField, ((ULONG *) strField)[-1]);

    if (NULL != pdiConfig->pDispatch)
    {
	VARIANT avar[1];

	avar[0].vt = VT_BSTR;
	avar[0].bstrVal = strField;

	hr = DispatchInvoke(
			pdiConfig,
			CONFIG_GETFIELD,
			ARRAYSIZE(avar),
			avar,
			VT_BSTR,
			pstr);
	_JumpIfError(hr, error, "Invoke(GetField)");
    }
    else
    {
	hr = ((ICertConfig *) pdiConfig->pUnknown)->GetField(strField, pstr);
	_JumpIfErrorNotSpecific(
			    hr,
			    error,
			    "ICertConfig::GetField",
			    CERTSRV_E_PROPERTY_EMPTY);
    }
    hr = S_OK;

error:
    if (NULL != strField)
    {
	SysFreeString(strField);
    }
    return(hr);
}


HRESULT
Config_GetConfig(
    IN DISPATCHINTERFACE *pdiConfig,
    IN LONG Flags,
    OUT BSTR *pstrConfig)
{
    HRESULT hr;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    if (NULL != pdiConfig->pDispatch)
    {
	VARIANT avar[1];

	avar[0].vt = VT_I4;
	avar[0].lVal = Flags;

	hr = DispatchInvoke(
			pdiConfig,
			CONFIG_GETCONFIG,
			ARRAYSIZE(avar),
			avar,
			VT_BSTR,
			pstrConfig);
	_JumpIfError(hr, error, "Invoke(GetConfig)");
    }
    else
    {
	hr = ((ICertConfig *) pdiConfig->pUnknown)->GetConfig(Flags, pstrConfig);
	_JumpIfError(hr, error, "ICertConfig::GetConfig");
    }

error:
    return(hr);
}


HRESULT
Config2_SetSharedFolder(
    IN DISPATCHINTERFACE *pdiConfig,
    IN WCHAR const *pwszSharedFolder)
{
    HRESULT hr;
    BSTR strSharedFolder = NULL;

    CSASSERT(NULL != pdiConfig && NULL != pdiConfig->pDispatchTable);

    hr = ConfigVerifyVersion(pdiConfig, 2);
    _JumpIfError(hr, error, "ConfigVerifyVersion");

    if (!ConvertWszToBstr(&strSharedFolder, pwszSharedFolder, -1))
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "ConvertWszToBstr");
    }
    //wprintf(L"str=%ws, len=%u\n", strSharedFolder, ((ULONG *) strSharedFolder)[-1]);

    if (NULL != pdiConfig->pDispatch)
    {
	VARIANT avar[1];

	avar[0].vt = VT_BSTR;
	avar[0].bstrVal = strSharedFolder;

	hr = DispatchInvoke(
			pdiConfig,
			CONFIG2_SETSHAREDFOLDER,
			ARRAYSIZE(avar),
			avar,
			0,
			NULL);
	_JumpIfError(hr, error, "Invoke(SetSharedFolder)");
    }
    else
    {
	hr = ((ICertConfig2 *) pdiConfig->pUnknown)->SetSharedFolder(strSharedFolder);
	_JumpIfError(hr, error, "ICertConfig::GetConfig");
    }

error:
    if (NULL != strSharedFolder)
    {
	SysFreeString(strSharedFolder);
    }
    return(hr);
}


WCHAR const * const s_apwszFieldNames[] = {
    wszCONFIG_COMMONNAME,
    wszCONFIG_ORGUNIT,
    wszCONFIG_ORGANIZATION,
    wszCONFIG_LOCALITY,
    wszCONFIG_STATE,
    wszCONFIG_COUNTRY,
    wszCONFIG_CONFIG,
    wszCONFIG_EXCHANGECERTIFICATE,
    wszCONFIG_SIGNATURECERTIFICATE,
    wszCONFIG_DESCRIPTION,
    wszCONFIG_SERVER,
    wszCONFIG_AUTHORITY,
    wszCONFIG_SANITIZEDNAME,
    wszCONFIG_SHORTNAME,
    wszCONFIG_SANITIZEDSHORTNAME,
    wszCONFIG_FLAGS,
};
#define CSTRING (sizeof(s_apwszFieldNames)/sizeof(s_apwszFieldNames[0]))

WCHAR const *s_apwszDisplayNames[CSTRING] = {
    wszCONFIG_COMMONNAME L":",
    wszCONFIG_ORGUNIT L":",
    wszCONFIG_ORGANIZATION L":",
    wszCONFIG_LOCALITY L":",
    wszCONFIG_STATE L":",
    wszCONFIG_COUNTRY L":",
    wszCONFIG_CONFIG L":",
    wszCONFIG_EXCHANGECERTIFICATE L":",
    wszCONFIG_SIGNATURECERTIFICATE L":",
    wszCONFIG_DESCRIPTION L":",
    wszCONFIG_SERVER L":",
    wszCONFIG_AUTHORITY L":",
    wszCONFIG_SANITIZEDNAME L":",
    wszCONFIG_SHORTNAME L":",
    wszCONFIG_SANITIZEDSHORTNAME L":",
    wszCONFIG_FLAGS L":",
};


HRESULT
ConfigDumpSetDisplayNames(
    IN WCHAR const * const *apwszFieldNames,
    IN WCHAR const * const *apwszDisplayNames,
    IN DWORD cNames)
{
    DWORD i;
    DWORD j;
    HRESULT hr;

    for (i = 0; i < cNames; i++)
    {
	for (j = 0; j < CSTRING; j++)
	{
	    if (0 == lstrcmpi(s_apwszFieldNames[j], apwszFieldNames[i]) ||
		(0 == lstrcmpi(s_apwszFieldNames[j], wszCONFIG_DESCRIPTION) &&
		 0 == lstrcmpi(apwszFieldNames[i], wszCONFIG_COMMENT)))
	    {
		s_apwszDisplayNames[j] = apwszDisplayNames[i];
		break;
	    }
	}
	if (CSTRING <= j)
	{
	    hr = E_INVALIDARG;
	    _JumpErrorStr(hr, error, "column name", apwszFieldNames[i]);
	}
    }
    hr = S_OK;

error:
    return(hr);
}


HRESULT
ConfigDumpEntry(
    IN DISPATCHINTERFACE *pdiConfig,
    IN WCHAR const *pwszEntry,		// localized L"Entry"
    IN LONG Index,  // less than 0 skip index, entry, & suffix print
    OPTIONAL IN WCHAR const *pwszSuffix)
{
    HRESULT hr;
    DWORD i;
    BSTR strings[CSTRING];

    for (i = 0; i < CSTRING; i++)
    {
	strings[i] = NULL;
    }
    for (i = 0; i < CSTRING; i++)
    {
	hr = Config_GetField(pdiConfig, s_apwszFieldNames[i], &strings[i]);
	_JumpIfError(hr, error, "Config_GetField");
    }
    if (-1 < Index)
    {
        myConsolePrintf(
	    L"%ws%ws %u:%ws%ws\n",
	    0 == Index? L"" : L"\n",
	    pwszEntry,
	    Index,
	    NULL != pwszSuffix? L" " : L"",
	    NULL != pwszSuffix? pwszSuffix : L"");
    }
    for (i = 0; i < CSTRING; i++)
    {
	myConsolePrintf(L"  ");
	myConsolePrintString(24, s_apwszDisplayNames[i]);
	myConsolePrintf(L"\t`");
	if (0 != wcscmp(s_apwszFieldNames[i], L"ExchangeCertificate"))
	{
	    if (NULL != strings[i])
	    {
		myConsolePrintf(L"%ws", strings[i]);
	    }
	}
	myConsolePrintf(L"'\n");
    }

error:
    for (i = 0; i < CSTRING; i++)
    {
	if (NULL != strings[i])
	{
	    SysFreeString(strings[i]);
	}
    }
    return(hr);
}


HRESULT
ConfigDump(
    IN DWORD Flags,				// See DispatchSetup() Flags
    IN WCHAR const *pwszEntry,			// localized L"Entry"
    OPTIONAL IN WCHAR const *pwszLocalSuffix,	// localized L"(Local)"
    OPTIONAL IN WCHAR const *pwszMach1,
    OPTIONAL IN WCHAR const *pwszMach2)
{
    HRESULT hr;
    LONG i;
    LONG count;
    LONG Index;
    BSTR strServer = NULL;
    WCHAR const *pwszSuffix;
    DISPATCHINTERFACE diConfig;

    hr = Config_Init(Flags, &diConfig);
    _JumpIfError(hr, error, "Config_Init");

    hr = Config_Reset(&diConfig, 0, &count);
    _JumpIfError(hr, error, "Config_Reset");

    Index = 0;
    for (i = 0; i < count; i++)
    {
	hr = Config_Next(&diConfig, &Index);
	if (S_OK != hr && S_FALSE != hr)
	{
	    _JumpError(hr, error, "Config_Next");
	}
	hr = S_OK;
	if (-1 == Index)
	{
	    break;
	}

	pwszSuffix = NULL;
	if (NULL != pwszLocalSuffix)
	{
	    hr = Config_GetField(&diConfig, wszCONFIG_SERVER, &strServer);
	    _JumpIfError(hr, error, "Config_GetField");

	    if ((NULL != pwszMach1 && 0 == lstrcmpi(strServer, pwszMach1)) ||
		(NULL != pwszMach2 && 0 == lstrcmpi(strServer, pwszMach2)))
	    {
		pwszSuffix = pwszLocalSuffix;
	    }
	}
	hr = ConfigDumpEntry(&diConfig, pwszEntry, Index, pwszSuffix);
	_JumpIfError(hr, error, "ConfigDumpEntry");
    }

error:
    if (NULL != strServer)
    {
	SysFreeString(strServer);
    }
    Config_Release(&diConfig);
    return(hr);
}


HRESULT
ConfigGetConfig(
    IN DWORD Flags,
    IN DWORD dwUIFlag,
    OUT BSTR *pstrConfig)
{
    HRESULT hr;
    LONG count;
    DISPATCHINTERFACE diConfig;

    hr = Config_Init(Flags, &diConfig);
    _JumpIfError(hr, error, "Config_Init");

    hr = Config_GetConfig(&diConfig, dwUIFlag, pstrConfig);
    _JumpIfError(hr, error, "Config_GetConfig");

error:
    Config_Release(&diConfig);
    return(hr);
}


DWORD
myGetDisplayLength(
    IN WCHAR const *pwsz)
{
    HRESULT hr;
    LONG ccol;

    CSASSERT(NULL != pwsz);

    ccol = WideCharToMultiByte(
		    GetACP(),	// CodePage
		    0,		// dwFlags
		    pwsz,	// lpWideCharStr
		    -1,		// cchWideChar, -1 => L'\0' terminated
		    NULL,	// lpMultiByteStr
		    0,		// cbMultiByte
		    NULL,	// lpDefaultChar
		    NULL);	// lpUsedDefaultChar
    if (0 >= ccol)
    {
	if (0 > ccol || L'\0' != *pwsz)
	{
	    hr = myHLastError();
	    _PrintError(hr, "WideCharToMultiByte");
	}
	ccol = wcslen(pwsz);
    }
    else
    {
	ccol--;			// don't include trailing L'\0'
    }
//error:
    return(ccol);
}


LONG
myConsolePrintString(
    IN DWORD ccolMin,
    IN WCHAR const *pwszString)
{
    DWORD ccolDisplay;
    DWORD ccolRet;

    ccolRet = myGetDisplayLength(pwszString);
    ccolDisplay = ccolRet;
    if (ccolMin < ccolDisplay)
    {
	ccolDisplay = ccolMin;
    }
    myConsolePrintf(L"%ws%*ws", pwszString, ccolMin - ccolDisplay, L"");
    return(ccolRet);
}


static BOOL s_fConsolePrintfDisable = FALSE;

BOOL
myConsolePrintfDisable(
    IN BOOL fDisable)
{
    BOOL fDisableOld = s_fConsolePrintfDisable;

    s_fConsolePrintfDisable = fDisable;
    return(fDisableOld);
}


// Fall back to stdio if s_fConsolePrintfDisable is set OR
// if _vsnwprintf doesn't exist in ntdll.dll and msvcrt.dll OR
// if we run out of memory allocating a working buffer.
//
// Otherwise:
// if redirected, use WriteFile
// if not redirected, use WriteConsole

#define cwcBUFMIN	512
#define cwcBUFMAX	(64 * 1024)

int __cdecl
myConsolePrintf(
    OPTIONAL IN WCHAR const *pwszFmt,
    ...)
{
    HRESULT hr;
    va_list pva;
    int cwc;
    DWORD cwcOut;
    HANDLE hStdOut;
    WCHAR wszBuf[cwcBUFMIN];
    WCHAR *pwszBuf = wszBuf;
    DWORD cwcBuf = ARRAYSIZE(wszBuf);
    CHAR szAnsi[2 * cwcBUFMIN];
    CHAR *pszAnsi = NULL;
    DWORD cchAnsi;

    typedef int (__cdecl FN_VSNWPRINTF)(
        OUT wchar_t *,
        IN size_t,
        IN const wchar_t *,
        IN va_list);

    HMODULE hModule;
    static FN_VSNWPRINTF *s_pfn = NULL;

    if (NULL == pwszFmt)
    {
        pwszFmt = L"(null)";
    }
    if (L'\0' == *pwszFmt)
    {
	cwcOut = 0;
	goto error;
    }
    if (NULL == s_pfn)
    {
        hModule = GetModuleHandle(TEXT("ntdll.dll"));
        if (NULL != hModule)
        {
            s_pfn = (FN_VSNWPRINTF *) GetProcAddress(hModule, "_vsnwprintf");
        }
	if (NULL == s_pfn)
	{
	    hModule = GetModuleHandle(TEXT("msvcrt.dll"));
            s_pfn = (FN_VSNWPRINTF *) GetProcAddress(hModule, "_vsnwprintf");
	}
    }
    hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (NULL == s_pfn || s_fConsolePrintfDisable)
    {
	hStdOut = INVALID_HANDLE_VALUE;		// use stdio fallback
    }

    if (INVALID_HANDLE_VALUE != hStdOut)
    {
	while (TRUE)
	{
	    va_start(pva, pwszFmt);
	    cwc = (*s_pfn)(pwszBuf, cwcBuf, pwszFmt, pva);
	    va_end(pva);

	    if (-1 != cwc)
	    {
		break;
	    }
	    if (cwcBUFMAX <= cwcBuf)
	    {
		_PrintError(
		    HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW),
		    "_vsnwprintf");
		hStdOut = INVALID_HANDLE_VALUE;
		break;
	    }
	    if (pwszBuf != wszBuf)
	    {
		LocalFree(pwszBuf);
	    }
	    cwcBuf *= 2;
	    if (cwcBUFMAX < cwcBuf)
	    {
		cwcBuf = cwcBUFMAX;
	    }
	    pwszBuf = (WCHAR *) LocalAlloc(LMEM_FIXED, cwcBuf * sizeof(WCHAR));
	    if (NULL == pwszBuf)
	    {
		_PrintError(E_OUTOFMEMORY, "LocalAlloc");
		hStdOut = INVALID_HANDLE_VALUE;
		break;
	    }
	}
    }

    if (INVALID_HANDLE_VALUE != hStdOut)
    {
	BOOL fRedirected = FALSE;

	// time for output -- where are we going, to a file or the console?
	
	switch (~FILE_TYPE_REMOTE & GetFileType(hStdOut))
	{
	    //case FILE_TYPE_UNKNOWN:
	    //case FILE_TYPE_PIPE:
	    //case FILE_TYPE_DISK:
	    default:
		// if redirected to a pipe or a file, don't use WriteConsole;
		// it drops redirected output on the floor
		fRedirected = TRUE;
		break;
		
	    case FILE_TYPE_CHAR:
		break;
	}

	if (!fRedirected)
	{
	    if (!WriteConsole(hStdOut, pwszBuf, cwc, &cwcOut, NULL))
	    {
		hr = myHLastError();
		_PrintError(hr, "WriteConsole");
		hStdOut = INVALID_HANDLE_VALUE;
	    }
	}
	else  // WriteConsole is out of the question
	{
	    DWORD cch;

	    // Expand all \n chars to \r\n so the WriteFile ends up clean.
	    // Alloc new buffer big enough to hold two bytes per WCHAR for
	    // worst case MultiByte translation + inserted \r chars.

	    cchAnsi = 2 * (cwc + 1);
	    if (ARRAYSIZE(szAnsi) >= cchAnsi)
	    {
		pszAnsi = szAnsi;
	    }
	    else
	    {
		pszAnsi = (LPSTR) LocalAlloc(LMEM_FIXED, cchAnsi);
		if (NULL == pszAnsi)
		{
		    _PrintError(E_OUTOFMEMORY, "LocalAlloc");
		    hStdOut = INVALID_HANDLE_VALUE;
		}
	    }
	    if (INVALID_HANDLE_VALUE != hStdOut)
	    {
		cch = WideCharToMultiByte(
				    GetConsoleOutputCP(),
				    0,
				    pwszBuf,
				    cwc,
				    pszAnsi,
				    cchAnsi,
				    NULL,
				    NULL);
		if (0 == cch)
		{
		    hr = myHLastError();
		    _PrintError(hr, "WideCharToMultiByte");
		    hStdOut = INVALID_HANDLE_VALUE;
		}
	    }
	    if (INVALID_HANDLE_VALUE != hStdOut)
	    {
		CHAR *pchWork = pszAnsi;
		DWORD cchOut;
		
		// expand all \n chars to \r\n

		cwcOut = cwc;
		for (unsigned int j = 0; j < cch; j++, pchWork++)
		{
		    if (*pchWork == '\n')
		    {
			// create a 1-char space before \n

			MoveMemory(&pchWork[1], pchWork, cch - j);

			// Fill with \r and skip past \r (this statement) and
			// \n (automatic via loop incr).

			*pchWork++ = '\r';
			j++;
			cch++;		// write one more char for each \n
		    }
		}
		CSASSERT(pchWork <= &pszAnsi[2 * cwc]);
		
		if (!WriteFile(hStdOut, pszAnsi, cch, &cchOut, NULL))
		{
		    hr = myHLastError();
		    _PrintError(hr, "WriteFile");
		    if (E_HANDLE == hr)
		    {
			hStdOut = INVALID_HANDLE_VALUE;
			s_fConsolePrintfDisable = TRUE;
		    }
		    else
		    {
			// This is the only case we drop output on the floor.
			// Most likely cause is disk full, so stdio won't help.
		    }
		}
	    }
	} // else WriteConsole
    }

    if (INVALID_HANDLE_VALUE == hStdOut)
    {
	BOOL fRetried = FALSE;
	
	while (TRUE)
	{
	    ALIGNIOB(stdout);
	    va_start(pva, pwszFmt);
	    __try
	    {
		cwcOut = vfwprintf(stdout, pwszFmt, pva);
		hr = S_OK;
	    }
	    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
	    {
		cwcOut = MAXDWORD;
		DBGPRINT((
		    DBG_SS_ERROR,
		    "Exception %x: myConsolePrintf(%ws)",
		    hr,
		    pwszFmt));
	    }
	    va_end(pva);
	    if (S_OK == hr || fRetried || !IOBUNALIGNED(stdout))
	    {
		break;
	    }
	    fRetried = TRUE;
	}
    }

error:
    if (NULL != pwszBuf && wszBuf != pwszBuf)
    {
	LocalFree(pwszBuf);
    }
    if (NULL != pszAnsi && szAnsi != pszAnsi)
    {
	LocalFree(pszAnsi);
    }
    return((int) cwcOut);
}