// Try to get the right version of the CLR running. HRESULT LoadClr(std::wstring clrVersion, ICorRuntimeHost **ppHost) { // Check whether the .Net 4+ MetaHost interfaces are present. // The checks here are according to this blog post: // http://bradwilson.typepad.com/blog/2010/04/selecting-clr-version-from-unmanaged-host.html /* 1. LoadLibrary mscoree 2. GetProcAddress for CLRCreateInstance. If you get NULL, fall back to legacy path (CorBindToRuntimeEx) 3. Call CLRCreateInstance to get ICLRMetaHost. If you get E_NOTIMPL, fall back to legacy path (same as above) 4. Otherwise, party on the ICLRMetaHost you just got */ // If present, load the desired version using the new interfaces. // If not, check if we want .Net 4+, if so fail, else load old-style. HRESULT hr = E_FAIL; HMODULE hMscoree = NULL; ICLRMetaHostPtr pMetaHost; bool needNet40 = (CompareNoCase(clrVersion, L"v4.0") >= 0); bool needMetaHost = needNet40; hMscoree = LoadLibrary(L"mscoree.dll"); if (hMscoree == 0) { // No .Net installed // CONSIDER: Doing explicit checking according to http://support.microsoft.com/kb/318785 if (needNet40) { ShowMessage(IDS_MSG_HEADER_NEEDCLR40, IDS_MSG_BODY_LOADMSCOREE, IDS_MSG_FOOTER_ENSURECLR40 ); } else { ShowMessage(IDS_MSG_HEADER_NEEDCLR20, IDS_MSG_BODY_LOADMSCOREE, IDS_MSG_FOOTER_ENSURECLR20 ); } hr = E_FAIL; } else { pfnCLRCreateInstance CLRCreateInstance = (pfnCLRCreateInstance)GetProcAddress(hMscoree, "CLRCreateInstance"); if (CLRCreateInstance == 0) { // Certainly no .Net 4 installed if (needMetaHost) { // We need .Net 4.0 but it is not installed ShowMessage(IDS_MSG_HEADER_NEEDCLR40, IDS_MSG_BODY_NOCLRCREATEINSTANCE, IDS_MSG_FOOTER_ENSURECLR40 ); hr = E_FAIL; } else { // We need only .Net 2.0 runtime and cannot MetaHost. // Load .Net 2.0 with old code path hr = LoadClr20(ppHost); } } else { hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost); if (FAILED(hr)) { // MetaHost is not available, even though we have a new version of MSCorEE.dll // Certainly no .Net 4 installed if (needMetaHost) { // We need .Net 4.0 but it is not installed ShowMessage(IDS_MSG_HEADER_NEEDCLR40, IDS_MSG_BODY_CLRCREATEINSTANCEFAILED, IDS_MSG_FOOTER_ENSURECLR40, hr ); hr = E_FAIL; } else { // We need only .Net 2.0 runtime and cannot MetaHost. // Load .Net 2.0 with old code path hr = LoadClr20(ppHost); } } else { // Yay! We have a metahost hr = LoadClrMeta(clrVersion, pMetaHost, ppHost); } } FreeLibrary(hMscoree); } return hr; }
bool XlLibraryInitialize(XlAddInExportInfo* pExportInfo) { HRESULT hr; CComPtr<ICorRuntimeHost> pHost; hr = LoadClr20(&pHost); if (FAILED(hr) || pHost == NULL) { // LoadClr20 shows diagnostic MessageBoxes if needed. // Perhaps remember that we are not loaded? return 0; } // If all is fine now, also start the CLR (always safe to do again. hr = pHost->Start(); if (FAILED(hr)) { ShowMessage(IDS_MSG_HEADER_NEEDCLR20, IDS_MSG_BODY_HOSTSTART, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CString addInFullPath = AddInFullPath(); CPath xllDirectory(addInFullPath); xllDirectory.RemoveFileSpec(); CComPtr<IUnknown> pAppDomainSetupUnk; hr = pHost->CreateDomainSetup(&pAppDomainSetupUnk); if (FAILED(hr) || pAppDomainSetupUnk == NULL) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_APPDOMAINSETUP, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CComQIPtr<IAppDomainSetup> pAppDomainSetup = pAppDomainSetupUnk; hr = pAppDomainSetup->put_ApplicationBase(CComBSTR(xllDirectory)); if (FAILED(hr)) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_APPLICATIONBASE, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CComBSTR configFileName = addInFullPath; configFileName.Append(L".config"); pAppDomainSetup->put_ConfigurationFile(configFileName); CComBSTR appDomainName = L"ExcelDna: "; appDomainName.Append(addInFullPath); pAppDomainSetup->put_ApplicationName(appDomainName); IUnknown *pAppDomainUnk = NULL; hr = pHost->CreateDomainEx(appDomainName, pAppDomainSetupUnk, 0, &pAppDomainUnk); if (FAILED(hr) || pAppDomainUnk == NULL) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_APPDOMAIN, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CComQIPtr<_AppDomain> pAppDomain(pAppDomainUnk); // Load plan for ExcelDna.Loader: // Try AppDomain.Load with the name ExcelDna.Loader. // Then if it does not work, we will try to load from a known resource in the .xll. CComPtr<_Assembly> pExcelDnaLoaderAssembly; hr = pAppDomain->Load_2(CComBSTR(L"ExcelDna.Loader"), &pExcelDnaLoaderAssembly); if (FAILED(hr) || pExcelDnaLoaderAssembly == NULL) { HRSRC hResInfoLoader = FindResource(hModuleCurrent, L"EXCELDNA_LOADER", L"ASSEMBLY"); if (hResInfoLoader == NULL) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_MISSINGEXCELDNALOADER, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } HGLOBAL hLoader = LoadResource(hModuleCurrent, hResInfoLoader); void* pLoader = LockResource(hLoader); ULONG sizeLoader = (ULONG)SizeofResource(hModuleCurrent, hResInfoLoader); CComSafeArray<BYTE> bytesLoader; bytesLoader.Add(sizeLoader, (byte*)pLoader); hr = pAppDomain->Load_3(bytesLoader, &pExcelDnaLoaderAssembly); if (FAILED(hr)) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_EXCELDNALOADER, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CComBSTR pFullName; hr = pExcelDnaLoaderAssembly->get_FullName(&pFullName); if (FAILED(hr)) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_EXCELDNALOADERNAME, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } } CComPtr<_Type> pXlAddInType; hr = pExcelDnaLoaderAssembly->GetType_2(CComBSTR(L"ExcelDna.Loader.XlAddIn"), &pXlAddInType); if (FAILED(hr) || pXlAddInType == NULL) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_XLADDIN, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } CComSafeArray<VARIANT> initArgs; initArgs.Add(CComVariant((INT32)pExportInfo)); initArgs.Add(CComVariant((INT32)hModuleCurrent)); initArgs.Add(CComVariant(addInFullPath.AllocSysString())); CComVariant initRetVal; CComVariant target; hr = pXlAddInType->InvokeMember_3(CComBSTR("Initialize"), (BindingFlags)(BindingFlags_Static | BindingFlags_Public | BindingFlags_InvokeMethod), NULL, target, initArgs, &initRetVal); if (FAILED(hr)) { ShowMessage(IDS_MSG_HEADER_APPDOMAIN, IDS_MSG_BODY_XLADDININIT, IDS_MSG_FOOTER_UNEXPECTED, hr); return 0; } pHost_ForUnload = pHost.Detach(); pAppDomain_ForUnload = (IUnknown*)pAppDomain.Detach(); return initRetVal.boolVal == 0 ? false : true; }