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.
738 lines
22 KiB
738 lines
22 KiB
#include "stdinc.h"
|
|
|
|
HRESULT
|
|
SxspExtractPathPieces(
|
|
_bstr_t bstSourceName,
|
|
_bstr_t &bstPath,
|
|
_bstr_t &bstName
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
PCWSTR cwsOriginal = static_cast<PCWSTR>(bstSourceName);
|
|
PWSTR cwsSlashSpot;
|
|
|
|
cwsSlashSpot = wcsrchr( cwsOriginal, L'\\' );
|
|
if ( cwsSlashSpot )
|
|
{
|
|
*cwsSlashSpot = L'\0';
|
|
bstName = _bstr_t( cwsSlashSpot + 1 );
|
|
bstPath = cwsSlashSpot;
|
|
}
|
|
else
|
|
{
|
|
bstPath = L"";
|
|
bstName = bstSourceName;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SxspSimplifyPutAttribute(
|
|
ISXSManifestPtr Document,
|
|
::ATL::CComPtr<IXMLDOMNamedNodeMap> Attributes,
|
|
const wstring AttribName,
|
|
const wstring Value,
|
|
const wstring NamespaceURI
|
|
)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMNode> pAttribNode;
|
|
::ATL::CComPtr<IXMLDOMAttribute> pAttribActual;
|
|
::ATL::CComPtr<IXMLDOMNode> pTempNode;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Get the attribute from our namespace
|
|
//
|
|
hr = Attributes->getQualifiedItem(
|
|
_bstr_t(AttribName.c_str()),
|
|
_bstr_t(NamespaceURI.c_str()),
|
|
&pAttribNode);
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
//
|
|
// If we had success, but the attribute node is null, then we have to
|
|
// go create one, which is tricky.
|
|
//
|
|
if ( pAttribNode == NULL )
|
|
{
|
|
VARIANT vt;
|
|
vt.vt = VT_INT;
|
|
vt.intVal = NODE_ATTRIBUTE;
|
|
|
|
//
|
|
// Do the actual creation part
|
|
//
|
|
hr = Document->createNode(
|
|
vt,
|
|
_bstr_t(AttribName.c_str()),
|
|
_bstr_t(NamespaceURI.c_str()),
|
|
&pTempNode);
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
wstringstream ss;
|
|
ss << wstring(L"Can't create the new attribute node ") << AttribName;
|
|
ReportError( ErrorFatal, ss );
|
|
goto lblGetOut;
|
|
}
|
|
|
|
//
|
|
// Now we go and put that item into the map.
|
|
//
|
|
if ( FAILED( hr = Attributes->setNamedItem( pTempNode, &pAttribNode ) ) )
|
|
goto lblGetOut;
|
|
}
|
|
|
|
hr = pAttribNode->put_text( _bstr_t(Value.c_str()) );
|
|
}
|
|
|
|
lblGetOut:
|
|
// SAFERELEASE( pAttribNode );
|
|
// SAFERELEASE( pTempNode );
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SxspSimplifyGetAttribute(
|
|
::ATL::CComPtr<IXMLDOMNamedNodeMap> Attributes,
|
|
wstring AttribName,
|
|
wstring &Destination,
|
|
wstring NamespaceURI
|
|
)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMNode> NodeValue;
|
|
HRESULT hr = S_OK;
|
|
BSTR _bst_pretemp;
|
|
|
|
Destination = L"";
|
|
|
|
if ( FAILED( hr = Attributes->getNamedItem(
|
|
_bstr_t(AttribName.c_str()),
|
|
&NodeValue
|
|
) ) )
|
|
{
|
|
goto lblBopOut;
|
|
}
|
|
else if ( NodeValue == NULL )
|
|
{
|
|
goto lblBopOut;
|
|
}
|
|
else
|
|
{
|
|
if ( FAILED( hr = NodeValue->get_text( &_bst_pretemp ) ) )
|
|
{
|
|
goto lblBopOut;
|
|
}
|
|
Destination = _bstr_t(_bst_pretemp,FALSE);
|
|
}
|
|
|
|
lblBopOut:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
wostream& operator<<(
|
|
wostream& ost,
|
|
const CPostbuildProcessListEntry& thing
|
|
)
|
|
{
|
|
ost << wstring(L"(path=") << thing.manifestFullPath.c_str()
|
|
<< wstring(L" name=")<< thing.name.c_str()
|
|
<< wstring(L" version=") << thing.version.c_str()
|
|
<< wstring(L" language=") << thing.language.c_str() << wstring(L")");
|
|
return ost;
|
|
}
|
|
|
|
bool g_bDisplaySpew = false, g_bDisplayWarnings = true;
|
|
|
|
|
|
bool FileExists( const wstring& str )
|
|
{
|
|
return (GetFileAttributesW(str.c_str()) != -1) &&
|
|
((GetLastError() == ERROR_FILE_NOT_FOUND) || (GetLastError() == ERROR_PATH_NOT_FOUND));
|
|
}
|
|
|
|
wstring JustifyPath( const wstring& str )
|
|
{
|
|
vector<WCHAR> vec;
|
|
DWORD dwCount = GetLongPathNameW( str.c_str(), NULL, 0 );
|
|
|
|
if (dwCount == 0)
|
|
{
|
|
wstringstream ws;
|
|
|
|
dwCount = ::GetLastError();
|
|
|
|
ws << wstring(L"Unable to get the length of the 'long name' of ") << str
|
|
<< wstring(L" error ") << dwCount;
|
|
ReportError( ErrorFatal, ws);
|
|
exit(1);
|
|
}
|
|
|
|
vec.resize( dwCount );
|
|
GetLongPathNameW( str.c_str(), &vec.front(), vec.size() );
|
|
|
|
return wstring( &vec.front() );
|
|
}
|
|
|
|
|
|
void CPostbuildProcessListEntry::setManifestLocation( wstring root, wstring where )
|
|
{
|
|
manifestFullPath = root + L"\\" + where;
|
|
manifestPathOnly = manifestFullPath.substr( 0, manifestFullPath.find_last_of( L'\\' ) );
|
|
manifestFileName = manifestFullPath.substr( manifestFullPath.find_last_of( L'\\' ) + 1 );
|
|
|
|
if ( !FileExists( manifestFullPath ) )
|
|
{
|
|
wstringstream ss;
|
|
ss << wstring(L"Referenced manifest ") << where << wstring(L" does not exist.");
|
|
ReportError(ErrorSpew, ss);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Converts a series of strings, foo=bar chunklets, space-seperated, into a map
|
|
// from 'foo' to 'bar'
|
|
//
|
|
StringStringMap
|
|
MapFromDefLine(const wstring& source, wchar_t wchBreakValue)
|
|
{
|
|
wstring::const_iterator here = source.begin();
|
|
StringStringMap rvalue;
|
|
|
|
//
|
|
//
|
|
// The tricky bit is that there could be spaces in quoted strings...
|
|
//
|
|
while ( here != source.end() )
|
|
{
|
|
wstring tag, value;
|
|
wchar_t end_of_value = L' ';
|
|
wstring::const_iterator equals;
|
|
|
|
//
|
|
// Look for an equals first
|
|
//
|
|
equals = find( here, source.end(), L'=' );
|
|
|
|
//
|
|
// If there is no equals sign, stop.
|
|
//
|
|
if (equals == source.end())
|
|
break;
|
|
|
|
tag.assign( here, equals );
|
|
|
|
//
|
|
// Hop over the equals
|
|
//
|
|
here = equals;
|
|
here++;
|
|
|
|
//
|
|
// If the equals sign was the last character in the wstring, stop.
|
|
//
|
|
if (here == source.end())
|
|
break;
|
|
|
|
//
|
|
// Is 'here' at an open quote? Then extract everything to the next
|
|
// quote, remembering to skip this quote as well
|
|
//
|
|
if ( *here == L'\"' )
|
|
{
|
|
end_of_value = L'\"';
|
|
here++;
|
|
|
|
//
|
|
// If the quote was the last character in the wstring, stop.
|
|
//
|
|
if (here == source.end())
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now go and look for the end of the wstring, or a quote or a space.
|
|
//
|
|
wstring::const_iterator fullstring = find( here, source.end(), end_of_value );
|
|
|
|
value.assign( here, fullstring );
|
|
|
|
//
|
|
// If it was a quote or a space, skip it. If end of wstring, stay put.
|
|
//
|
|
if (fullstring != source.end())
|
|
here = fullstring + 1;
|
|
|
|
rvalue.insert( pair<wstring,wstring>( tag, value ) );
|
|
|
|
//
|
|
// Skip whitespace, but stop if we hit the end of the wstring.
|
|
//
|
|
while (here != source.end() && (*here == L' ' || *here == L'\t' || *here == '\n' || *here == '\r' || iswspace(*here)))
|
|
here++;
|
|
}
|
|
|
|
return rvalue;
|
|
}
|
|
|
|
|
|
const wstring c_wsp_usage = wstring(L"-?");
|
|
const wstring c_wsp_nologo = wstring(L"-nologo");
|
|
const wstring c_wsp_verbose = wstring(L"-verbose");
|
|
const wstring c_wsp_manifest = wstring(L"-manifest");
|
|
const wstring c_wsp_hashupdate = wstring(L"-hashupdate");
|
|
const wstring c_wsp_makecdfs = wstring(L"-makecdfs");
|
|
const wstring c_wsp_razzle = wstring(L"-razzle");
|
|
const wstring c_wsp_binplacelog = wstring(L"-binplacelog");
|
|
const wstring c_wsp_asmsroot = wstring(L"-asmsroot");
|
|
const wstring c_wsp_cdfpath = wstring(L"-cdfpath");
|
|
const wstring c_wsp_version = wstring(L"-version");
|
|
const wstring c_wsp_name = wstring(L"-name");
|
|
const wstring c_wsp_language = wstring(L"-language");
|
|
const wstring c_wsp_type = wstring(L"-type");
|
|
const wstring c_wsp_pkt = wstring(L"-publickeytoken");
|
|
const wstring c_wsp_procarch = wstring(L"-processorarchitecture");
|
|
const wstring c_wsp_depends = wstring(L"-dependency");
|
|
const wstring c_wsp_freshassembly = wstring(L"-freshassembly");
|
|
|
|
|
|
CParameters::CParameters() : m_fVerbose(false), m_fNoLogo(false), m_fUpdateHash(false), m_fUsage(false),
|
|
m_fCreateCdfs(false), m_fDuringRazzle(false), m_fSingleItem(false), m_fCreateNewAssembly(false)
|
|
{
|
|
}
|
|
|
|
bool
|
|
CParameters::ChunkifyParameters(UINT uiP, WCHAR** ppwszParams)
|
|
{
|
|
for (UINT i = 0; i < uiP; i++)
|
|
{
|
|
this->m_Parameters.push_back(wstring(ppwszParams[i]));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
CParameters::ParseDependentString(const wstring & ws, CSimpleIdentity & target)
|
|
{
|
|
wstring::const_iterator here = ws.begin();
|
|
wstring::const_iterator i;
|
|
|
|
target.wsLanguage = wstring(L"*");
|
|
target.wsProcessorArchitecture = wstring(L"*");
|
|
target.wsName = target.wsPublicKeyToken = target.wsType = target.wsVersion = wstring(L"");
|
|
|
|
//
|
|
// Find the name part of the identity
|
|
//
|
|
if ((i = find(ws.begin(), ws.end(), L',')) == ws.end())
|
|
return false;
|
|
|
|
target.wsName = wstring(ws.begin(), i);
|
|
|
|
here = i + 1;
|
|
|
|
//
|
|
// Now, let's look for the name:namespace= part of the name-value pair
|
|
//
|
|
while (here != ws.end())
|
|
{
|
|
std::wstring wsThisAttributePart;
|
|
std::wstring wsValueName;
|
|
std::wstring wsValueValue;
|
|
std::wstring wsValueNamespace;
|
|
std::wstring::const_iterator close_quote;
|
|
|
|
i = std::find(here, ws.end(), L'=');
|
|
if (i == ws.end())
|
|
return false;
|
|
|
|
wsThisAttributePart = wstring(here, i++);
|
|
|
|
//
|
|
// The cursor had better not be at the end of the string yet.
|
|
//
|
|
if (i == ws.end())
|
|
return false;
|
|
|
|
// No namespace thing in this string, so we can skip it
|
|
if (find(wsThisAttributePart.begin(), wsThisAttributePart.end(), L':') == wsThisAttributePart.end())
|
|
{
|
|
wsValueName = wsThisAttributePart;
|
|
}
|
|
|
|
//
|
|
// i should be at the quote right after the =. Make sure of that.
|
|
//
|
|
if (*i++ != L'\'')
|
|
return false;
|
|
|
|
close_quote = find(i, ws.end(), L'\'');
|
|
if (close_quote == ws.end())
|
|
return false;
|
|
|
|
wsValueValue = wstring(i, close_quote);
|
|
|
|
static const std::wstring c_ws_version = wstring(L"version");
|
|
static const std::wstring c_ws_language = wstring(L"language");
|
|
static const std::wstring c_ws_publicKeyToken = wstring(L"publicKeyToken");
|
|
static const std::wstring c_ws_type = wstring(L"type");
|
|
static const std::wstring c_ws_processorArchitecture = wstring(L"processorArchitecture");
|
|
|
|
if (wsValueNamespace.length() == 0)
|
|
{
|
|
if (wsValueName == c_ws_version) {
|
|
target.wsVersion = wsValueValue;
|
|
}
|
|
else if (wsValueName == c_ws_language) {
|
|
target.wsLanguage = wsValueValue;
|
|
}
|
|
else if (wsValueName == c_ws_publicKeyToken) {
|
|
target.wsPublicKeyToken = wsValueValue;
|
|
}
|
|
else if (wsValueName == c_ws_type) {
|
|
target.wsType = wsValueValue;
|
|
}
|
|
else if (wsValueName == c_ws_processorArchitecture) {
|
|
target.wsProcessorArchitecture = wsValueValue;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
target.OtherValues.push_back(
|
|
CSimpleIdentity::CUnknownIdentityThing(
|
|
wsValueNamespace,
|
|
wsValueName,
|
|
wsValueValue));
|
|
}
|
|
|
|
here = close_quote + 1;
|
|
if (here == ws.end())
|
|
break;
|
|
else if (*here != L',')
|
|
return false;
|
|
|
|
here++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
CParameters::SetParametersResult
|
|
CParameters::SetComandLine(UINT uiParameters, WCHAR** wszParameters)
|
|
{
|
|
|
|
std::vector<wstring>::const_iterator ci;
|
|
|
|
class CMatching {
|
|
public:
|
|
const wstring &wsparam;
|
|
wstring &wstarget;
|
|
CMatching(const wstring& p, wstring &t) : wsparam(p), wstarget(t) { }
|
|
};
|
|
|
|
CMatching SingleParamThings[] = {
|
|
CMatching(c_wsp_binplacelog, m_BinplaceLog),
|
|
CMatching(c_wsp_cdfpath, m_CdfOutputPath),
|
|
CMatching(c_wsp_asmsroot, m_AsmsRoot),
|
|
CMatching(c_wsp_manifest, m_SingleEntry.wsManifestPath),
|
|
CMatching(c_wsp_version, m_SingleEntry.wsVersion),
|
|
CMatching(c_wsp_name, m_SingleEntry.wsName),
|
|
CMatching(c_wsp_language, m_SingleEntry.wsLanguage),
|
|
CMatching(c_wsp_procarch, m_SingleEntry.wsProcessorArchitecture),
|
|
CMatching(c_wsp_type, m_SingleEntry.wsType),
|
|
CMatching(c_wsp_pkt, m_SingleEntry.wsPublicKeyToken)
|
|
};
|
|
|
|
if (!ChunkifyParameters(uiParameters, wszParameters))
|
|
{
|
|
return eCommandLine_usage;
|
|
}
|
|
|
|
for (ci = m_Parameters.begin(); ci != m_Parameters.end(); ci++)
|
|
{
|
|
wstring ws = *ci;
|
|
|
|
m_fVerbose |= (*ci == c_wsp_verbose);
|
|
m_fNoLogo |= (*ci == c_wsp_nologo);
|
|
m_fUpdateHash |= (*ci == c_wsp_hashupdate);
|
|
m_fCreateCdfs |= (*ci == c_wsp_makecdfs);
|
|
m_fDuringRazzle |= (*ci == c_wsp_razzle);
|
|
m_fCreateNewAssembly |= (*ci == c_wsp_freshassembly);
|
|
|
|
//
|
|
// Things that have a single parameter
|
|
//
|
|
for (int i = 0; i < NUMBER_OF(SingleParamThings); i++)
|
|
{
|
|
if (SingleParamThings[i].wsparam == *ci)
|
|
{
|
|
std::vector<wstring>::const_iterator cinext = ci + 1;
|
|
|
|
if ((cinext == m_Parameters.end()) || ((*cinext).at(0) == L'-'))
|
|
{
|
|
std::wstringstream wss;
|
|
wss << *ci << wstring(L" requires an extra parameter");
|
|
ReportError(ErrorFatal, wss);
|
|
return eCommandLine_usage;
|
|
}
|
|
|
|
SingleParamThings[i].wstarget = *cinext;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Injecting a dependency gathers up a 'textual identity' string that's
|
|
// turned into an assemblyIdentity statement and added to the dependency
|
|
//
|
|
if (*ci == c_wsp_depends)
|
|
{
|
|
CSimpleIdentity Dependent;
|
|
|
|
if ((ci + 1) == m_Parameters.end() || ((*(ci+1)).at(0) == L'-'))
|
|
{
|
|
std::wstringstream wss;
|
|
wss << *ci << wstring(L" needs a parameter blob");
|
|
ReportError(ErrorFatal, wss);
|
|
return eCommandLine_usage;
|
|
}
|
|
|
|
// These can be defaulted
|
|
Dependent.wsLanguage = Dependent.wsProcessorArchitecture = wstring(L"*");
|
|
if (ParseDependentString(*++ci, Dependent))
|
|
{
|
|
this->m_InjectDependencies.push_back(Dependent);
|
|
}
|
|
else
|
|
{
|
|
std::wstringstream wss;
|
|
wss << *ci << wstring(L" isn't a valid dependency statement");
|
|
ReportError(ErrorFatal, wss);
|
|
return eCommandLine_usage;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Was there a single manifestish thing?
|
|
//
|
|
if (this->m_SingleEntry.wsManifestPath.length() != 0)
|
|
{
|
|
WCHAR wchBlob[MAX_PATH];
|
|
this->m_fSingleItem = TRUE;
|
|
|
|
GetCurrentDirectoryW(NUMBER_OF(wchBlob), wchBlob);
|
|
m_SinglePostbuildItem.setManifestLocation(wstring(wchBlob), m_SingleEntry.wsManifestPath);
|
|
m_SinglePostbuildItem.language = m_SingleEntry.wsLanguage;
|
|
m_SinglePostbuildItem.version = m_SingleEntry.wsVersion;
|
|
m_SinglePostbuildItem.name = m_SingleEntry.wsName;
|
|
}
|
|
|
|
return m_fNoLogo ? eCommandLine_nologo : eCommandLine_normal;
|
|
}
|
|
|
|
|
|
std::string ConvertWString(const wstring& src)
|
|
{
|
|
vector<CHAR> chBuffer;
|
|
int iChars;
|
|
|
|
iChars = WideCharToMultiByte(CP_ACP, 0, src.c_str(), -1, NULL, 0, NULL, NULL);
|
|
if (iChars > 0)
|
|
{
|
|
chBuffer.resize(iChars);
|
|
WideCharToMultiByte(CP_ACP, 0, src.c_str(), -1, &chBuffer.front(), chBuffer.size(), NULL, NULL);
|
|
}
|
|
|
|
return std::string(&chBuffer.front());
|
|
}
|
|
|
|
|
|
std::wstring ConvertString(const std::string& source)
|
|
{
|
|
std::vector<WCHAR> wch;
|
|
wch.resize( MultiByteToWideChar( CP_ACP, 0, source.data(), source.size(), NULL, 0 ) );
|
|
MultiByteToWideChar( CP_ACP, 0, source.data(), source.size(), &wch.front(), wch.size() );
|
|
|
|
return std::wstring( wch.begin(), wch.end() );
|
|
}
|
|
|
|
bool AllWhitespace(const wstring& ws)
|
|
{
|
|
return (ws.find_first_not_of(L"\r\n \t") == wstring::npos);
|
|
}
|
|
|
|
//
|
|
// Strip out all whitespace
|
|
//
|
|
HRESULT UglifyXmlNode(::ATL::CComPtr<IXMLDOMNode> Parent)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMNode> Iterator;
|
|
HRESULT hr;
|
|
|
|
|
|
Parent->get_firstChild(&Iterator);
|
|
while (Iterator != NULL)
|
|
{
|
|
DOMNodeType NodeType;
|
|
::ATL::CComPtr<IXMLDOMNode> ThisNode;
|
|
|
|
ThisNode = Iterator;
|
|
Iterator = NULL;
|
|
|
|
//
|
|
// Move the iterator forward one, but keep ThisNode put
|
|
//
|
|
if (FAILED(hr = ThisNode->get_nextSibling(&Iterator)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = ThisNode->get_nodeType(&NodeType)))
|
|
return hr;
|
|
|
|
if (NodeType == NODE_TEXT)
|
|
{
|
|
BSTR bst;
|
|
|
|
if (FAILED(hr = ThisNode->get_text(&bst)))
|
|
return hr;
|
|
|
|
if (AllWhitespace(wstring(_bstr_t(bst, false))))
|
|
{
|
|
if (FAILED(hr = Parent->removeChild(ThisNode, NULL)))
|
|
return hr;
|
|
}
|
|
}
|
|
else if (NodeType == NODE_ELEMENT)
|
|
{
|
|
if (FAILED(hr = UglifyXmlNode(ThisNode)))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT UglifyXmlDocument(ISXSManifestPtr DocumentPtr)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMElement> RootElement;
|
|
::ATL::CComPtr<IXMLDOMNode> RootNode;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Clean up all adjacent-whitespace chunklets
|
|
//
|
|
if (FAILED(hr = DocumentPtr->get_documentElement(&RootElement)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = RootElement->normalize()))
|
|
return hr;
|
|
|
|
if (FAILED(hr = RootElement.QueryInterface(&RootNode)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = UglifyXmlNode(RootNode)))
|
|
return hr;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT PrettyFormatXmlNode(::ATL::CComPtr<IXMLDOMNode> ThisNode, int iLevel)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMDocument> Document;
|
|
::ATL::CComPtr<IXMLDOMNode> pIterator;
|
|
bool fHasChildren = false;
|
|
DOMNodeType NodeType = NODE_INVALID;
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = ThisNode->get_ownerDocument(&Document)))
|
|
return hr;
|
|
|
|
|
|
ThisNode->get_firstChild(&pIterator);
|
|
while (pIterator != NULL)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMText> CreatedNode;
|
|
::ATL::CComPtr<IXMLDOMNode> pNext;
|
|
fHasChildren = true;
|
|
|
|
wstring ws = wstring(L"\r\n");
|
|
ws.append(iLevel * 2, L' ');
|
|
|
|
if (FAILED(hr = pIterator->get_nodeType(&NodeType)))
|
|
return hr;
|
|
|
|
if (NodeType != NODE_TEXT)
|
|
{
|
|
if (FAILED(hr = Document->createTextNode(_bstr_t(ws.c_str()), &CreatedNode)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = ThisNode->insertBefore(CreatedNode, _variant_t(pIterator), NULL)))
|
|
return hr;
|
|
|
|
if (FAILED(PrettyFormatXmlNode(pIterator, iLevel + 1)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = pIterator->get_nextSibling(&pNext)))
|
|
return hr;
|
|
}
|
|
|
|
pIterator = pNext;
|
|
}
|
|
|
|
if (fHasChildren && (NodeType != NODE_TEXT))
|
|
{
|
|
::ATL::CComPtr<IXMLDOMText> CreatedNode;
|
|
|
|
wstring ws = wstring(L"\r\n");
|
|
ws.append((iLevel - 1) * 2, L' ');
|
|
|
|
|
|
if (FAILED(hr = Document->createTextNode(_bstr_t(ws.c_str()), &CreatedNode)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = ThisNode->appendChild(CreatedNode, NULL)))
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT PrettyFormatXmlDocument(ISXSManifestPtr DocumentPtr)
|
|
{
|
|
::ATL::CComPtr<IXMLDOMElement> RootElement;
|
|
::ATL::CComPtr<IXMLDOMNode> RootNode;
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = DocumentPtr->get_documentElement(&RootElement)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = RootElement.QueryInterface(&RootNode)))
|
|
return hr;
|
|
|
|
if (FAILED(hr = PrettyFormatXmlNode(RootNode, 1)))
|
|
return hr;
|
|
|
|
return hr;
|
|
}
|
|
|