#include #include #include #include #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 (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; }