Leaked source code of windows server 2003
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.
 
 
 
 
 
 

736 lines
23 KiB

/*++
Copyright (c) Microsoft Corporation
Module Name:
xmlchk.cpp
Abstract:
Use msxml.dll to see if an .xml file conforms to a schema.
Author:
Ted Padua (TedP)
Revision History:
Jay Krell (JayKrell) April 2001 partial cleanup
many leaks added in attempt to stop it from crashing
crash doesn't repro consistently, but there's always a few
in a world build
June 2001 let it run on Win9x and Win2000
--*/
#include "stdinc.h"
#include "helpers.h"
#define XMLCHK_FLAG_SILENT (0x00000001)
ULONG g_nrunFlags = 0; //global run flag - determines if should run in silent mode = 0x01
IClassFactory* g_XmlDomClassFactory;
IClassFactory* g_XmlSchemaCacheClassFactory;
__declspec(thread) long line = __LINE__;
__declspec(thread) ULONG lastError;
#if defined(_WIN64)
#define IsAtLeastXp() (TRUE)
#define g_IsNt (TRUE)
#else
DWORD g_Version;
BOOL g_IsNt;
#define IsAtLeastXp() (g_IsNt && g_Version >= 0x0501)
#endif
// Globals indicating what we're currently doing.
// L"" is different than the default constructed, because it can be derefed
::ATL::CComBSTR szwcharSchemaTmp = L"";
::ATL::CComBSTR szwcharManTmp = L"";
bool g_fInBuildProcess = false;
// string to put in front of all error messages so that BUILD can find them.
const char ErrMsgPrefix[] = "NMAKE : U1234: 'FUSION_MANIFEST_VALIDATOR' ";
void ConvertNewlinesToSpaces(char* s)
{
while (*s)
{
if (isspace(*s))
*s = ' ';
s += 1;
}
}
void Error(PCSTR szPrintFormatString, ...)
{
char StartBuffer[256];
char *buffer = StartBuffer;
int iAvailable = 256;
if (g_fInBuildProcess)
return;
while (buffer)
{
va_list args;
int iUsed;
va_start(args, szPrintFormatString);
buffer[iAvailable - 1] = buffer[iAvailable - 2] = '\0';
iUsed = _vsnprintf(buffer, iAvailable, szPrintFormatString, args);
va_end(args);
//
// Used all the characters, or we stomped the canary?
//
if ((iUsed >= iAvailable) || (buffer[iAvailable - 1] != '\0'))
{
if (buffer != StartBuffer)
{
delete [] buffer;
}
iAvailable *= 2;
buffer = new char[iAvailable];
continue;
}
else
{
buffer[iUsed] = '\0';
break;
}
}
if (buffer)
{
ConvertNewlinesToSpaces(buffer);
}
printf("%s line=%ld, %s\n", ErrMsgPrefix, line, buffer);
if (buffer && (buffer != StartBuffer))
delete [] buffer;
}
void PrintOutMode(PCSTR szPrintFormatString, ...)
{
if (g_fInBuildProcess)
return;
if ((g_nrunFlags & XMLCHK_FLAG_SILENT) == 0)
{
va_list args;
va_start(args, szPrintFormatString);
vprintf(szPrintFormatString, args);
va_end(args);
}
}
void PrintErrorDuringBuildProcess(IXMLDOMParseError* pError)
{
HRESULT hr = S_OK;
::ATL::CComBSTR bstrError;
long lErrorCode = 0;
long lErrorLine = 0;
if (FAILED(hr = pError->get_errorCode(&lErrorCode)))
goto FailedGettingDetails;
if (FAILED(hr = pError->get_line(&lErrorLine)))
goto FailedGettingDetails;
if (FAILED(hr = pError->get_reason(&bstrError)))
goto FailedGettingDetails;
//
// Now print in a way that build is likely to pick up
//
printf(
"%s : %ls(%ld) - %ls (Error 0x%08lx)\r\n",
ErrMsgPrefix,
static_cast<PCWSTR>(szwcharManTmp),
lErrorLine,
static_cast<PCWSTR>(bstrError),
lErrorCode);
return;
FailedGettingDetails:
printf("%s : %ls had an error, but the error data was unavailable (Error 0x%08lx).\r\n",
ErrMsgPrefix,
static_cast<PCWSTR>(szwcharManTmp),
hr);
return;
}
void PrintError(IXMLDOMParseError *pError)
{
::ATL::CComBSTR bstrError;
::ATL::CComBSTR bstrURL;
::ATL::CComBSTR bstrText;
long errCode = 0;
long errLine = 0;
long errPos = 0;
HRESULT hr = S_OK;
long line = __LINE__;
try
{
line = __LINE__;
hr = pError->get_reason(&bstrError);
if (FAILED(hr))
throw hr;
line = __LINE__;
hr = pError->get_url(&bstrURL);
if (FAILED(hr))
throw hr;
line = __LINE__;
hr = pError->get_errorCode(&errCode);
if (FAILED(hr))
throw hr;
line = __LINE__;
hr = pError->get_srcText(&bstrText);
if (FAILED(hr))
throw hr;
line = __LINE__;
hr = pError->get_line(&errLine);
if (FAILED(hr))
throw hr;
line = __LINE__;
hr = pError->get_linepos(&errPos);
if (FAILED(hr))
throw hr;
line = __LINE__;
PrintOutMode("\nError Info:\n");
if (bstrError != NULL)
PrintOutMode("\tDescription: %ls\n", static_cast<PCWSTR>(bstrError));
if (bstrURL != NULL)
PrintOutMode("\tURL: %ls\n", static_cast<PCWSTR>(bstrURL));
//if (errCode > 0)
PrintOutMode("\tCode=%X", errCode);
if (errLine > 0)
PrintOutMode(" on Line:%ld, ", errLine);
if (errPos > 0)
PrintOutMode("\tPos:%ld\n", errPos);
line = __LINE__;
if (errLine > 0 && bstrText != NULL)
{
PrintOutMode("\tLine %ld: ", errLine);
long lLen = ::SysStringLen(bstrText);
for (int i = 0; i < lLen; i++)
{
if (bstrText[i] == '\t')
PrintOutMode(" ");
else
PrintOutMode("%lc", bstrText[i]);
}
PrintOutMode("\n");
if (errPos > 0 || lLen > 0)
{
PrintOutMode("\tPos %ld: ", errPos);
for (int i = 1; i < errPos; i++)
{
PrintOutMode("-");
}
PrintOutMode("^\n");
}
}
line = __LINE__;
}
catch(HRESULT hr2)
{
Error("Failed getting error #1 information hr=%lx, line=%ld\n", static_cast<unsigned long>(hr2), line);
}
}
//tedp
// Load an msxml version. If we don't get v3, we fall to v2, then to v1. v1 is pretty darn useless,
// however, so it'd be nice if we didn't have to.
bool
InitializeMSXML3()
{
static HMODULE hMsXml3 = NULL;
typedef HRESULT (__stdcall * PFN_DLL_GET_CLASS_OBJECT)(REFCLSID, REFIID, LPVOID*);
PFN_DLL_GET_CLASS_OBJECT pfnGetClassObject = NULL;
::ATL::CComPtr<IClassFactory> pFactory;
HRESULT hr = S_OK;
::ATL::CComPtr<IClassFactory> pSchemaCacheFactory;
line = __LINE__;
if (hMsXml3 == NULL)
{
hMsXml3 = LoadLibrary(TEXT("msxml3.dll"));
if (hMsXml3 == NULL)
{
line = __LINE__;
if (IsAtLeastXp())
PrintOutMode("Unable to load msxml3, trying msxml2\n");
line = __LINE__;
if (IsAtLeastXp())
hMsXml3 = LoadLibrary(TEXT("msxml2.dll"));
line = __LINE__;
if (hMsXml3 == NULL)
{
line = __LINE__;
if (IsAtLeastXp())
PrintOutMode("Unable to load msxml2\n");
line = __LINE__;
}
}
}
line = __LINE__;
if (hMsXml3 == NULL)
{
if (IsAtLeastXp())
Error("LoadLibrary(msxml) lastError=%lu\n", GetLastError());
return false;
}
line = __LINE__;
pfnGetClassObject = reinterpret_cast<PFN_DLL_GET_CLASS_OBJECT>(GetProcAddress(hMsXml3, "DllGetClassObject"));
if (!pfnGetClassObject)
{
line = __LINE__;
Error("GetProcAddress(msxml, DllGetClassObject) lastError=%lu\n", GetLastError());
return false;
}
line = __LINE__;
hr = pfnGetClassObject(__uuidof(MSXML2::DOMDocument30), __uuidof(pFactory), (void**)&pFactory);
if (FAILED(hr))
{
PrintOutMode("Can't load version 3.0, trying 2.6\n");
hr = pfnGetClassObject(__uuidof(MSXML2::DOMDocument26), __uuidof(pFactory), (void**)&pFactory);
if (FAILED(hr))
{
PrintOutMode("Can't load version 2.6\n");
}
}
pFactory->LockServer(TRUE); // possibly the right fix for the crash
static_cast<IUnknown*>(pFactory)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(pFactory)->AddRef(); // jaykrell hack to try to avoid crash
line = __LINE__;
if (FAILED(hr))
{
Error("msxml.DllGetClassObject(DOMDocument) hr=%lx\n", hr);
return false;
}
g_XmlDomClassFactory = pFactory;
hr = pfnGetClassObject(__uuidof(MSXML2::XMLSchemaCache30), __uuidof(pFactory), (void**)&pSchemaCacheFactory);
if (FAILED(hr))
{
PrintOutMode("Can't load SchemaCache version 3.0, trying 2.6\n");
hr = pfnGetClassObject(__uuidof(MSXML2::XMLSchemaCache26), __uuidof(pFactory), (void**)&pSchemaCacheFactory);
if (FAILED(hr))
{
PrintOutMode("Can't load SchemaCache version 2.6\n");
}
}
pSchemaCacheFactory->LockServer(TRUE); // possibly the right fix for the crash
static_cast<IUnknown*>(pSchemaCacheFactory)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(pSchemaCacheFactory)->AddRef(); // jaykrell hack to try to avoid crash
if (FAILED(hr))
{
Error("msxml.DllGetClassObject(SchemaCache) hr=%lx\n", hr);
return false;
}
g_XmlSchemaCacheClassFactory = pSchemaCacheFactory;
return true;
}
BOOL
Validating(
PCWSTR SourceManName,
PCWSTR SchemaName
)
{
HRESULT hr = S_OK;
BOOL bResult = FALSE;
short sResult = FALSE;
VARIANT_BOOL vb = VARIANT_FALSE;
::ATL::CComPtr<IXMLDOMParseError> pParseError;
::ATL::CComPtr<IXMLDOMParseError> pParseError2;
::ATL::CComPtr<IXMLDOMDocument> document;
::ATL::CComPtr<MSXML2::IXMLDOMDocument2> spXMLDOMDoc2;
::ATL::CComPtr<MSXML2::IXMLDOMSchemaCollection> spIXMLDOMSchemaCollection;
try
{
hr = g_XmlDomClassFactory->CreateInstance(NULL, __uuidof(document), (void**)&document);
if (FAILED(hr))
{
Error("msxml.CreateInstance(document) hr=%lx\n", hr);
throw hr;
}
if (document != NULL)
{
static_cast<IUnknown*>(document)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(document)->AddRef(); // jaykrell hack to try to avoid crash
}
//
// If they're willing to deal with bad XML, then so be it.
//
// First pass - validating the manifest itself alone
PrintOutMode("Validating the manifest as XML file...\n");
hr = document->put_async(VARIANT_FALSE);
if (FAILED(hr))
throw hr;
hr = document->put_validateOnParse(VARIANT_FALSE);
if (FAILED(hr))
throw hr;
hr = document->put_resolveExternals(VARIANT_FALSE);
if (FAILED(hr))
throw hr;
line = __LINE__;
CFileStreamBase* fsbase = new CFileStreamBase; // jaykrell leak out of paranoia
fsbase->AddRef(); // jaykrell leak out of paranoia
fsbase->AddRef(); // jaykrell leak out of paranoia
fsbase->AddRef(); // jaykrell leak out of paranoia
::ATL::CComPtr<IStream> istream = fsbase;
if (!fsbase->OpenForRead(SourceManName))
{
lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
Error("OpenForRead(%ls) lastError=%lu\n", SourceManName, lastError);
throw hr;
}
hr = document->load(::ATL::CComVariant(istream), &vb);
if (FAILED(hr) || vb == VARIANT_FALSE)
{
if (vb == VARIANT_FALSE)
PrintOutMode("Well Formed XML Validation: FAILED\n");
{
HRESULT loc_hr = document->get_parseError(&pParseError);
if (pParseError != NULL)
{
static_cast<IUnknown*>(pParseError)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(pParseError)->AddRef(); // jaykrell hack to try to avoid crash
}
if (g_fInBuildProcess)
PrintErrorDuringBuildProcess(pParseError);
else
PrintError(pParseError);
}
throw hr;
}
else
PrintOutMode("Well Formed XML Validation: Passed\n");
// Second pass - validating manifest against schema
PrintOutMode("\nNow validating manifest against XML Schema file...\n");
// CreateInstance creates you an instance of the object you requested above, and puts
// the pointer in the out param. Think of this like CoCreateInstance, but knowing who
// is going
hr = g_XmlDomClassFactory->CreateInstance(NULL, __uuidof(spXMLDOMDoc2), (void**)&spXMLDOMDoc2);
if (FAILED(hr))
{
PrintOutMode("Failed creating IXMLDOMDoc2...\n");
throw hr;
}
static_cast<IUnknown*>(spXMLDOMDoc2)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(spXMLDOMDoc2)->AddRef(); // jaykrell hack to try to avoid crash
hr = spXMLDOMDoc2->put_async(VARIANT_FALSE);
if (FAILED(hr))
throw hr;
hr = spXMLDOMDoc2->put_validateOnParse(VARIANT_TRUE); //changed - was FALSE
if (FAILED(hr))
throw hr;
hr = spXMLDOMDoc2->put_resolveExternals(VARIANT_FALSE);
if (FAILED(hr))
throw hr;
hr = g_XmlSchemaCacheClassFactory->CreateInstance(NULL, __uuidof(spIXMLDOMSchemaCollection), (void**)&spIXMLDOMSchemaCollection);
if (FAILED(hr))
{
PrintOutMode("Failed creating IXMLDOMSchemaCollection...\n");
throw hr;
}
static_cast<IUnknown*>(spIXMLDOMSchemaCollection)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(spIXMLDOMSchemaCollection)->AddRef(); // jaykrell hack to try to avoid crash
if ((FAILED(hr) || !spIXMLDOMSchemaCollection))
throw hr;
hr = spIXMLDOMSchemaCollection->add(
::ATL::CComBSTR(L"urn:schemas-microsoft-com:asm.v1"),
::ATL::CComVariant(SchemaName));
if(FAILED(hr))
{
PrintOutMode("BAD SCHEMA file.\n");
throw hr;
}
static_cast<IUnknown*>(spIXMLDOMSchemaCollection)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(spIXMLDOMSchemaCollection)->AddRef(); // jaykrell hack to try to avoid crash
// ownership of the idispatch/variant-by-value is not clear
::ATL::CComVariant varValue(::ATL::CComQIPtr<IDispatch>(spIXMLDOMSchemaCollection).Detach());
hr = spXMLDOMDoc2->putref_schemas(varValue);
// The document will load only if a valid schema is
// attached to the xml file.
// jaykrell leak here because ownership isn't clear
hr = spXMLDOMDoc2->load(::ATL::CComVariant(::ATL::CComBSTR(SourceManName).Copy()), &sResult);
if (FAILED(hr) || sResult == VARIANT_FALSE)
{
PrintOutMode("Manifest Schema Validation: FAILED\n");
if (sResult == VARIANT_FALSE)
{
HRESULT loc_hr = spXMLDOMDoc2->get_parseError(&pParseError2);
if (pParseError2 != NULL)
{
static_cast<IUnknown*>(pParseError2)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(pParseError2)->AddRef(); // jaykrell hack to try to avoid crash
}
if (g_fInBuildProcess)
PrintErrorDuringBuildProcess(pParseError2);
else
PrintError(pParseError2);
bResult = FALSE;
}
else
{
throw hr;
}
}
else
{
PrintOutMode("Manifest Schema Validation: Passed\n");
bResult = TRUE;
}
}
catch(HRESULT hr)
{
bResult = FALSE;
if (E_NOINTERFACE == hr)
{
Error("*** Error *** No such interface supported! \n");
}
else
{
::ATL::CComPtr<IErrorInfo> pErrorInfo;
HRESULT loc_hr = GetErrorInfo(0, &pErrorInfo);
if (pErrorInfo != NULL)
{
static_cast<IUnknown*>(pErrorInfo)->AddRef(); // jaykrell hack to try to avoid crash
static_cast<IUnknown*>(pErrorInfo)->AddRef(); // jaykrell hack to try to avoid crash
}
if ((S_OK == loc_hr) && pErrorInfo != NULL)
{
::ATL::CComQIPtr<IXMLError> pXmlError(pErrorInfo);
XML_ERROR xError;
::ATL::CComBSTR errSource;
::ATL::CComBSTR errDescr;
pErrorInfo->GetDescription(&errDescr);
pErrorInfo->GetSource(&errSource);
Error("*** ERROR *** generated by %ls\n", static_cast<PCWSTR>(errSource));
Error("*** ERROR *** description: %ls\n", static_cast<PCWSTR>(errDescr));
if (pXmlError)
{
pXmlError->GetErrorInfo(&xError);
Error("*** ERROR *** document line %d, text '%.*ls'\n", xError._nLine, xError._pchBuf, xError._cchBuf);
}
}
else
{
if (hr == CO_E_CLASSSTRING)
{
Error("*** Error *** hr returned: CO_E_CLASSSTRING, value %x\n", hr);
Error(" msg: The registered CLSID for the ProgID is invalid.\n");
}
else
{
Error("*** Error *** Cannot obtain additional error info hr=%lx!\n", static_cast<unsigned long>(hr));
}
}
}
}
return bResult;
}
BOOL IsValidCommandLineArgs(int argc, wchar_t** argv, ::ATL::CComBSTR& szwcharSchemaTmp, ::ATL::CComBSTR& szwcharManTmp)
{
// check commandline args a little
int nOnlyAllowFirstTimeReadFlag = 0; //Manifest = 0x01 Schema = 0x02 Quiet = 0x04
if((4 >= argc) && (3 <= argc))
{
//now check actual values
for (int i = 1; i < argc; i++)
{
if (argv[i][0] == L'/')
{
switch (argv[i][1])
{
case L'?': return FALSE; break;
case L'q': case L'Q':
if(0x04 & nOnlyAllowFirstTimeReadFlag)
return FALSE;
else
g_nrunFlags |= XMLCHK_FLAG_SILENT;
nOnlyAllowFirstTimeReadFlag = 0x04;
break;
case L'm': case L'M':
if (argv[i][2] == L':')
{
if(0x01 & nOnlyAllowFirstTimeReadFlag)
return FALSE;
else
szwcharManTmp = &argv[i][3];
nOnlyAllowFirstTimeReadFlag = 0x01;
break;
}
else
{
return FALSE;
}
case L's': case L'S':
if (argv[i][2] == L':')
{
if(0x02 & nOnlyAllowFirstTimeReadFlag)
return FALSE;
else
szwcharSchemaTmp = &argv[i][3];
nOnlyAllowFirstTimeReadFlag = 0x02;
break;
}
else
{
return FALSE;
}
case L'B': case L'b':
g_fInBuildProcess = true;
break;
default:
return FALSE;
}
}
else
return FALSE;
}
if ((0 == szwcharSchemaTmp[0]) ||
(0 == szwcharManTmp[0]))
{
return FALSE;
}
return TRUE;
}
else
{
return FALSE;
}
}
void PrintUsage()
{
printf("\n");
printf("Validates Fusion Win32 Manifest files using a schema.");
printf("\n");
printf("Usage:");
printf(" FusionManifestValidator /S:[drive:][path]schema_filename /M:[drive:][path]xml_manifest_filename [/Q]\n\n");
printf(" /S: Specify schema filename used to validate manifest\n");
printf(" /M: Specify manifest filename to validate\n");
printf(" /Q Quiet mode - suppresses output to console\n");
printf(" \n");
printf(" The tool without /Q displays details of first encountered error\n");
printf(" (if errors are present in manifest), and displays Pass or Fail\n");
printf(" of the validation result. The application returns 0 for Pass,\n");
printf(" 1 for Fail, and returns 2 for bad command line argument.\n");
}
int __cdecl wmain(int argc, wchar_t** argv)
{
int iValidationResult = 0;
#if !defined(_WIN64)
g_Version = GetVersion();
g_IsNt = ((g_Version & 0x80000000) == 0);
g_Version = ((g_Version >> 8) & 0xFF) | ((g_Version & 0xFF) << 8);
//printf("%x\n", g_Version);
#endif
// Start COM
CoInitialize(NULL);
if (!IsValidCommandLineArgs(argc, argv, szwcharSchemaTmp, szwcharManTmp))
{
PrintUsage();
iValidationResult = 2; //return error value 2 for CommandLine Arg error
}
else
{
PrintOutMode("Schema is: %ls\n", static_cast<PCWSTR>(szwcharSchemaTmp));
PrintOutMode("Manifest is: %ls\n\n", static_cast<PCWSTR>(szwcharManTmp));
if (InitializeMSXML3())
{
BOOL bResult = Validating(szwcharManTmp, szwcharSchemaTmp);
if (bResult)
PrintOutMode("\nOverall Validation PASSED.\n");
else
{
Error("Overall Validation FAILED, CommandLine=%ls.\n", GetCommandLineW());
iValidationResult = 1; //return error value 1 for Validation routine error
}
}
else
{
//
// If running on less than Windows XP, just claim success.
//
if (IsAtLeastXp())
{
Error("Unable to load MSXML3\n");
iValidationResult = 3;
}
else
PrintOutMode("\nMsXml3 not always available downlevel, just claim overall Validation PASSED.\n");
}
}
// Stop COM
CoUninitialize();
// TerminateProcess(GetCurrentProcess(), iValidationResult);
return iValidationResult;
}