Esempio n. 1
0
HRESULT AssemblySpec::GetAssemblyFromFusion(AppDomain* pAppDomain,
                                            IAssemblyName* pFusionAssemblyName,
                                            CodeBaseInfo* pCodeBase,
                                            IAssembly** ppFusionAssembly,
                                            PEFile** ppFile,
                                            CQuickWSTR* pFusionLog,
                                            OBJECTREF* pExtraEvidence,
                                            OBJECTREF* pThrowable)
{
    _ASSERTE(ppFile);
    HRESULT hr = S_OK;
    IAssembly *pFusionAssembly = NULL;

    COMPLUS_TRY {
        DWORD dwSize = MAX_PATH;
        CQuickWSTR bufferPath;
        WCHAR *pPath = NULL;
        AssemblySink* pSink;
        DWORD eLocation;

        CQuickWSTR bufferCodebase;
        LPWSTR pwsCodeBase = NULL;
        DWORD  dwCodeBase = 0;
        IAssemblyName *pNameDef = NULL;
        
        IApplicationContext *pFusionContext = pAppDomain->GetFusionContext();
        pSink = pAppDomain->GetAssemblySink();
        if(!pSink) {
            hr = E_OUTOFMEMORY;
            COMPLUS_LEAVE;
        }
        
        pSink->pFusionLog=pFusionLog;
        hr = FusionBind::GetAssemblyFromFusion(pFusionContext,
                                               pSink,
                                               pFusionAssemblyName,
                                               pCodeBase,
                                               &pFusionAssembly);
        pSink->pFusionLog=NULL;
        if(SUCCEEDED(hr)) {
            _ASSERTE(pFusionAssembly);

            // Get the path to the module containing the manifest
            dwSize = bufferPath.MaxSize();
            pPath = bufferPath.Ptr();
            hr = pFusionAssembly->GetManifestModulePath(pPath,
                                                        &dwSize);
            if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
                pPath = bufferPath.Alloc(dwSize);
                if (pPath == NULL) {
                    hr = E_OUTOFMEMORY;
                    COMPLUS_LEAVE;
                }
                hr = pFusionAssembly->GetManifestModulePath(pPath,
                                                        &dwSize);
            }

            if(SUCCEEDED(hr)) {
                hr = pFusionAssembly->GetAssemblyNameDef(&pNameDef);
                if (SUCCEEDED(hr)) {
                    dwCodeBase = bufferCodebase.MaxSize();
                    pwsCodeBase = bufferCodebase.Ptr();
                    hr = pNameDef->GetProperty(ASM_NAME_CODEBASE_URL, pwsCodeBase, &dwCodeBase);
                    if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
                        pwsCodeBase = bufferCodebase.Alloc(dwCodeBase);
                        if (pwsCodeBase == NULL) {
                            hr = E_OUTOFMEMORY;
                            COMPLUS_LEAVE;
                        }
                        hr = pNameDef->GetProperty(ASM_NAME_CODEBASE_URL, pwsCodeBase, &dwCodeBase);
                    }
                    pNameDef->Release();
                }
            }
        }
        pSink->Release();

        if(hr == S_OK && dwSize) {
            // See if we need to perform a strong name verification.
            hr = pFusionAssembly->GetAssemblyLocation(&eLocation);


            if (SUCCEEDED(hr)) {
                switch ((eLocation & ASMLOC_LOCATION_MASK)) {
                case ASMLOC_GAC:
                case ASMLOC_DOWNLOAD_CACHE:
                case ASMLOC_UNKNOWN:
                    // Assemblies from the GAC or download cache have
                    // already been verified by Fusion. Location Unknown
                    // indicates a load from the dev path, which we'll
                    // assume isn't a interesting case for verification.
                    hr = S_OK;
                    break;
                case ASMLOC_RUN_FROM_SOURCE:
                    // For now, just verify these every time, we need to
                    // cache the fact that at least one verification has
                    // been performed (if strong name policy permits
                    if (SUCCEEDED(hr)&&(eLocation&ASMLOC_CODEBASE_HINT))
                    {
                        hr=CheckFileAccess(pPath,FILE_READ_DATA);
                        if (FAILED(hr))
                            break;
                    }
                    // caching of verification results).
                    if (StrongNameSignatureVerification(pPath,
                                                        SN_INFLAG_INSTALL|SN_INFLAG_ALL_ACCESS|SN_INFLAG_RUNTIME,
                                                        NULL))
                        hr = S_OK;
                    else {
                        hr = StrongNameErrorInfo();
                        if (hr == CORSEC_E_MISSING_STRONGNAME)
                            hr = S_OK;
                        else
                            hr = CORSEC_E_INVALID_STRONGNAME;
                    }
                    break;
                default:
                    _ASSERTE(FALSE);
                }
                if (SUCCEEDED(hr)) {
                    hr = SystemDomain::LoadFile(pPath, 
                                                NULL, 
                                                mdFileNil, 
                                                FALSE, 
                                                pFusionAssembly, 
                                                pwsCodeBase,
                                                pExtraEvidence,
                                                ppFile,
                                                FALSE);
                    if (SUCCEEDED(hr)) {
                        if(ppFusionAssembly) {
                            pFusionAssembly->AddRef();
                            *ppFusionAssembly = pFusionAssembly;
                        }
                        if((eLocation & ASMLOC_LOCATION_MASK) == ASMLOC_GAC)
                            // Assemblies in the GAC have also had any internal module
                            // hashes verified at install time.
                            (*ppFile)->SetHashesVerified();
                    }
                }
            }
            else if (hr == E_NOTIMPL) {
                // process exe
                _ASSERTE(pAppDomain == SystemDomain::System()->DefaultDomain());
                hr = PEFile::Clone(SystemDomain::System()->DefaultDomain()->m_pRootFile, ppFile);
                if(SUCCEEDED(hr) && ppFusionAssembly) {
                    pFusionAssembly->AddRef();
                    *ppFusionAssembly = pFusionAssembly;
                }
            }
        }
    }
    COMPLUS_CATCH {
        BEGIN_ENSURE_COOPERATIVE_GC();
        if (pThrowable) 
        {
            *pThrowable = GETTHROWABLE();
            hr = SecurityHelper::MapToHR(*pThrowable);
        }
        else
            hr = SecurityHelper::MapToHR(GETTHROWABLE());
        END_ENSURE_COOPERATIVE_GC();
    } COMPLUS_END_CATCH
 
    if (pFusionAssembly)
        pFusionAssembly->Release();

    return hr;
}
Esempio n. 2
0
STDAPI CopyPDBs(IAssembly *pAsm)
{
    HRESULT                                       hr = S_OK;
    IAssemblyName                                *pName = NULL;
    IAssemblyModuleImport                        *pModImport = NULL;
    DWORD                                         dwSize;
    WCHAR                                         wzAsmCachePath[MAX_PATH];
    WCHAR                                         wzFileName[MAX_PATH];
    WCHAR                                         wzSourcePath[MAX_PATH];
    WCHAR                                         wzPDBName[MAX_PATH];
    WCHAR                                         wzPDBSourcePath[MAX_PATH];
    WCHAR                                         wzPDBTargetPath[MAX_PATH];
    WCHAR                                         wzModPath[MAX_PATH];
    LPWSTR                                        wzCodebase=NULL;
    LPWSTR                                        wzModName = NULL;
    DWORD                                         dwIdx = 0;
    LPWSTR                                        wzTmp = NULL;

    if (!pAsm) {
        hr = E_INVALIDARG;
        goto Exit;
    }

    if (pAsm->GetAssemblyLocation(NULL) == E_NOTIMPL) {
        // This is a registered "known assembly" (ie. the process EXE).
        // We don't copy PDBs for the process EXE because it's never
        // shadow copied.

        hr = S_FALSE;
        goto Exit;
    }

    // Find the source location. Make sure this is a file:// URL (ie. we
    // don't support retrieving the PDB over http://).

    hr = pAsm->GetAssemblyNameDef(&pName);
    if (FAILED(hr)) {
        goto Exit;
    }

    wzCodebase = NEW(WCHAR[MAX_URL_LENGTH+1]);
    if (!wzCodebase)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    dwSize = MAX_URL_LENGTH * sizeof(WCHAR);
    hr = pName->GetProperty(ASM_NAME_CODEBASE_URL, (void *)wzCodebase, &dwSize);
    if (FAILED(hr)) {
        goto Exit;
    }

    if (!UrlIsW(wzCodebase, URLIS_FILEURL)) {
        hr = E_INVALIDARG;
        goto Exit;
    }

    dwSize = MAX_PATH;
    hr = PathCreateFromUrlWrap(wzCodebase, wzSourcePath, &dwSize, 0);
    if (FAILED(hr)) {
        goto Exit;
    }

    wzTmp = PathFindFileName(wzSourcePath);
    ASSERT(wzTmp > (LPWSTR)wzSourcePath);
    *wzTmp = L'\0';
        
   // Find the target location in the cache.
   
    dwSize = MAX_PATH;
    hr = pAsm->GetManifestModulePath(wzAsmCachePath, &dwSize);
    if (FAILED(hr)) {
        goto Exit;
    }

    wzTmp = PathFindFileName(wzAsmCachePath);
    ASSERT(wzTmp > (LPWSTR)wzAsmCachePath);

    StrCpy(wzFileName, wzTmp);
    *wzTmp = L'\0';


    // Copy the manifest PDB.

    // Hack for now
    dwSize = MAX_PATH;
    hr = GetPDBName(wzFileName, wzPDBName, &dwSize);
    if (FAILED(hr)) {
        goto Exit;
    }

    wnsprintfW(wzPDBSourcePath, MAX_PATH, L"%ws%ws", wzSourcePath, wzPDBName);
    wnsprintf(wzPDBTargetPath, MAX_PATH, L"%ws%ws", wzAsmCachePath, wzPDBName);

    if (GetFileAttributes(wzPDBTargetPath) == (DWORD) -1 && lstrcmpiW(wzPDBSourcePath, wzPDBTargetPath)) {
        CopyFile(wzPDBSourcePath, wzPDBTargetPath, TRUE);
    }

    // Copy the module PDBs.

    dwIdx = 0;
    while (SUCCEEDED(hr)) {
        hr = pAsm->GetNextAssemblyModule(dwIdx++, &pModImport);

        if (SUCCEEDED(hr)) {
            if (pModImport->IsAvailable()) {
                dwSize = MAX_PATH;
                hr = pModImport->GetModulePath(wzModPath, &dwSize);
                if (FAILED(hr)) {
                    SAFERELEASE(pModImport);
                    goto Exit;
                }

                wzModName = PathFindFileName(wzModPath);
                ASSERT(wzModName);

                dwSize = MAX_PATH;
                hr = GetPDBName(wzModName, wzPDBName, &dwSize);
                if (FAILED(hr)) {
                    SAFERELEASE(pModImport);
                    goto Exit;
                }

                wnsprintfW(wzPDBSourcePath, MAX_PATH, L"%ws%ws", wzSourcePath,
                           wzPDBName);
                wnsprintfW(wzPDBTargetPath, MAX_PATH, L"%ws%ws", wzAsmCachePath,
                           wzPDBName);

                if (GetFileAttributes(wzPDBTargetPath) == (DWORD) -1 && lstrcmpiW(wzPDBSourcePath, wzPDBTargetPath)) {
                    CopyFile(wzPDBSourcePath, wzPDBTargetPath, TRUE);
                }
            }

            SAFERELEASE(pModImport);
        }
    }

    // Copy complete. Return success.

    if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
        hr = S_OK;
    }

Exit:
    SAFERELEASE(pName);
    SAFEDELETEARRAY(wzCodebase);
    return hr;
}
Esempio n. 3
0
void    AsmMan::EndAssembly()
{
    if(m_pCurAsmRef)
    {
        if(m_pCurAsmRef->isRef)
        { // list the assembly ref
            if(GetAsmRefByName(m_pCurAsmRef->szAlias))
            {
                //report->warn("Multiple declarations of Assembly Ref '%s', ignored except the 1st one\n",m_pCurAsmRef->szName);
                delete m_pCurAsmRef;
                m_pCurAsmRef = NULL;
                return;
            }
            if(m_pCurAsmRef->isAutodetect)
            {
                IAssemblyName* pIAsmName;
                HRESULT hr;
                // Convert name to Unicode
                WszMultiByteToWideChar(g_uCodePage,0,m_pCurAsmRef->szName,-1,wzUniBuf,dwUniBuf);
                hr = CreateAssemblyNameObject(&pIAsmName,wzUniBuf,CANOF_PARSE_DISPLAY_NAME,NULL);
                if(SUCCEEDED(hr))
                {
                    // set enumeration criteria: what is known about AsmRef (besides name)
                    if(m_pCurAsmRef->usVerMajor != (USHORT)0xFFFF)
                        pIAsmName->SetProperty(ASM_NAME_MAJOR_VERSION,&(m_pCurAsmRef->usVerMajor),2);
                    if(m_pCurAsmRef->usVerMinor != (USHORT)0xFFFF)
                        pIAsmName->SetProperty(ASM_NAME_MINOR_VERSION,&(m_pCurAsmRef->usVerMinor),2);
                    if(m_pCurAsmRef->usBuild != (USHORT)0xFFFF)
                        pIAsmName->SetProperty(ASM_NAME_BUILD_NUMBER,&(m_pCurAsmRef->usBuild),2);
                    if(m_pCurAsmRef->usRevision != (USHORT)0xFFFF)
                        pIAsmName->SetProperty(ASM_NAME_REVISION_NUMBER,&(m_pCurAsmRef->usRevision),2);
                    if(m_pCurAsmRef->pPublicKeyToken)
                        pIAsmName->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN,
                                               m_pCurAsmRef->pPublicKeyToken->ptr(),
                                               m_pCurAsmRef->pPublicKeyToken->length());
                    if(m_pCurAsmRef->pLocale)
                        pIAsmName->SetProperty(ASM_NAME_CULTURE,
                                               m_pCurAsmRef->pLocale->ptr(),
                                               m_pCurAsmRef->pLocale->length());

                    // enumerate assemblies
                    IAssemblyEnum* pIAsmEnum = NULL;
                    hr = CreateAssemblyEnum(&pIAsmEnum, NULL, pIAsmName, ASM_CACHE_GAC, NULL);
                    if(SUCCEEDED(hr))
                    {
                        IAssemblyName* pIAsmNameFound;
                        IAssemblyName* pIAsmNameLatestVer = NULL;
                        ULONGLONG   ullVer=0, ullVerLatest=0;
                        DWORD  dwVerHi, dwVerLo;
    
                        // find the latest and greatest, if any
                        for(;;)
                        {
                            pIAsmNameFound = NULL;
                            hr = pIAsmEnum->GetNextAssembly(NULL,&pIAsmNameFound,0); 
                            if(SUCCEEDED(hr) && pIAsmNameFound)
                            {
    
                                pIAsmNameFound->GetVersion(&dwVerHi,&dwVerLo);
                                ullVer = (ULONGLONG)dwVerHi;
                                ullVer <<= sizeof(DWORD);
                                ullVer |= dwVerLo;
                                if(ullVer > ullVerLatest)
                                {
                                    if(pIAsmNameLatestVer)
                                       pIAsmNameLatestVer->Release(); 
                                    ullVerLatest = ullVer;
                                    pIAsmNameLatestVer = pIAsmNameFound;
                                }
                                else
                                    pIAsmNameFound->Release();
                            }
                            else break;
                        }
                        // if found, fill the gaps
                        if(pIAsmNameLatestVer)
                        {
                            DWORD cbSize=0;
                            USHORT usDummy=0;

                            if(m_pCurAsmRef->pPublicKeyToken == NULL)
                            {
                                cbSize = 1024;
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_PUBLIC_KEY_TOKEN,
                                                wzUniBuf, &cbSize);
                                if(cbSize)
                                {
                                    if((m_pCurAsmRef->pPublicKeyToken = new BinStr()))
                                        memcpy(m_pCurAsmRef->pPublicKeyToken->getBuff(cbSize),
                                            wzUniBuf, cbSize);
                                }
                            }
    
                            if(m_pCurAsmRef->usVerMajor == (USHORT)0xFFFF)
                            {
                                cbSize = (DWORD)sizeof(WORD);
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_MAJOR_VERSION,
                                                &usDummy, &cbSize);
                                m_pCurAsmRef->usVerMajor = usDummy;
                            }
                            if(m_pCurAsmRef->usVerMinor == (USHORT)0xFFFF)
                            {
                                cbSize = (DWORD)sizeof(WORD);
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_MINOR_VERSION,
                                                &usDummy, &cbSize);
                                m_pCurAsmRef->usVerMinor = usDummy;
                            }
                            if(m_pCurAsmRef->usBuild == (USHORT)0xFFFF)
                            {
                                cbSize = (DWORD)sizeof(WORD);
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_BUILD_NUMBER,
                                                &usDummy, &cbSize);
                                m_pCurAsmRef->usBuild = usDummy;
                            }
                            if(m_pCurAsmRef->usRevision == (USHORT)0xFFFF)
                            {
                                cbSize = (DWORD)sizeof(WORD);
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_REVISION_NUMBER,
                                                &usDummy, &cbSize);
                                m_pCurAsmRef->usRevision = usDummy;
                            }
                            
                            if(m_pCurAsmRef->pLocale == NULL)
                            {
                                cbSize = 1024;
                                pIAsmNameLatestVer->GetProperty(ASM_NAME_CULTURE,
                                                wzUniBuf, &cbSize);

                                if(cbSize > (DWORD)sizeof(WCHAR))
                                {
                                    if((m_pCurAsmRef->pLocale = new BinStr()))
                                        memcpy(m_pCurAsmRef->pLocale->getBuff(cbSize),
                                            wzUniBuf, cbSize);
                                }
                            }
                            pIAsmNameLatestVer->Release(); 
                        }
                        else
                            report->warn("Failed to autodetect assembly '%s'\n",m_pCurAsmRef->szName);
                        // if no assembly found, leave it as is, it might be not a GAC assembly
    
                        pIAsmEnum->Release();
                    }
                    else
                        report->error("Failed to enum assemblies %S, hr=0x%08X\n",wzUniBuf,hr);
                    pIAsmName->Release();
                }
                else
                    report->error("Failed to create assembly name object for %S, hr=0x%08X\n",wzUniBuf,hr);
            } // end if isAutodetect
            m_AsmRefLst.PUSH(m_pCurAsmRef);
            m_pCurAsmRef->tkTok = TokenFromRid(m_AsmRefLst.COUNT(),mdtAssemblyRef);
        }
        else
        {
            HRESULT                 hr = S_OK;
            m_pCurAsmRef->tkTok = TokenFromRid(1,mdtAssembly);
            // Determine the strong name public key. This may have been set
            // via a directive in the source or from the command line (which
            // overrides the directive). From the command line we may have
            // been provided with a file or the name of a CAPI key
            // container. Either may contain a public key or a full key
            // pair.
            if (((Assembler*)m_pAssembler)->m_wzKeySourceName)
            {
                {
                    // Read public key or key pair from file.
                    HANDLE hFile = WszCreateFile(((Assembler*)m_pAssembler)->m_wzKeySourceName,
                                                 GENERIC_READ,
                                                 FILE_SHARE_READ,
                                                 NULL,
                                                 OPEN_EXISTING,
                                                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
                                                 NULL);
                    if(hFile == INVALID_HANDLE_VALUE)
                    {
                        hr = GetLastError();
                        report->error("Failed to open key file '%S': 0x%08X\n",((Assembler*)m_pAssembler)->m_wzKeySourceName,hr);
                        m_pCurAsmRef = NULL;
                        return;
                    }

                    // Determine file size and allocate an appropriate buffer.
                    m_sStrongName.m_cbPublicKey = SafeGetFileSize(hFile, NULL);
                    if (m_sStrongName.m_cbPublicKey == 0xffffffff) {
                        report->error("File size too large\n");
                        m_pCurAsmRef = NULL;
                        CloseHandle(hFile);
                        return;
                    }

                    m_sStrongName.m_pbPublicKey = new BYTE[m_sStrongName.m_cbPublicKey];
                    if (m_sStrongName.m_pbPublicKey == NULL) {
                        report->error("Failed to allocate key buffer\n");
                        m_pCurAsmRef = NULL;
                        CloseHandle(hFile);
                        return;
                    }
                    m_sStrongName.m_dwPublicKeyAllocated = 2;

                    // Read the file into the buffer.
                    DWORD dwBytesRead;
                    if (!ReadFile(hFile, m_sStrongName.m_pbPublicKey, m_sStrongName.m_cbPublicKey, &dwBytesRead, NULL)) {
                        hr = GetLastError();
                        report->error("Failed to read key file '%S': 0x%08X\n",((Assembler*)m_pAssembler)->m_wzKeySourceName,hr);
                        m_pCurAsmRef = NULL;
                        CloseHandle(hFile);
                        return;
                    }

                    CloseHandle(hFile);

                    // Guess whether we're full or delay signing based on
                    // whether the blob passed to us looks like a public
                    // key. (I.e. we may just have copied a full key pair
                    // into the public key buffer).
                    if (m_sStrongName.m_cbPublicKey >= sizeof(PublicKeyBlob) &&
                        (offsetof(PublicKeyBlob, PublicKey) +
                         ((PublicKeyBlob*)m_sStrongName.m_pbPublicKey)->cbPublicKey) == m_sStrongName.m_cbPublicKey)
                        m_sStrongName.m_fFullSign = FALSE;
                    else
                        m_sStrongName.m_fFullSign = TRUE;

                    // If we really have a key pair, we'll move it into a
                    // key container so the signing code gets the key pair
                    // from a consistent place.
                    if (m_sStrongName.m_fFullSign)
                    {
                        m_sStrongName.m_pbPrivateKey = m_sStrongName.m_pbPublicKey;
                        m_sStrongName.m_cbPrivateKey = m_sStrongName.m_cbPublicKey;

                        m_sStrongName.m_pbPublicKey = NULL;
                        m_sStrongName.m_cbPublicKey = NULL;
                        m_sStrongName.m_dwPublicKeyAllocated = 0;

                        // Retrieve the public key portion as a byte blob.
                        if (!StrongNameGetPublicKey(NULL,
                                                    m_sStrongName.m_pbPrivateKey,
                                                    m_sStrongName.m_cbPrivateKey,
                                                    &m_sStrongName.m_pbPublicKey,
                                                    &m_sStrongName.m_cbPublicKey))
                        {
                            hr = StrongNameErrorInfo();
                            report->error("Failed to extract public key: 0x%08X\n",hr);
                            m_pCurAsmRef = NULL;
                            return;
                        }
    
                        m_sStrongName.m_dwPublicKeyAllocated = 2;
                    }
                }
            }
            else if (m_pAssembly->pPublicKey)
            {
                m_sStrongName.m_pbPublicKey = m_pAssembly->pPublicKey->ptr();
                m_sStrongName.m_cbPublicKey = m_pAssembly->pPublicKey->length();
                m_sStrongName.m_wzKeyContainer = NULL;
                m_sStrongName.m_fFullSign = FALSE;
                m_sStrongName.m_dwPublicKeyAllocated = 0;
            }
            else
            {
                m_sStrongName.m_pbPublicKey = NULL;
                m_sStrongName.m_cbPublicKey = 0;
                m_sStrongName.m_wzKeyContainer = NULL;
                m_sStrongName.m_fFullSign = FALSE;
                m_sStrongName.m_dwPublicKeyAllocated = 0;
            }

        }
        m_pCurAsmRef = NULL;
    }
    ((Assembler*)m_pAssembler)->m_pCustomDescrList = ((Assembler*)m_pAssembler)->m_CustomDescrListStack.POP();
}
Esempio n. 4
0
HRESULT CNodeFactory::ProcessQualifyAssemblyTag(XML_NODE_INFO **aNodeInfo, USHORT cNumRecs)
{
    HRESULT                                            hr = S_OK;
    USHORT                                             idx = 1;
    LPWSTR                                             pwzAttributeNS = NULL;
    LPWSTR                                             pwzPartialName = NULL;
    LPWSTR                                             pwzFullName = NULL;
    CQualifyAssembly                                  *pqa = NULL;
    IAssemblyName                                     *pNameFull = NULL;
    IAssemblyName                                     *pNamePartial = NULL;
    IAssemblyName                                     *pNameQualified = NULL;
    LPWSTR                              wzCanonicalDisplayName=NULL;

    ASSERT(aNodeInfo && cNumRecs);

    while (idx < cNumRecs) {
        if (aNodeInfo[idx]->dwType == XML_ATTRIBUTE) {
            // Found an attribute. Find out which one, and extract the data.
            // Node: ::ExtractXMLAttribute increments idx.

            hr = ApplyNamespace(aNodeInfo[idx], &pwzAttributeNS, 0);
            if (FAILED(hr)) {
                goto Exit;
            }

            if (!lstrcmpW(pwzAttributeNS, XML_ATTRIBUTE_PARTIALNAME)) {
                SAFEDELETEARRAY(pwzAttributeNS);

                if (pwzPartialName) {
                    // Ignore duplicate attribute
                    idx++;
                }
                else {
                    hr = ::ExtractXMLAttribute(&pwzPartialName, aNodeInfo, &idx, cNumRecs);
                    if (FAILED(hr)) {
                        goto Exit;
                    }
                }
            }
            else if (!lstrcmpW(pwzAttributeNS, XML_ATTRIBUTE_FULLNAME)) {
                SAFEDELETEARRAY(pwzAttributeNS);

                if (pwzFullName) {
                    // Ignore duplicate attribute
                    idx++;
                }
                else {
                    hr = ::ExtractXMLAttribute(&pwzFullName, aNodeInfo, &idx, cNumRecs);
                    if (FAILED(hr)) {
                        goto Exit;
                    }
                }

            }
            else {
                SAFEDELETEARRAY(pwzAttributeNS);
                idx++;
            }
        }
        else {
            idx++;
        }
    }

    if (pwzPartialName && pwzFullName) {
        DWORD                               dwSize;
        DWORD                               adwProperties[] = { ASM_NAME_NAME,
                                                                ASM_NAME_MAJOR_VERSION,
                                                                ASM_NAME_MINOR_VERSION,
                                                                ASM_NAME_BUILD_NUMBER,
                                                                ASM_NAME_REVISION_NUMBER,
                                                                ASM_NAME_CULTURE,
                                                                ASM_NAME_PUBLIC_KEY_TOKEN
                                                              };
        DWORD                               adwCmpFlags[] = { ASM_CMPF_NAME,
                                                              ASM_CMPF_MAJOR_VERSION,
                                                              ASM_CMPF_MINOR_VERSION,
                                                              ASM_CMPF_BUILD_NUMBER,
                                                              ASM_CMPF_REVISION_NUMBER,
                                                              ASM_CMPF_CULTURE,
                                                              ASM_CMPF_PUBLIC_KEY_TOKEN
                                                            };
        DWORD                               dwNumProps = sizeof(adwProperties) / sizeof(adwProperties[0]);

        if (FAILED(CreateAssemblyNameObject(&pNameFull, pwzFullName,
                                            CANOF_PARSE_DISPLAY_NAME, 0))) {
            goto Exit;
        }

        if (FAILED(CreateAssemblyNameObject(&pNamePartial, pwzPartialName,
                                            CANOF_PARSE_DISPLAY_NAME, 0))) {
            goto Exit;
        }

        // Check validity of qualification

        if (CAssemblyName::IsPartial(pNameFull) || !CAssemblyName::IsPartial(pNamePartial)) {
            goto Exit;
        }

        if (FAILED(pNamePartial->Clone(&pNameQualified))) {
            goto Exit;
        }

        for (DWORD i = 0; i < dwNumProps; i++) {
            dwSize = 0;
            if (pNamePartial->GetProperty(adwProperties[i], NULL, &dwSize) != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
                // Partial on this field. Set pNameQualified's corresponding
                // property to whatever is in pNameFull.

                dwSize = 0;
                pNameFull->GetProperty(adwProperties[i], NULL, &dwSize);
                if (!dwSize) {
                    goto Exit;
                }
                else {
                    BYTE                       *pBuf;

                    pBuf = NEW(BYTE[dwSize]);
                    if (!pBuf) {
                        hr = E_OUTOFMEMORY;
                        goto Exit;
                    }

                    if (FAILED(pNameFull->GetProperty(adwProperties[i], pBuf, &dwSize))) {
                        SAFEDELETEARRAY(pBuf);
                        goto Exit;
                    }

                    if (FAILED(pNameQualified->SetProperty(adwProperties[i], pBuf, dwSize))) {
                        SAFEDELETEARRAY(pBuf);
                        goto Exit;
                    }

                    SAFEDELETEARRAY(pBuf);
                }
            }
            else {
                // Full-specified on this field. Make sure it matches the full ref specified.

                if (pNamePartial->IsEqual(pNameFull, adwCmpFlags[i]) != S_OK) {
                    goto Exit;
                }
            }
        }

        if (CAssemblyName::IsPartial(pNameQualified)) {
            goto Exit;
        }

        // Get canonical display name format

        wzCanonicalDisplayName = NEW(WCHAR[MAX_URL_LENGTH+1]);
        if (!wzCanonicalDisplayName)
        {
            hr = E_OUTOFMEMORY;
            goto Exit;
        }

        dwSize = MAX_URL_LENGTH;
        if (FAILED(pNamePartial->GetDisplayName(wzCanonicalDisplayName, &dwSize, 0))) {
            goto Exit;
        }

        // Add qualified assembly entry to list

        pqa = new CQualifyAssembly;
        if (!pqa) {
            hr = E_OUTOFMEMORY;
            goto Exit;
        }

        pqa->_pwzPartialName = WSTRDupDynamic(wzCanonicalDisplayName);
        if (!pqa->_pwzPartialName) {
            hr = E_OUTOFMEMORY;
            goto Exit;
        }

        pqa->_pNameFull = pNameQualified;
        pNameQualified->AddRef();

        _listQualifyAssembly.AddTail(pqa);
    }

Exit:
    SAFEDELETEARRAY(pwzPartialName);
    SAFEDELETEARRAY(pwzFullName);

    SAFERELEASE(pNameFull);
    SAFERELEASE(pNamePartial);
    SAFERELEASE(pNameQualified);

    SAFEDELETEARRAY(wzCanonicalDisplayName);
    return hr;
}