windows-nt/Source/XPSP1/NT/net/upnp/host/upnphost/registrar/validationmanager.cpp

880 lines
25 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: V A L I D A T I O N M A N A G E R . C P P
//
// Contents: Validates device host inputs
//
// Notes:
//
// Author: mbend 9 Oct 2000
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "uhbase.h"
#include "ValidationManager.h"
#include "uhutil.h"
#include "ncstring.h"
#include "validate.h"
#include "uhcommon.h"
// Functions declarationc
HRESULT HrValidateDevice(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString);
CValidationManager::CValidationManager ()
{
}
CValidationManager::~CValidationManager ()
{
}
HRESULT HrGetDocumentAndRootNode(
BSTR bstrTemplate,
IXMLDOMDocumentPtr & pDoc,
IXMLDOMNodePtr & pRootNode)
{
TraceTag(ttidValidate, "HrGetDocumentAndRootNode");
HRESULT hr = S_OK;
// Load document and fetch needed items
hr = HrLoadDocument(bstrTemplate, pDoc);
if(SUCCEEDED(hr))
{
hr = pRootNode.HrAttach(pDoc);
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrGetDocumentAndRootNode");
return hr;
}
struct PresenceItem
{
const wchar_t * m_szName;
bool m_bEmpty;
bool m_bSuffix;
bool m_bOptional;
LONG m_cchMax;
};
// m_szName EMPTY SUFFIX OPTONAL CCH
// ----------------------------------------------------------
const PresenceItem g_arpiDeviceItems[] =
{
{L"deviceType", false, true, false, 64},
{L"friendlyName", false, false, false, 64},
{L"manufacturer", false, false, false, 64},
{L"manufacturerURL", false, false, true, -1},
{L"modelDescription", false, false, true, 128},
{L"modelName", false, false, false, 32},
{L"modelNumber", false, false, true, 32},
{L"modelURL", false, false, true, -1},
{L"serialNumber", false, false, true, 64},
{L"UDN", false, false, false, -1},
{L"UPC", false, false, true, 12},
};
const long c_nDeviceItems = celems(g_arpiDeviceItems);
PresenceItem g_arpiServiceItems[] =
{
{L"serviceType", false, true, false, 64},
{L"serviceId", false, true, false, 64},
{L"SCPDURL", false, false, false, -1},
{L"controlURL", true, false, false, -1},
{L"eventSubURL", true, false, false, -1},
};
const long c_nServiceItems = celems(g_arpiServiceItems);
PresenceItem g_arpiIconItems[] =
{
{L"mimetype", false, false, false, -1},
{L"width", false, false, false, -1},
{L"height", false, false, false, -1},
{L"depth", false, false, false, -1},
{L"url", false, false, false, -1},
};
const long c_nIconItems = celems(g_arpiIconItems);
PresenceItem g_arpiRootItems[] =
{
{L"/root/specVersion", false, false, false, -1},
};
const long c_nRootItems = celems(g_arpiRootItems);
HRESULT HrValidateSufixes(IXMLDOMNodePtr & pNode,
const wchar_t * szName,
LONG cchMax,
CUString & strErrorString)
{
HRESULT hr = S_OK;
DWORD ctok = 0;
LPCWSTR pchText;
CUString strText;
IXMLDOMNodePtr pNodeItem;
// This function validates the serviceType, deviceType, and serviceId
// elements in the following way:
// Each of these is of the form: urn:domain-name:keyword:SUFFIX:version
// Since they all follow the same format (which currently we DO NOT
// validate), we can make an assumption that the 4th token (SUFFIX) is the
// one that we need to validate. The validation is strictly as according
// to UPnP architecture 1.0 where this suffix must be <= 64 characters in
// length.
//
hr = HrSelectNode(szName, pNode, pNodeItem);
if (SUCCEEDED(hr))
{
hr = HrGetNodeText(pNodeItem, strText);
if (SUCCEEDED(hr))
{
pchText = strText.GetBuffer();
while (*pchText)
{
if (*pchText == L':')
{
ctok++;
pchText++;
if (ctok == 3)
{
// Fourth token is the one we need to examine
LONG cch = 0;
while (*pchText && *pchText != L':')
{
pchText++;
cch++;
}
// ISSUE-2000/11/29-danielwe: We don't yet
// validate the format of the suffix
//
if (cch > cchMax)
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(),
IDS_SUFFIX_TOO_LONG),
strText);
if (SUCCEEDED(hr))
{
hr = UPNP_E_SUFFIX_TOO_LONG;
}
}
break;
}
}
else
{
pchText++;
}
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateSufixes(%S)",
strErrorString.GetLength() ? strErrorString.GetBuffer(): L"Unspecified");
return hr;
}
HRESULT HrValidatePresenceItems(
IXMLDOMNodePtr & pNode,
long nPresenceItems,
const PresenceItem * arPresenceItems,
CUString & strErrorString)
{
HRESULT hr = S_OK;
for(long n = 0; n < nPresenceItems && SUCCEEDED(hr); ++n)
{
AssertSz(FImplies(arPresenceItems[n].m_bEmpty,
arPresenceItems[n].m_cchMax == -1),
"Empty elements mean there shouldn't be a size to verify "
"against! Fix the array above!");
if(arPresenceItems[n].m_bEmpty)
{
hr = HrIsNodePresentOnceAndEmpty(arPresenceItems[n].m_szName, pNode);
if(S_OK != hr)
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(), IDS_EMPTY_NODE_NOT_PRESENT),
arPresenceItems[n].m_szName);
if(SUCCEEDED(hr))
{
hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
}
}
}
else if (!arPresenceItems[n].m_bSuffix)
{
hr = HrIsNodePresentOnceAndNotEmpty(arPresenceItems[n].m_szName, pNode);
if(S_OK != hr)
{
if (UPNP_E_DUPLICATE_NOT_ALLOWED == hr)
{
// Didn't find the item and it's not optional
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(),
IDS_DUPLICATES_NOT_ALLOWED),
arPresenceItems[n].m_szName);
if(SUCCEEDED(hr))
{
hr = UPNP_E_DUPLICATE_NOT_ALLOWED;
}
}
else if (!arPresenceItems[n].m_bOptional)
{
// Didn't find the item and it's not optional
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(),
IDS_NON_EMPTY_NODE_NOT_PRESENT),
arPresenceItems[n].m_szName);
if(SUCCEEDED(hr))
{
hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
}
}
else
{
// Element was optional
hr = S_OK;
}
}
else if (arPresenceItems[n].m_cchMax != -1)
{
// Check length if one is specified
//
hr = HrIsNodeOfValidLength(arPresenceItems[n].m_szName, pNode,
arPresenceItems[n].m_cchMax);
if(S_FALSE == hr)
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(), IDS_ELEMENT_VALUE_TOO_LONG),
arPresenceItems[n].m_szName);
if(SUCCEEDED(hr))
{
hr = UPNP_E_VALUE_TOO_LONG;
}
}
else if (arPresenceItems[n].m_bOptional && (FAILED(hr)))
{
// If item was optional, forget any errors
hr = S_OK;
}
}
}
if (SUCCEEDED(hr))
{
if (arPresenceItems[n].m_bSuffix && arPresenceItems[n].m_cchMax != -1)
{
hr = HrValidateSufixes(pNode, arPresenceItems[n].m_szName,
arPresenceItems[n].m_cchMax,
strErrorString);
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidatePresenceItems(%S)",
strErrorString.GetLength() ? strErrorString.GetBuffer(): L"Unspecified");
return hr;
}
HRESULT HrValidateDeviceService(
IXMLDOMNodePtr & pNodeService,
CUString & strErrorString)
{
HRESULT hr = S_OK;
hr = HrValidatePresenceItems(pNodeService, c_nServiceItems, g_arpiServiceItems, strErrorString);
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceService");
return hr;
}
HRESULT HrCheckForDuplicatesInList(
IXMLDOMNodeListPtr & pNodeList,
CUString & strErrorString)
{
HRESULT hr = S_OK;
CUArray<CUString> arstrValues;
while(SUCCEEDED(hr))
{
IXMLDOMNodePtr pNode;
HRESULT hrTemp = pNodeList->nextNode(pNode.AddressOf());
if(S_OK != hrTemp)
{
break;
}
CUString strText;
hr = HrGetNodeText(pNode, strText);
if(SUCCEEDED(hr))
{
long nIndex = 0;
hrTemp = arstrValues.HrFind(strText, nIndex);
if(S_OK == hrTemp)
{
// We found a duplicate
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(), IDS_DUPLICATES_NOT_ALLOWED),
strText.GetBuffer());
if(SUCCEEDED(hr))
{
hr = UPNP_E_DUPLICATE_NOT_ALLOWED;
}
}
else
{
hr = arstrValues.HrPushBack(strText);
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrCheckForDuplicatesInList");
return hr;
}
HRESULT HrValidateDeviceServices(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString)
{
HRESULT hr = S_OK;
// serviceList is required and must contain a service
BOOL bServiceNotPresent = TRUE;
HRESULT hrTemp = HrIsNodePresentOnce(L"serviceList", pNodeDevice);
if(S_OK == hrTemp)
{
IXMLDOMNodeListPtr pNodeList;
hrTemp = HrSelectNodes(L"serviceList/service", pNodeDevice, pNodeList);
if(S_OK == hrTemp)
{
while(SUCCEEDED(hr))
{
IXMLDOMNodePtr pNode;
hrTemp = pNodeList->nextNode(pNode.AddressOf());
if(S_OK != hrTemp)
{
break;
}
// We have a service
bServiceNotPresent = FALSE;
hr = HrValidateDeviceService(pNode, strErrorString);
}
}
// Make sure all ServiceId's are unique
if(SUCCEEDED(hr))
{
pNodeList.Release();
hrTemp = HrSelectNodes(L"serviceList/service/serviceId", pNodeDevice, pNodeList);
if(S_OK == hrTemp)
{
hr = HrCheckForDuplicatesInList(pNodeList, strErrorString);
}
}
}
if(bServiceNotPresent)
{
hr = strErrorString.HrAssign(
WszLoadString(_Module.GetResourceInstance(), IDS_SERVICE_MISSING));
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_SERVICE;
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceServices");
return hr;
}
HRESULT HrValidateDeviceIcon(
IXMLDOMNodePtr & pNodeIcon,
CUString & strErrorString)
{
HRESULT hr = S_OK;
hr = HrValidatePresenceItems(pNodeIcon, c_nIconItems, g_arpiIconItems, strErrorString);
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceIcon");
return hr;
}
HRESULT HrValidateDeviceIcons(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString)
{
HRESULT hr = S_OK;
HRESULT hrTemp = HrIsNodePresentOnce(L"iconList", pNodeDevice);
if(S_OK == hrTemp)
{
BOOL fGotAnIcon = FALSE;
IXMLDOMNodeListPtr pNodeList;
hrTemp = HrSelectNodes(L"iconList/icon", pNodeDevice, pNodeList);
if(S_OK == hrTemp)
{
while(SUCCEEDED(hr))
{
IXMLDOMNodePtr pNode;
hrTemp = pNodeList->nextNode(pNode.AddressOf());
if(S_OK != hrTemp)
{
break;
}
fGotAnIcon = TRUE;
hr = HrValidateDeviceIcon(pNode, strErrorString);
}
}
if (!fGotAnIcon)
{
hr = strErrorString.HrAssign(
WszLoadString(_Module.GetResourceInstance(), IDS_ICON_MISSING));
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_ICON;
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceIcons");
return hr;
}
HRESULT HrValidateDeviceChildren(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString)
{
HRESULT hr = S_OK;
BOOL fGotADevice = FALSE;
HRESULT hrTemp = HrIsNodePresentOnce(L"deviceList", pNodeDevice);
if(S_OK == hrTemp)
{
IXMLDOMNodeListPtr pNodeList;
hrTemp = HrSelectNodes(L"deviceList/device", pNodeDevice, pNodeList);
if(S_OK == hrTemp)
{
while(SUCCEEDED(hr))
{
IXMLDOMNodePtr pNode;
hrTemp = pNodeList->nextNode(pNode.AddressOf());
if(S_OK != hrTemp)
{
break;
}
fGotADevice = TRUE;
hr = HrValidateDevice(pNode, strErrorString);
}
}
if (!fGotADevice)
{
hr = strErrorString.HrAssign(
WszLoadString(_Module.GetResourceInstance(), IDS_DEVICE_MISSING));
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_DOCUMENT;
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceChildren");
return hr;
}
HRESULT HrValidateDevice(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString)
{
TraceTag(ttidValidate, "HrValidateDevice");
HRESULT hr = S_OK;
hr = HrValidatePresenceItems(pNodeDevice, c_nDeviceItems, g_arpiDeviceItems, strErrorString);
if(SUCCEEDED(hr))
{
hr = HrValidateDeviceServices(pNodeDevice, strErrorString);
if(SUCCEEDED(hr))
{
hr = HrValidateDeviceIcons(pNodeDevice, strErrorString);
if(SUCCEEDED(hr))
{
hr = HrValidateDeviceChildren(pNodeDevice, strErrorString);
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDevice");
return hr;
}
HRESULT HrValidateUDNs(
IXMLDOMNodePtr & pNodeDevice,
CUString & strErrorString)
{
HRESULT hr = S_OK;
IXMLDOMNodeListPtr pNodeList;
hr = HrSelectNodes(L"//UDN", pNodeDevice, pNodeList);
if(SUCCEEDED(hr))
{
hr = HrCheckForDuplicatesInList(pNodeList, strErrorString);
}
TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateUDNs");
return hr;
}
/*
HRESULT HrValidateDevice(
IXMLDOMNodePtr & pNodeDevice)
{
TraceTag(ttidValidate, "");
HRESULT hr = S_OK;
TraceHr(ttidValidate, FAL, hr, FALSE, "");
return hr;
}
*/
// IUPnPValidationManager methods
STDMETHODIMP CValidationManager::ValidateDescriptionDocument(
/*[in]*/ BSTR bstrTemplate,
/*[out, string]*/ wchar_t ** pszErrorString)
{
CHECK_POINTER(bstrTemplate);
CHECK_POINTER(pszErrorString);
HRESULT hr = S_OK;
CUString strErrorString;
*pszErrorString = NULL;
IXMLDOMDocumentPtr pDoc;
IXMLDOMNodePtr pRootNode;
hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
if (SUCCEEDED(hr))
{
hr = HrGetDocumentAndRootNode(bstrTemplate, pDoc, pRootNode);
}
if(SUCCEEDED(hr))
{
hr = HrValidatePresenceItems(pRootNode, c_nRootItems, g_arpiRootItems, strErrorString);
if(SUCCEEDED(hr))
{
hr = HrIsNodePresentOnce(L"/root/device", pRootNode);
if(S_OK == hr)
{
hr = HrIsNodePresentOnce(L"/root/URLBase", pRootNode);
if (S_OK != hr)
{
hr = HrValidateUDNs(pRootNode, strErrorString);
if(SUCCEEDED(hr))
{
IXMLDOMNodePtr pNodeDevice;
hr = HrSelectNode(L"/root/device", pRootNode, pNodeDevice);
if(SUCCEEDED(hr))
{
hr = HrValidateDevice(pNodeDevice, strErrorString);
}
}
}
else
{
hr = strErrorString.HrAssign(
WszLoadString(_Module.GetResourceInstance(), IDS_URLBASE_PRESENT));
if(SUCCEEDED(hr))
{
hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
}
}
}
else
{
hr = strErrorString.HrAssign(
WszLoadString(_Module.GetResourceInstance(), IDS_ROOT_DEVICE_MISSING));
if(SUCCEEDED(hr))
{
hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
}
}
}
}
// Let's make sure the root namespace is according to spec
//
if (SUCCEEDED(hr))
{
IXMLDOMNodePtr pNodeRootSub;
hr = HrSelectNode(L"/root", pRootNode, pNodeRootSub);
if (SUCCEEDED(hr))
{
BSTR bstrUri;
hr = pNodeRootSub->get_namespaceURI(&bstrUri);
if (S_OK != hr || lstrcmpi(bstrUri,
L"urn:schemas-upnp-org:device-1-0"))
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(),
IDS_INVALID_ROOT_NAMESPACE),
bstrUri);
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_ROOT_NAMESPACE;
}
}
}
}
if(FAILED(hr))
{
if(strErrorString.GetLength())
{
strErrorString.HrGetCOM(pszErrorString);
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateDescriptionDocument(%S)",
*pszErrorString ? *pszErrorString : L"Unspecified");
return hr;
}
STDMETHODIMP CValidationManager::ValidateServiceDescription(
/*[in, string]*/ const wchar_t * szFullPath,
/*[out, string]*/ wchar_t ** pszErrorString)
{
CHECK_POINTER(szFullPath);
CHECK_POINTER(pszErrorString);
HRESULT hr = S_OK;
CUString strErrorString;
*pszErrorString = NULL;
BSTR bstrPath;
IXMLDOMDocumentPtr pDoc;
hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
if (SUCCEEDED(hr))
{
bstrPath = SysAllocString(szFullPath);
}
if (bstrPath)
{
hr = HrLoadDocumentFromFile(bstrPath, pDoc);
if (SUCCEEDED(hr))
{
IXMLDOMElementPtr pxdeSDRoot;
hr = pDoc->get_documentElement(pxdeSDRoot.AddressOf());
if (S_OK == hr)
{
hr = HrValidateServiceDescription(pxdeSDRoot, pszErrorString);
}
}
else
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(), IDS_INVALID_XML),
szFullPath);
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_XML;
}
}
SysFreeString(bstrPath);
}
else
{
hr = E_OUTOFMEMORY;
}
if(FAILED(hr))
{
if(strErrorString.GetLength())
{
strErrorString.HrGetCOM(pszErrorString);
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateServiceDescription(%S)",
*pszErrorString ? *pszErrorString : L"Unspecified");
return hr;
}
HRESULT CValidationManager::ValidateServiceDescriptions(const wchar_t * szResourcePath,
IXMLDOMNodePtr pRootNode,
wchar_t ** pszErrorString)
{
HRESULT hr = S_OK;
IXMLDOMNodeListPtr pNodeList;
// Select all of the SCPDURL nodes in the description document
hr = HrSelectNodes(L"//device/serviceList/service/SCPDURL",
pRootNode, pNodeList);
while (S_OK == hr)
{
IXMLDOMNodePtr pNode;
CUString strUrl;
hr = pNodeList->nextNode(pNode.AddressOf());
if (S_OK == hr)
{
hr = HrGetNodeText(pNode, strUrl);
if (SUCCEEDED(hr))
{
CUString strFullPath;
hr = HrMakeFullPath(szResourcePath, strUrl, strFullPath);
if (SUCCEEDED(hr))
{
hr = ValidateServiceDescription(strFullPath,
pszErrorString);
}
}
}
}
if (SUCCEEDED(hr))
{
// normalize error code
hr = S_OK;
}
TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateServiceDescriptions(%S)",
*pszErrorString ? *pszErrorString : L"Unspecified");
return hr;
}
HRESULT CValidationManager::ValidateIconFiles(const wchar_t * szResourcePath,
IXMLDOMNodePtr pRootNode,
wchar_t ** pszErrorString)
{
HRESULT hr = S_OK;
IXMLDOMNodeListPtr pNodeList;
CUString strErrorString;
// Select all of the SCPDURL nodes in the description document
hr = HrSelectNodes(L"//device/iconList/icon/url", pRootNode, pNodeList);
while (S_OK == hr)
{
IXMLDOMNodePtr pNode;
CUString strUrl;
hr = pNodeList->nextNode(pNode.AddressOf());
if (S_OK == hr)
{
hr = HrGetNodeText(pNode, strUrl);
if (SUCCEEDED(hr))
{
CUString strFullPath;
hr = HrMakeFullPath(szResourcePath, strUrl, strFullPath);
if (SUCCEEDED(hr))
{
if (!FFileExists((LPTSTR)strFullPath.GetBuffer(), FALSE))
{
hr = strErrorString.HrPrintf(
WszLoadString(_Module.GetResourceInstance(), IDS_INVALID_ICON),
strFullPath);
if(SUCCEEDED(hr))
{
hr = UPNP_E_INVALID_ICON;
}
}
}
}
}
}
if(FAILED(hr))
{
if(strErrorString.GetLength())
{
strErrorString.HrGetCOM(pszErrorString);
}
}
if (SUCCEEDED(hr))
{
// normalize error code
hr = S_OK;
}
TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateIconFiles(%S)",
*pszErrorString ? *pszErrorString : L"Unspecified");
return hr;
}
STDMETHODIMP CValidationManager::ValidateDescriptionDocumentAndReferences(
/*[in]*/ BSTR bstrTemplate,
/*[in, string]*/ const wchar_t * szResourcePath,
/*[out, string]*/ wchar_t ** pszErrorString)
{
CHECK_POINTER(bstrTemplate);
CHECK_POINTER(szResourcePath);
CHECK_POINTER(pszErrorString);
HRESULT hr = S_OK;
*pszErrorString = NULL;
hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
if (SUCCEEDED(hr))
{
hr = ValidateDescriptionDocument(bstrTemplate, pszErrorString);
}
if (SUCCEEDED(hr))
{
IXMLDOMDocumentPtr pDoc;
IXMLDOMNodePtr pRootNode;
hr = HrGetDocumentAndRootNode(bstrTemplate, pDoc, pRootNode);
if (SUCCEEDED(hr))
{
hr = ValidateServiceDescriptions(szResourcePath, pRootNode,
pszErrorString);
if (SUCCEEDED(hr))
{
hr = ValidateIconFiles(szResourcePath, pRootNode,
pszErrorString);
}
}
}
TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateDescriptionDocumentAndReferences(%S)",
*pszErrorString ? *pszErrorString : L"Unspecified");
return hr;
}