3

I have an application hosting the .net clr with a custom AppDomain Manager and an AssemblyManager with a store.

This all works fine when the Assembly with the AppDomainManager in is a dll in the same directory as the executable.

What I want to do is embed the Managers assembly inside the executable. When I do this ProvideAssembly is called with the correct strong name, I return a stream with the assembly bytes, but ICLRRuntimeHost->Start() returns an error indicating that a type cannot be loaded.

All the assembly binding details match etc.

My questions is, does anyone know if this configuration is supported? Can the AppDomainManagers assembly be loaded in this way rather than from a file?


Currently only provide an IHostAssemblyManager to the CLR. And Call:
#define ASSEMBLY L"MscoreeIntegration, Version=1.0.0.0, PublicKeyToken=a0c02a181a22f567, Culture=neutral"
#define MANAGER L"MscoreeIntegration.Manager"

m_clrcontrol->SetAppDomainManagerType(ASSEMBLY, MANAGER);

Lookup Binding identity from map, return an IStream of the stored data (Have stepped through with a debugger and nothing fails).

HRESULT STDMETHODCALLTYPE AssemblyManager::GetNonHostStoreAssemblies(ICLRAssemblyReferenceList **ppReferenceList){
    *ppReferenceList = NULL;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE AssemblyManager::GetAssemblyStore(IHostAssemblyStore **ppAssemblyStore){
    *ppAssemblyStore = m_impl->m_store;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE AssemblyStore::ProvideAssembly(AssemblyBindInfo *pBindInfo, UINT64 *pAssemblyId, UINT64 *pContext, IStream **ppStmAssemblyImage, IStream **ppStmPDB){
    map<wstring,Data*>::iterator find = m_impl->m_assemblies.find(pBindInfo->lpPostPolicyIdentity);
    if(find!=m_impl->m_assemblies.end()){
        *pAssemblyId = find->second->m_id;

        HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, find->second->m_cbLength);
        LPVOID pData = ::GlobalLock(hMem);
        memcpy(pData, find->second->m_pData, find->second->m_cbLength);
        ::GlobalUnlock(hMem);

        HRESULT hr = ::CreateStreamOnHGlobal(hMem, FALSE, ppStmAssemblyImage);

        *pContext = 0;
        *ppStmPDB = NULL;
        return S_OK;
    }

    return 0x80070002; //COR_E_FILENOTFOUND;
}

I get the binding identities like so:

void AddAssembly(AssemblyStore *store, ICLRAssemblyIdentityManager *ident, const char* filename){
    int length = 0;
    const char *buffer = LoadData(filename, length);
    IStream *stream = GetStream(buffer, length);
    if(!stream){ return; }

    DWORD cbBuffer = 0;
    HRESULT hr = ident->GetBindingIdentityFromStream(stream, 0, NULL, &cbBuffer);

    wchar_t *bind = (wchar_t*)malloc(cbBuffer*sizeof(wchar_t));
    stream = GetStream(buffer, length);
    hr = ident->GetBindingIdentityFromStream(stream, 0, bind, &cbBuffer);

    BOOL strong;
    hr = ident->IsStronglyNamed(bind, &strong);
    if(!strong){
        printf("NOT STRONG: %S\n", bind);
    }

    store->AddAssembly(bind, (BYTE*)buffer, length);
}
  • Hi, Thanks for the comment, do you have any docs/pages you could point me towards so I can work out why my host fails? Im refering to the MS CLR Hosting online docs and the MS Book "Customising the CLR" I must be doing something stupid! – BafflingTangent Feb 06 '12 at 21:47
  • Nothing you don't already have. Nobody can help you until you do a better job describing your problem. A code snippet is a minimum requirement. – Hans Passant Feb 06 '12 at 21:52
  • Hi, have added snippets, which other areas would you like to see? Thanks. – BafflingTangent Feb 06 '12 at 22:07

2 Answers2

1

Like Hans said, you already have everything you need. The book you mentioned has a ready-made example in which the assembly that contains the AppDomainManager class is pulled out of a OLE compound file by the host.

I am doing something similar, so I can confirm it works. You have to be careful on three points:

  • when you generate the list of non-host assemblies. If you do not know how to build it properly, it's way better to let the CLR handle it (passing back NULL) In this way the resolution becomes GAC -> Host -> other Fusion search paths
  • when you return pAssemblyId, never, ever pass 0. The docs don't tell it, but it results in a very .. peculiar behaviour.
  • reading the file into a IStream. Personally, I wrote my FileStream unamanged class that implements IStream by using the Win32 API. Way better than relying on code that was not written for this purpose (or linking to something "strange", like the shell API)
Lorenzo Dematté
  • 7,638
  • 3
  • 37
  • 77
0

Be aware that in the "pBindInfo" the identity of the assembly you pass matches the requested "processorarchitecture" in particular check the "lpPostPolicyIdentity" for the requested "processorarchitecture" and fail the request if your assembly doesn't match. The clr will try to load first the x64 or the x86 and then the msil (anycpu) so wait untill you get the correct request