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.
436 lines
15 KiB
436 lines
15 KiB
#include <fusenetincludes.h>
|
|
#include <msxml2.h>
|
|
#include <manifestemit.h>
|
|
#include <manifestimport.h>
|
|
#include "macros.h"
|
|
|
|
CRITICAL_SECTION CAssemblyManifestEmit::g_cs;
|
|
|
|
// CLSID_XML DOM Document 3.0
|
|
class __declspec(uuid("f6d90f11-9c73-11d3-b32e-00c04f990bb4")) private_MSXML_DOMDocument30;
|
|
|
|
|
|
// Publics
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CreateAssemblyManifestEmit
|
|
// ---------------------------------------------------------------------------
|
|
STDAPI CreateAssemblyManifestEmit(LPASSEMBLY_MANIFEST_EMIT* ppEmit,
|
|
LPCOLESTR pwzManifestFilePath, MANIFEST_TYPE eType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
MAKE_ERROR_MACROS_STATIC(hr);
|
|
|
|
CAssemblyManifestEmit* pEmit = NULL;
|
|
|
|
IF_NULL_EXIT(ppEmit, E_INVALIDARG);
|
|
|
|
*ppEmit = NULL;
|
|
|
|
// only support emitting desktop manifest now
|
|
IF_FALSE_EXIT(eType == MANIFEST_TYPE_DESKTOP, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
|
|
|
|
pEmit = new(CAssemblyManifestEmit);
|
|
IF_ALLOC_FAILED_EXIT(pEmit);
|
|
|
|
IF_FAILED_EXIT(pEmit->Init(pwzManifestFilePath));
|
|
|
|
*ppEmit = (IAssemblyManifestEmit*) pEmit;
|
|
pEmit->AddRef();
|
|
|
|
exit:
|
|
|
|
SAFERELEASE(pEmit);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ctor
|
|
// ---------------------------------------------------------------------------
|
|
CAssemblyManifestEmit::CAssemblyManifestEmit()
|
|
: _dwSig('TMEM'), _cRef(1), _hr(S_OK), _pXMLDoc(NULL),
|
|
_pAssemblyNode(NULL), _pDependencyNode(NULL),
|
|
_pApplicationNode(NULL),_bstrManifestFilePath(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// dtor
|
|
// ---------------------------------------------------------------------------
|
|
CAssemblyManifestEmit::~CAssemblyManifestEmit()
|
|
{
|
|
SAFERELEASE(_pAssemblyNode);
|
|
SAFERELEASE(_pDependencyNode);
|
|
SAFERELEASE(_pApplicationNode);
|
|
SAFERELEASE(_pXMLDoc);
|
|
|
|
if (_bstrManifestFilePath)
|
|
::SysFreeString(_bstrManifestFilePath);
|
|
}
|
|
|
|
// IUnknown Boilerplate
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CAssemblyManifestEmit::QI
|
|
// ---------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CAssemblyManifestEmit::QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
if ( IsEqualIID(riid, IID_IUnknown)
|
|
|| IsEqualIID(riid, IID_IAssemblyManifestEmit))
|
|
{
|
|
*ppvObj = static_cast<IAssemblyManifestEmit*> (this);
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CAssemblyManifestEmit::AddRef
|
|
// ---------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG)
|
|
CAssemblyManifestEmit::AddRef()
|
|
{
|
|
return InterlockedIncrement ((LONG*) &_cRef);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CAssemblyManifestEmit::Release
|
|
// ---------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG)
|
|
CAssemblyManifestEmit::Release()
|
|
{
|
|
ULONG lRet = InterlockedDecrement ((LONG*) &_cRef);
|
|
if (!lRet)
|
|
delete this;
|
|
return lRet;
|
|
}
|
|
|
|
// Privates
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Init
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::Init(LPCOLESTR pwzManifestFilePath)
|
|
{
|
|
IF_NULL_EXIT(pwzManifestFilePath, E_INVALIDARG);
|
|
|
|
// Alloc manifest file path.
|
|
_bstrManifestFilePath = ::SysAllocString((LPWSTR) pwzManifestFilePath);
|
|
IF_ALLOC_FAILED_EXIT(_bstrManifestFilePath);
|
|
|
|
// note: DOM Doc is delayed initialized in ImportAssemblyNode() to enable sharing of BSTRs
|
|
_hr = S_OK;
|
|
|
|
exit:
|
|
return _hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// InitGlobalCritSect
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::InitGlobalCritSect()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
__try {
|
|
InitializeCriticalSection(&g_cs);
|
|
}
|
|
__except (GetExceptionCode() == STATUS_NO_MEMORY ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DelGlobalCritSect
|
|
// ---------------------------------------------------------------------------
|
|
void CAssemblyManifestEmit::DelGlobalCritSect()
|
|
{
|
|
DeleteCriticalSection(&g_cs);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ImportManifestInfo
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::ImportManifestInfo(LPASSEMBLY_MANIFEST_IMPORT pManImport)
|
|
{
|
|
DWORD dwType = MANIFEST_TYPE_UNKNOWN;
|
|
IXMLDOMDocument2 *pXMLDocSrc = NULL;
|
|
IXMLDOMNode *pIDOMNode = NULL;
|
|
IXMLDOMNode *pIDOMNodeClone = NULL;
|
|
IXMLDOMElement *pIXMLDOMElement = NULL;
|
|
|
|
VARIANT varVersionWildcard;
|
|
VARIANT varTypeDesktop;
|
|
VARIANT varRefNode;
|
|
|
|
IF_NULL_EXIT(pManImport, E_INVALIDARG);
|
|
|
|
IF_FAILED_EXIT(pManImport->ReportManifestType(&dwType));
|
|
|
|
IF_FALSE_EXIT(dwType == MANIFEST_TYPE_APPLICATION, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
|
|
|
|
IF_TRUE_EXIT(_pApplicationNode != NULL, S_FALSE);
|
|
|
|
if (_pAssemblyNode == NULL)
|
|
IF_FAILED_EXIT(ImportAssemblyNode(pManImport));
|
|
|
|
// application manifest: clone and insert 'assemblyIdentity' node (change 'version', 'type' attribute)
|
|
// and 'application' node
|
|
|
|
pXMLDocSrc = ((CAssemblyManifestImport*)pManImport)->_pXMLDoc;
|
|
|
|
// BUGBUG: this only pick the 1st instance of 'application'
|
|
IF_FAILED_EXIT(pXMLDocSrc->selectSingleNode(
|
|
CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::ApplicationNode].bstr, &pIDOMNode));
|
|
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
|
|
|
|
// clone all children
|
|
IF_FAILED_EXIT(pIDOMNode->cloneNode(VARIANT_TRUE, &pIDOMNodeClone));
|
|
|
|
VariantInit(&varRefNode);
|
|
varRefNode.vt = VT_UNKNOWN;
|
|
V_UNKNOWN(&varRefNode) = _pDependencyNode;
|
|
// insert before 'dependency', if present
|
|
IF_FAILED_EXIT(_pAssemblyNode->insertBefore(pIDOMNodeClone, varRefNode, &_pApplicationNode));
|
|
|
|
SAFERELEASE(pIDOMNodeClone);
|
|
SAFERELEASE(pIDOMNode);
|
|
|
|
// BUGBUG: this only pick the 1st instance of 'assemblyIdentity'
|
|
IF_FAILED_EXIT(pXMLDocSrc->selectSingleNode(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::AssemblyId].bstr, &pIDOMNode));
|
|
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
|
|
|
|
// clone all children
|
|
IF_FAILED_EXIT(pIDOMNode->cloneNode(VARIANT_TRUE, &pIDOMNodeClone));
|
|
|
|
SAFERELEASE(pIDOMNode);
|
|
|
|
VariantInit(&varRefNode);
|
|
varRefNode.vt = VT_UNKNOWN;
|
|
V_UNKNOWN(&varRefNode) = _pApplicationNode;
|
|
// insert before 'application'
|
|
IF_FAILED_EXIT(_pAssemblyNode->insertBefore(pIDOMNodeClone, varRefNode, &pIDOMNode));
|
|
|
|
// change 'version' = '*', 'type' = 'desktop'
|
|
IF_FAILED_EXIT(pIDOMNode->QueryInterface(IID_IXMLDOMElement, (void**) &pIXMLDOMElement));
|
|
|
|
VariantInit(&varVersionWildcard);
|
|
varVersionWildcard.vt = VT_BSTR;
|
|
V_BSTR(&varVersionWildcard) = CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::VersionWildcard].bstr;
|
|
IF_FAILED_EXIT(pIXMLDOMElement->setAttribute(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Version].bstr, varVersionWildcard));
|
|
|
|
VariantInit(&varTypeDesktop);
|
|
varTypeDesktop.vt = VT_BSTR;
|
|
V_BSTR(&varTypeDesktop) = CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Desktop].bstr;
|
|
_hr = pIXMLDOMElement->setAttribute(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Type].bstr, varTypeDesktop);
|
|
|
|
exit:
|
|
SAFERELEASE(pIXMLDOMElement);
|
|
SAFERELEASE(pIDOMNodeClone);
|
|
SAFERELEASE(pIDOMNode);
|
|
|
|
if (FAILED(_hr))
|
|
SAFERELEASE(_pApplicationNode);
|
|
|
|
return _hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SetDependencySubscription
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::SetDependencySubscription(LPASSEMBLY_MANIFEST_IMPORT pManImport, LPWSTR pwzManifestUrl)
|
|
{
|
|
DWORD dwType = MANIFEST_TYPE_UNKNOWN;
|
|
IXMLDOMDocument2 *pXMLDocSrc = NULL;
|
|
IXMLDOMNode *pIDOMNode = NULL;
|
|
IXMLDOMNode *pIDOMNodeClone = NULL;
|
|
IXMLDOMElement *pIDOMElement = NULL;
|
|
IXMLDOMNode *pDependentAssemblyNode = NULL;
|
|
|
|
VARIANT varVersionWildcard;
|
|
VARIANT varCodebase;
|
|
BSTR bstrManifestUrl = NULL;
|
|
|
|
VariantInit(&varCodebase);
|
|
|
|
IF_FALSE_EXIT(pManImport && pwzManifestUrl, E_INVALIDARG);
|
|
|
|
IF_FAILED_EXIT(pManImport->ReportManifestType(&dwType));
|
|
|
|
IF_FALSE_EXIT(dwType == MANIFEST_TYPE_SUBSCRIPTION || dwType == MANIFEST_TYPE_APPLICATION, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
|
|
|
|
IF_TRUE_EXIT(_pDependencyNode != NULL, S_FALSE);
|
|
|
|
if (_pAssemblyNode == NULL)
|
|
IF_FAILED_EXIT(ImportAssemblyNode(pManImport));
|
|
|
|
// setup manifest subscription data: create dependency/dependentAssembly
|
|
// then add a clone of the asm Id node from pManImport and an 'install' node with the given URL
|
|
IF_FAILED_EXIT(_pXMLDoc->createElement(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Dependency].bstr, &pIDOMElement));
|
|
|
|
// insert at the end
|
|
IF_FAILED_EXIT(_pAssemblyNode->appendChild(pIDOMElement, &_pDependencyNode));
|
|
|
|
SAFERELEASE(pIDOMElement);
|
|
|
|
IF_FAILED_EXIT(_pXMLDoc->createElement(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::DependentAssembly].bstr, &pIDOMElement));
|
|
|
|
IF_FAILED_EXIT(_pDependencyNode->appendChild(pIDOMElement, &pDependentAssemblyNode));
|
|
|
|
SAFERELEASE(pIDOMElement);
|
|
|
|
pXMLDocSrc = ((CAssemblyManifestImport*)pManImport)->_pXMLDoc;
|
|
|
|
// BUGBUG: this only pick the 1st instance of 'assemblyIdentity'
|
|
IF_FAILED_EXIT(pXMLDocSrc->selectSingleNode(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::AssemblyId].bstr, &pIDOMNode));
|
|
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
|
|
|
|
// clone all children
|
|
IF_FAILED_EXIT(pIDOMNode->cloneNode(VARIANT_TRUE, &pIDOMNodeClone));
|
|
|
|
// change 'version' = '*'
|
|
IF_FAILED_EXIT(pIDOMNodeClone->QueryInterface(IID_IXMLDOMElement, (void**) &pIDOMElement));
|
|
|
|
VariantInit(&varVersionWildcard);
|
|
varVersionWildcard.vt = VT_BSTR;
|
|
V_BSTR(&varVersionWildcard) = CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::VersionWildcard].bstr;
|
|
IF_FAILED_EXIT(pIDOMElement->setAttribute(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Version].bstr, varVersionWildcard));
|
|
|
|
SAFERELEASE(pIDOMElement);
|
|
|
|
IF_FAILED_EXIT(pDependentAssemblyNode->appendChild(pIDOMNodeClone, NULL));
|
|
|
|
IF_FAILED_EXIT(_pXMLDoc->createElement(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Install].bstr, &pIDOMElement));
|
|
|
|
bstrManifestUrl = ::SysAllocString(pwzManifestUrl);
|
|
IF_ALLOC_FAILED_EXIT(bstrManifestUrl);
|
|
|
|
// bstrManifestUrl to be freed by VariantClear()
|
|
varCodebase.vt = VT_BSTR;
|
|
V_BSTR(&varCodebase) = bstrManifestUrl;
|
|
IF_FAILED_EXIT(pIDOMElement->setAttribute(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::Codebase].bstr, varCodebase));
|
|
|
|
IF_FAILED_EXIT(pDependentAssemblyNode->appendChild(pIDOMElement, NULL));
|
|
|
|
exit:
|
|
VariantClear(&varCodebase);
|
|
|
|
SAFERELEASE(pDependentAssemblyNode);
|
|
SAFERELEASE(pIDOMElement);
|
|
SAFERELEASE(pIDOMNode);
|
|
SAFERELEASE(pIDOMNodeClone);
|
|
|
|
if (FAILED(_hr))
|
|
SAFERELEASE(_pDependencyNode);
|
|
|
|
return _hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ImportAssemblyNode
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::ImportAssemblyNode(LPASSEMBLY_MANIFEST_IMPORT pManImport)
|
|
{
|
|
VARIANT varNameSpaces;
|
|
VARIANT varXPath;
|
|
|
|
IXMLDOMDocument2 *pXMLDocSrc = NULL;
|
|
IXMLDOMNode *pIDOMNode = NULL;
|
|
IXMLDOMNode *pIDOMNodeClone = NULL;
|
|
|
|
// note: _pXMLDoc, _pAssemblyNode must be NULL
|
|
// and this must be called _only and exactly once_
|
|
|
|
|
|
// Create the DOM Doc interface
|
|
IF_FAILED_EXIT(CoCreateInstance(__uuidof(private_MSXML_DOMDocument30),
|
|
NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument2, (void**)&_pXMLDoc));
|
|
|
|
// Load synchronously
|
|
IF_FAILED_EXIT(_pXMLDoc->put_async(VARIANT_FALSE));
|
|
|
|
// Setup namespace filter
|
|
VariantInit(&varNameSpaces);
|
|
varNameSpaces.vt = VT_BSTR;
|
|
V_BSTR(&varNameSpaces) = CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::NameSpace].bstr;
|
|
IF_FAILED_EXIT(_pXMLDoc->setProperty(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::SelNameSpaces].bstr, varNameSpaces));
|
|
|
|
// Setup query type
|
|
VariantInit(&varXPath);
|
|
varXPath.vt = VT_BSTR;
|
|
V_BSTR(&varXPath) = CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::XPath].bstr;
|
|
IF_FAILED_EXIT(_pXMLDoc->setProperty(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::SelLanguage].bstr, varXPath));
|
|
|
|
// initialize manifest file: clone and insert 'assembly' node
|
|
// by doing this, manifestVersion and other attributes are maintained
|
|
|
|
pXMLDocSrc = ((CAssemblyManifestImport*)pManImport)->_pXMLDoc;
|
|
|
|
// BUGBUG: this only pick the 1st instance of 'assembly'
|
|
IF_FAILED_EXIT(pXMLDocSrc->selectSingleNode(CAssemblyManifestImport::g_StringTable[CAssemblyManifestImport::AssemblyNode].bstr, &pIDOMNode));
|
|
IF_FALSE_EXIT(_hr == S_OK, E_FAIL);
|
|
|
|
// clone no child
|
|
IF_FAILED_EXIT(pIDOMNode->cloneNode(VARIANT_FALSE, &pIDOMNodeClone));
|
|
|
|
_hr = _pXMLDoc->appendChild(pIDOMNodeClone, &_pAssemblyNode);
|
|
|
|
exit:
|
|
if (FAILED(_hr))
|
|
SAFERELEASE(_pXMLDoc);
|
|
|
|
SAFERELEASE(pIDOMNodeClone);
|
|
SAFERELEASE(pIDOMNode);
|
|
|
|
return _hr;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Commit
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT CAssemblyManifestEmit::Commit()
|
|
{
|
|
// considered safe to be called multiple times
|
|
VARIANT varFileName;
|
|
|
|
if (_pXMLDoc)
|
|
{
|
|
// ignore any error occured before, do save anyway
|
|
// it's caller's responsibility to track a incomplete xml manifest file/XMLDoc state
|
|
VariantInit(&varFileName);
|
|
varFileName.vt = VT_BSTR;
|
|
V_BSTR(&varFileName) = _bstrManifestFilePath;
|
|
|
|
_hr = _pXMLDoc->save(varFileName);
|
|
}
|
|
else
|
|
{
|
|
// not initialized
|
|
_hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
|
|
}
|
|
|
|
return _hr;
|
|
}
|
|
|