/// <summary> /// Invokes a managed custom action from native code by /// extracting the package to a temporary working directory /// then hosting the CLR and locating and calling the entrypoint. /// </summary> /// <param name="hSession">Handle to the installation session. /// Passed to custom action entrypoints by the installer engine.</param> /// <param name="szWorkingDir">Directory containing the CA binaries /// and the CustomAction.config file defining the entrypoints. /// This may be NULL, in which case the current module must have /// a concatenated cabinet containing those files, which will be /// extracted to a temporary directory.</param> /// <param name="szEntryPoint">Name of the CA entrypoint to be invoked. /// This must be either an explicit "AssemblyName!Namespace.Class.Method" /// string, or a simple name that maps to a full entrypoint definition /// in CustomAction.config.</param> /// <returns>The value returned by the managed custom action method, /// or ERROR_INSTALL_FAILURE if the CA could not be invoked.</returns> int InvokeCustomAction(MSIHANDLE hSession, const wchar_t* szWorkingDir, const wchar_t* szEntryPoint) { #ifdef MANAGED_CAs_OUT_OF_PROC if (!g_fRunningOutOfProc && szWorkingDir == NULL) { return InvokeOutOfProcManagedCustomAction(hSession, szEntryPoint); } #endif wchar_t szTempDir[MAX_PATH]; bool fDeleteTemp = false; if (szWorkingDir == NULL) { if (!ExtractToTempDirectory(hSession, g_hModule, szTempDir, MAX_PATH)) { return ERROR_INSTALL_FAILURE; } szWorkingDir = szTempDir; fDeleteTemp = true; } wchar_t szConfigFilePath[MAX_PATH + 20]; StringCchCopy(szConfigFilePath, MAX_PATH + 20, szWorkingDir); StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\CustomAction.config"); const wchar_t* szConfigFile = szConfigFilePath; if (!::PathFileExists(szConfigFilePath)) { szConfigFile = NULL; } wchar_t szWIAssembly[MAX_PATH + 50]; StringCchCopy(szWIAssembly, MAX_PATH + 50, szWorkingDir); StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\Microsoft.Deployment.WindowsInstaller.dll"); int iResult = ERROR_INSTALL_FAILURE; ICorRuntimeHost* pHost; if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &pHost)) { _AppDomain* pAppDomain; if (CreateAppDomain(hSession, pHost, L"CustomAction", szWorkingDir, szConfigFile, &pAppDomain)) { if (!InvokeManagedCustomAction(hSession, pAppDomain, szEntryPoint, &iResult)) { iResult = ERROR_INSTALL_FAILURE; } HRESULT hr = pHost->UnloadDomain(pAppDomain); if (FAILED(hr)) { Log(hSession, L"Failed to unload app domain. Error code 0x%X", hr); } pAppDomain->Release(); } pHost->Stop(); pHost->Release(); } if (fDeleteTemp) { DeleteDirectory(szTempDir); } return iResult; }
unsigned __stdcall ThreadProc(void *pArg) { RuntimeAndApp *pContext = (RuntimeAndApp *)pArg; HRESULT hr; #define IfFailGoMsg(MSG) \ if (FAILED(hr)) \ { \ printf("Runtime %d (%S): " MSG " (%x)\n", \ pContext->m_dwRuntimeNo, \ pContext->m_pwzRuntimeVer, \ hr); \ goto lExit; \ } ICLRRuntimeHost *pHost_V2 = NULL; ICorRuntimeHost *pHost = NULL; IUnknown *pUnk = NULL; mscorlib::_AppDomain *pAD = NULL; // Get ICorRuntimeHost to execute the app. hr = pContext->m_pRuntime->GetInterface( CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (LPVOID *)&pHost); IfFailGoMsg("Failed to load the runtime"); // Also try to get ICLRRuntimeHost which has a useful Stop method. (ICorRuntimeHost::Stop is empty.) if (SUCCEEDED(pContext->m_pRuntime->GetInterface( CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&pHost_V2))) { hr = pHost_V2->Start(); IfFailGoMsg("Failed to start ICLRRuntimeHost"); } ReportSyncPoint(SyncPointRuntimeStart, pContext->m_dwRuntimeNo); hr = pHost->Start(); IfFailGoMsg("Failed to start ICorRuntimeHost"); if(g_options.m_bUseDefaultAppDomain) { hr = pHost->GetDefaultDomain(&pUnk); IfFailGoMsg("Failed to get default domain"); } else { hr = pHost->CreateDomain(L"AD2",NULL,&pUnk); IfFailGoMsg("Failed to create AppDomain"); } hr = pUnk->QueryInterface(__uuidof(mscorlib::_AppDomain), (LPVOID *)&pAD); IfFailGoMsg("Failed to QI for _AppDomain"); BSTR bstrPath = SysAllocString(pContext->m_pwzAppPath); SAFEARRAY *rgArgs = CreateArgList(pContext->m_pwzAppArgs); ReportSyncPoint(SyncPointApplicationStart, pContext->m_dwRuntimeNo); if (g_options.m_bUseLoadByName) { hr = ExecuteAssemblyByName(pAD, bstrPath, rgArgs, &pContext->m_dwRetCode); } else { hr = pAD->ExecuteAssembly_3(bstrPath, NULL, rgArgs, &pContext->m_dwRetCode); } if (hr == COR_E_NEWER_RUNTIME) { IfFailGoMsg("Failed to execute assembly\nWas it built by a compiler that is newer than this runtime?"); } else { // we don't know whether the error comes from the runtime (failed to execute assembly) or // the assembly actually ran and threw an unhandled exception that was converted to the HR IfFailGoMsg("ExecuteAssembly returned an error code"); } SysFreeString(bstrPath); SafeArrayDestroy(rgArgs); if(!g_options.m_bUseDefaultAppDomain) { ReportSyncPoint(SyncPointAppDomainUnload, pContext->m_dwRuntimeNo); hr = pHost->UnloadDomain(pAD); IfFailGoMsg("Failed to unload AppDomain"); } pAD->Release(); pAD = NULL; pUnk->Release(); pUnk = NULL; hr = pHost->Stop(); IfFailGoMsg("Failed to stop ICorRuntimeHost"); if (pHost_V2 != NULL) { hr = pHost_V2->Stop(); IfFailGoMsg("Failed to stop ICLRRuntimeHost"); pHost_V2 = NULL; } lExit: if (pHost_V2 != NULL) pHost_V2->Release(); if (pAD != NULL) pAD->Release(); if (pUnk != NULL) pUnk->Release(); if (pHost != NULL) pHost->Release(); if (g_options.m_bRunMultithreaded) { // make sure we don't deadlock the other thread // this is also needed if the value of g_options.m_dwADUnloadOrder is SameOrder or ReverseOrder // since one of the threads will be waiting for the other one to Unload the AppDomain. SetEvent(g_rhndEvents[pContext->m_dwRuntimeNo - 1]); _endthreadex(0); } return 0; #undef IfFailGoMsg }