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?
#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);
}
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:
GAC -> Host -> other Fusion search paths
pAssemblyId
, never, ever pass 0. The docs don't tell it, but it results in a very .. peculiar behaviour.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)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
User contributions licensed under CC BY-SA 3.0