// One large main method to keep this sample streamlined. // // This function demonstrates how to start the .NET Core runtime, // create an AppDomain, and execute managed code. // // It is meant as an educational sample, so not all error paths are checked, // cross-platform functionality is not yet implemented, and some design // decisions have been made to emphasize readability over efficiency. int wmain(int argc, wchar_t* argv[]) { printf("Sample CoreCLR Host\n\n"); // // STEP 1: Get the app to be run from the command line // if (argc < 2) { printf("ERROR - Specify exe to run as the app's first parameter"); return -1; } // <Snippet1> // The managed application to run should be the first command-line parameter. // Subsequent command line parameters will be passed to the managed app later in this host. wchar_t targetApp[MAX_PATH]; GetFullPathNameW(argv[1], MAX_PATH, targetApp, NULL); // </Snippet1> // Also note the directory the target app is in, as it will be referenced later. // The directory is determined by simply truncating the target app's full path // at the last path delimiter (\) wchar_t targetAppPath[MAX_PATH]; wcscpy_s(targetAppPath, targetApp); size_t i = wcslen(targetAppPath - 1); while (i >= 0 && targetAppPath[i] != L'\\') i--; targetAppPath[i] = L'\0'; // // STEP 2: Find and load CoreCLR.dll // HMODULE coreCLRModule; // Look in %CORE_ROOT% wchar_t coreRoot[MAX_PATH]; size_t outSize; _wgetenv_s(&outSize, coreRoot, MAX_PATH, L"CORE_ROOT"); coreCLRModule = LoadCoreCLR(coreRoot); // If CoreCLR.dll wasn't in %CORE_ROOT%, look next to the target app if (!coreCLRModule) { wcscpy_s(coreRoot, MAX_PATH, targetAppPath); coreCLRModule = LoadCoreCLR(coreRoot); } // If CoreCLR.dll wasn't in %CORE_ROOT% or next to the app, // look in the common 1.1.0 install directory if (!coreCLRModule) { ::ExpandEnvironmentStringsW(coreCLRInstallDirectory, coreRoot, MAX_PATH); coreCLRModule = LoadCoreCLR(coreRoot); } if (!coreCLRModule) { printf("ERROR - CoreCLR.dll could not be found"); return -1; } else { wprintf(L"CoreCLR loaded from %s\n", coreRoot); } // // STEP 3: Get ICLRRuntimeHost2 instance // // <Snippet3> ICLRRuntimeHost2* runtimeHost; FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(coreCLRModule, "GetCLRRuntimeHost"); if (!pfnGetCLRRuntimeHost) { printf("ERROR - GetCLRRuntimeHost not found"); return -1; } // Get the hosting interface HRESULT hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&runtimeHost); // </Snippet3> if (FAILED(hr)) { printf("ERROR - Failed to get ICLRRuntimeHost2 instance.\nError code:%x\n", hr); return -1; } // // STEP 4: Set desired startup flags and start the CLR // // <Snippet4> hr = runtimeHost->SetStartupFlags( // These startup flags control runtime-wide behaviors. // A complete list of STARTUP_FLAGS can be found in mscoree.h, // but some of the more common ones are listed below. static_cast<STARTUP_FLAGS>( // STARTUP_FLAGS::STARTUP_SERVER_GC | // Use server GC // STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN | // Maximize domain-neutral loading // STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST | // Domain-neutral loading for strongly-named assemblies STARTUP_FLAGS::STARTUP_CONCURRENT_GC | // Use concurrent GC STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN | // All code executes in the default AppDomain // (required to use the runtimeHost->ExecuteAssembly helper function) STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN // Prevents domain-neutral loading ) ); // </Snippet4> if (FAILED(hr)) { printf("ERROR - Failed to set startup flags.\nError code:%x\n", hr); return -1; } // Starting the runtime will initialize the JIT, GC, loader, etc. hr = runtimeHost->Start(); if (FAILED(hr)) { printf("ERROR - Failed to start the runtime.\nError code:%x\n", hr); return -1; } else { printf("Runtime started\n"); } // // STEP 5: Prepare properties for the AppDomain we will create // // Flags // <Snippet5> int appDomainFlags = // APPDOMAIN_FORCE_TRIVIAL_WAIT_OPERATIONS | // Do not pump messages during wait // APPDOMAIN_SECURITY_SANDBOXED | // Causes assemblies not from the TPA list to be loaded as partially trusted APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | // Enable platform-specific assemblies to run APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP | // Allow PInvoking from non-TPA assemblies APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT; // Entirely disables transparency checks // </Snippet5> // <Snippet6> // TRUSTED_PLATFORM_ASSEMBLIES // "Trusted Platform Assemblies" are prioritized by the loader and always loaded with full trust. // A common pattern is to include any assemblies next to CoreCLR.dll as platform assemblies. // More sophisticated hosts may also include their own Framework extensions (such as AppDomain managers) // in this list. int tpaSize = 100 * MAX_PATH; // Starting size for our TPA (Trusted Platform Assemblies) list wchar_t* trustedPlatformAssemblies = new wchar_t[tpaSize]; trustedPlatformAssemblies[0] = L'\0'; // Extensions to probe for when finding TPA list files wchar_t *tpaExtensions[] = { L"*.dll", L"*.exe", L"*.winmd" }; // Probe next to CoreCLR.dll for any files matching the extensions from tpaExtensions and // add them to the TPA list. In a real host, this would likely be extracted into a separate function // and perhaps also run on other directories of interest. for (int i = 0; i < _countof(tpaExtensions); i++) { // Construct the file name search pattern wchar_t searchPath[MAX_PATH]; wcscpy_s(searchPath, MAX_PATH, coreRoot); wcscat_s(searchPath, MAX_PATH, L"\\"); wcscat_s(searchPath, MAX_PATH, tpaExtensions[i]); // Find files matching the search pattern WIN32_FIND_DATAW findData; HANDLE fileHandle = FindFirstFileW(searchPath, &findData); if (fileHandle != INVALID_HANDLE_VALUE) { do { // Construct the full path of the trusted assembly wchar_t pathToAdd[MAX_PATH]; wcscpy_s(pathToAdd, MAX_PATH, coreRoot); wcscat_s(pathToAdd, MAX_PATH, L"\\"); wcscat_s(pathToAdd, MAX_PATH, findData.cFileName); // Check to see if TPA list needs expanded if (wcslen(pathToAdd) + (3) + wcslen(trustedPlatformAssemblies) >= tpaSize) { // Expand, if needed tpaSize *= 2; wchar_t* newTPAList = new wchar_t[tpaSize]; wcscpy_s(newTPAList, tpaSize, trustedPlatformAssemblies); trustedPlatformAssemblies = newTPAList; } // Add the assembly to the list and delimited with a semi-colon wcscat_s(trustedPlatformAssemblies, tpaSize, pathToAdd); wcscat_s(trustedPlatformAssemblies, tpaSize, L";"); // Note that the CLR does not guarantee which assembly will be loaded if an assembly // is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll // extensions. Therefore, a real host should probably add items to the list in priority order and only // add a file if it's not already present on the list. // // For this simple sample, though, and because we're only loading TPA assemblies from a single path, // we can ignore that complication. } while (FindNextFileW(fileHandle, &findData)); FindClose(fileHandle); } } // APP_PATHS // App paths are directories to probe in for assemblies which are not one of the well-known Framework assemblies // included in the TPA list. // // For this simple sample, we just include the directory the target application is in. // More complex hosts may want to also check the current working directory or other // locations known to contain application assets. wchar_t appPaths[MAX_PATH * 50]; // Just use the targetApp provided by the user and remove the file name wcscpy_s(appPaths, targetAppPath); // APP_NI_PATHS // App (NI) paths are the paths that will be probed for native images not found on the TPA list. // It will typically be similar to the app paths. // For this sample, we probe next to the app and in a hypothetical directory of the same name with 'NI' suffixed to the end. wchar_t appNiPaths[MAX_PATH * 50]; wcscpy_s(appNiPaths, targetAppPath); wcscat_s(appNiPaths, MAX_PATH * 50, L";"); wcscat_s(appNiPaths, MAX_PATH * 50, targetAppPath); wcscat_s(appNiPaths, MAX_PATH * 50, L"NI"); // NATIVE_DLL_SEARCH_DIRECTORIES // Native dll search directories are paths that the runtime will probe for native DLLs called via PInvoke wchar_t nativeDllSearchDirectories[MAX_PATH * 50]; wcscpy_s(nativeDllSearchDirectories, appPaths); wcscat_s(nativeDllSearchDirectories, MAX_PATH * 50, L";"); wcscat_s(nativeDllSearchDirectories, MAX_PATH * 50, coreRoot); // PLATFORM_RESOURCE_ROOTS // Platform resource roots are paths to probe in for resource assemblies (in culture-specific sub-directories) wchar_t platformResourceRoots[MAX_PATH * 50]; wcscpy_s(platformResourceRoots, appPaths); // AppDomainCompatSwitch // Specifies compatibility behavior for the app domain. This indicates which compatibility // quirks to apply if an assembly doesn't have an explicit Target Framework Moniker. If a TFM is // present on an assembly, the runtime will always attempt to use quirks appropriate to the version // of the TFM. // // Typically the latest behavior is desired, but some hosts may want to default to older Silverlight // or Windows Phone behaviors for compatibility reasons. wchar_t* appDomainCompatSwitch = L"UseLatestBehaviorWhenTFMNotSpecified"; // </Snippet6> // // STEP 6: Create the AppDomain // // <Snippet7> DWORD domainId; // Setup key/value pairs for AppDomain properties const wchar_t* propertyKeys[] = { L"TRUSTED_PLATFORM_ASSEMBLIES", L"APP_PATHS", L"APP_NI_PATHS", L"NATIVE_DLL_SEARCH_DIRECTORIES", L"PLATFORM_RESOURCE_ROOTS", L"AppDomainCompatSwitch" }; // Property values which were constructed in step 5 const wchar_t* propertyValues[] = { trustedPlatformAssemblies, appPaths, appNiPaths, nativeDllSearchDirectories, platformResourceRoots, appDomainCompatSwitch }; // Create the AppDomain hr = runtimeHost->CreateAppDomainWithManager( L"Sample Host AppDomain", // Friendly AD name appDomainFlags, NULL, // Optional AppDomain manager assembly name NULL, // Optional AppDomain manager type (including namespace) sizeof(propertyKeys)/sizeof(wchar_t*), propertyKeys, propertyValues, &domainId); // </Snippet7> if (FAILED(hr)) { printf("ERROR - Failed to create AppDomain.\nError code:%x\n", hr); return -1; } else { printf("AppDomain %d created\n\n", domainId); } // // STEP 7: Run managed code // // ExecuteAssembly will load a managed assembly and execute its entry point. wprintf(L"Executing %s...\n\n", targetApp); // <Snippet8> DWORD exitCode = -1; hr = runtimeHost->ExecuteAssembly(domainId, targetApp, argc - 1, (LPCWSTR*)(argc > 1 ? &argv[1] : NULL), &exitCode); // </Snippet8> if (FAILED(hr)) { wprintf(L"ERROR - Failed to execute %s.\nError code:%x\n", targetApp, hr); return -1; } // Alternatively, to start managed code execution with a method other than an assembly's entrypoint, // runtimeHost->CreateDelegate can be used to create a pointer to an arbitrary static managed method // which can then be executed directly or via runtimeHost->ExecuteInAppDomain. // // void *pfnDelegate = NULL; // hr = runtimeHost->CreateDelegate( // domainId, // L"HW, Version=1.0.0.0, Culture=neutral", // Target managed assembly // L"ConsoleApplication.Program", // Target managed type // L"Main", // Target entry point (static method) // (INT_PTR*)&pfnDelegate); // if (FAILED(hr)) // { // printf("ERROR - Failed to create delegate.\nError code:%x\n", hr); // return -1; // } // ((MainMethodFp*)pfnDelegate)(NULL); // // STEP 8: Clean up // // <Snippet9> runtimeHost->UnloadAppDomain(domainId, true /* Wait until unload complete */); runtimeHost->Stop(); runtimeHost->Release(); // </Snippet9> printf("\nDone\n"); return exitCode; }
bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbose, const bool waitForDebugger, DWORD &exitCode, wchar_t* programPath) { // Assume failure exitCode = -1; HostEnvironment hostEnvironment(&log); wchar_t appPath[MAX_PATH] = W(""); wchar_t appNiPath[MAX_PATH * 2] = W(""); wchar_t managedAssemblyFullName[MAX_PATH] = W(""); wchar_t* filePart = NULL; if (!::GetFullPathName(programPath, MAX_PATH, appPath, &filePart)) { log << W("Failed to get full path: ") << programPath << Logger::endl; log << W("Error code: ") << GetLastError() << Logger::endl; return false; } wcscpy_s(managedAssemblyFullName, appPath); *(filePart) = W('\0'); log << W("Loading: ") << managedAssemblyFullName << Logger::endl; wcscpy_s(appNiPath, appPath); wcscat_s(appNiPath, MAX_PATH * 2, W(";")); wcscat_s(appNiPath, MAX_PATH * 2, appPath); // Construct native search directory paths wchar_t nativeDllSearchDirs[MAX_PATH * 3]; wcscpy_s(nativeDllSearchDirs, appPath); wchar_t coreLibraries[MAX_PATH]; size_t outSize; if (_wgetenv_s(&outSize, coreLibraries, MAX_PATH, W("CORE_LIBRARIES")) == 0 && outSize > 0) { wcscat_s(nativeDllSearchDirs, MAX_PATH * 3, W(";")); wcscat_s(nativeDllSearchDirs, MAX_PATH * 3, coreLibraries); } wcscat_s(nativeDllSearchDirs, MAX_PATH * 3, W(";")); wcscat_s(nativeDllSearchDirs, MAX_PATH * 3, hostEnvironment.m_coreCLRDirectoryPath); // Start the CoreCLR ICLRRuntimeHost2 *host = hostEnvironment.GetCLRRuntimeHost(); if (!host) { return false; } HRESULT hr; log << W("Setting ICLRRuntimeHost2 startup flags") << Logger::endl; // Default startup flags hr = host->SetStartupFlags((STARTUP_FLAGS) (STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN)); if (FAILED(hr)) { log << W("Failed to set startup flags. ERRORCODE: ") << hr << Logger::endl; return false; } log << W("Starting ICLRRuntimeHost2") << Logger::endl; hr = host->Start(); if (FAILED(hr)) { log << W("Failed to start CoreCLR. ERRORCODE: ") << hr << Logger:: endl; return false; } //------------------------------------------------------------- // Create an AppDomain // Allowed property names: // APPBASE // - The base path of the application from which the exe and other assemblies will be loaded // // TRUSTED_PLATFORM_ASSEMBLIES // - The list of complete paths to each of the fully trusted assemblies // // APP_PATHS // - The list of paths which will be probed by the assembly loader // // APP_NI_PATHS // - The list of additional paths that the assembly loader will probe for ngen images // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke // const wchar_t *property_keys[] = { W("TRUSTED_PLATFORM_ASSEMBLIES"), W("APP_PATHS"), W("APP_NI_PATHS"), W("NATIVE_DLL_SEARCH_DIRECTORIES") W("AppDomainCompatSwitch"), }; const wchar_t *property_values[] = { // TRUSTED_PLATFORM_ASSEMBLIES hostEnvironment.GetTpaList(), // APP_PATHS appPath, // APP_NI_PATHS appNiPath, // NATIVE_DLL_SEARCH_DIRECTORIES nativeDllSearchDirs, }; log << W("Creating an AppDomain") << Logger::endl; log << W("TRUSTED_PLATFORM_ASSEMBLIES=") << property_values[0] << Logger::endl; log << W("APP_PATHS=") << property_values[1] << Logger::endl; log << W("APP_NI_PATHS=") << property_values[2] << Logger::endl; log << W("NATIVE_DLL_SEARCH_DIRECTORIES=") << property_values[3] << Logger::endl; DWORD domainId; hr = host->CreateAppDomainWithManager( hostEnvironment.GetHostExeName(), // The friendly name of the AppDomain // Flags: // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS // - By default CoreCLR only allows platform neutral assembly to be run. To allow // assemblies marked as platform specific, include this flag // // APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP // - Allows sandboxed applications to make P/Invoke calls and use COM interop // // APPDOMAIN_SECURITY_SANDBOXED // - Enables sandboxing. If not set, the app is considered full trust // // APPDOMAIN_IGNORE_UNHANDLED_EXCEPTION // - Prevents the application from being torn down if a managed exception is unhandled // APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP | APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT, NULL, // Name of the assembly that contains the AppDomainManager implementation NULL, // The AppDomainManager implementation type name sizeof(property_keys)/sizeof(wchar_t*), // The number of properties property_keys, property_values, &domainId); if (FAILED(hr)) { log << W("Failed call to CreateAppDomainWithManager. ERRORCODE: ") << hr << Logger::endl; return false; } if(waitForDebugger) { if(!IsDebuggerPresent()) { log << W("Waiting for the debugger to attach. Press any key to continue ...") << Logger::endl; getchar(); if (IsDebuggerPresent()) { log << "Debugger is attached." << Logger::endl; } else { log << "Debugger failed to attach." << Logger::endl; } } } hr = host->ExecuteAssembly(domainId, managedAssemblyFullName, argc, (argc)?&(argv[0]):NULL, &exitCode); if (FAILED(hr)) { log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << hr << Logger::endl; return false; } log << W("App exit value = ") << exitCode << Logger::endl; //------------------------------------------------------------- // Unload the AppDomain log << W("Unloading the AppDomain") << Logger::endl; hr = host->UnloadAppDomain( domainId, true); // Wait until done if (FAILED(hr)) { log << W("Failed to unload the AppDomain. ERRORCODE: ") << hr << Logger::endl; return false; } //------------------------------------------------------------- // Stop the host log << W("Stopping the host") << Logger::endl; hr = host->Stop(); if (FAILED(hr)) { log << W("Failed to stop the host. ERRORCODE: ") << hr << Logger::endl; return false; } //------------------------------------------------------------- // Release the reference to the host log << W("Releasing ICLRRuntimeHost2") << Logger::endl; host->Release(); return true; }
extern "C" __declspec(dllexport) HRESULT __stdcall CallApplicationMain(PCALL_APPLICATION_MAIN_DATA data) { HRESULT hr = S_OK; errno_t errno = 0; FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = nullptr; ICLRRuntimeHost2* pCLRRuntimeHost = nullptr; TCHAR szCurrentDirectory[MAX_PATH]; TCHAR szCoreClrDirectory[MAX_PATH]; TCHAR lpCoreClrModulePath[MAX_PATH]; size_t cchTrustedPlatformAssemblies = 0; LPWSTR pwszTrustedPlatformAssemblies = nullptr; Win32KDisable(); if (data->runtimeDirectory) { errno = wcscpy_s(szCurrentDirectory, data->runtimeDirectory); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); } else { GetModuleDirectory(NULL, szCurrentDirectory); } HMODULE hCoreCLRModule = LoadCoreClr(); if (!hCoreCLRModule) { printf_s("Failed to locate coreclr.dll.\n"); return E_FAIL; } // Get the path to the module DWORD dwCoreClrModulePathSize = GetModuleFileName(hCoreCLRModule, lpCoreClrModulePath, MAX_PATH); lpCoreClrModulePath[dwCoreClrModulePathSize] = '\0'; GetModuleDirectory(hCoreCLRModule, szCoreClrDirectory); HMODULE ignoreModule; // Pin the module - CoreCLR.dll does not support being unloaded. if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, lpCoreClrModulePath, &ignoreModule)) { printf_s("Failed to pin coreclr.dll.\n"); return E_FAIL; } pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(hCoreCLRModule, "GetCLRRuntimeHost"); if (!pfnGetCLRRuntimeHost) { printf_s("Failed to find export GetCLRRuntimeHost.\n"); return E_FAIL; } hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&pCLRRuntimeHost); if (FAILED(hr)) { printf_s("Failed to get IID_ICLRRuntimeHost2.\n"); return hr; } STARTUP_FLAGS dwStartupFlags = (STARTUP_FLAGS)( STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN | STARTUP_FLAGS::STARTUP_SERVER_GC ); pCLRRuntimeHost->SetStartupFlags(dwStartupFlags); // Authenticate with either CORECLR_HOST_AUTHENTICATION_KEY or CORECLR_HOST_AUTHENTICATION_KEY_NONGEN hr = pCLRRuntimeHost->Authenticate(CORECLR_HOST_AUTHENTICATION_KEY); if (FAILED(hr)) { printf_s("Failed to Authenticate().\n"); return hr; } hr = pCLRRuntimeHost->Start(); if (FAILED(hr)) { printf_s("Failed to Start().\n"); return hr; } const wchar_t* property_keys[] = { // Allowed property names: // APPBASE // - The base path of the application from which the exe and other assemblies will be loaded L"APPBASE", // // TRUSTED_PLATFORM_ASSEMBLIES // - The list of complete paths to each of the fully trusted assemblies L"TRUSTED_PLATFORM_ASSEMBLIES", // // APP_PATHS // - The list of paths which will be probed by the assembly loader L"APP_PATHS", // // APP_NI_PATHS // - The list of additional paths that the assembly loader will probe for ngen images // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke // }; cchTrustedPlatformAssemblies = TRUSTED_PLATFORM_ASSEMBLIES_STRING_BUFFER_SIZE_CCH; pwszTrustedPlatformAssemblies = (LPWSTR)calloc(cchTrustedPlatformAssemblies+1, sizeof(WCHAR)); if (pwszTrustedPlatformAssemblies == NULL) { goto Finished; } pwszTrustedPlatformAssemblies[0] = L'\0'; // Try native images first if (!GetTrustedPlatformAssembliesList(szCoreClrDirectory, true, pwszTrustedPlatformAssemblies, cchTrustedPlatformAssemblies)) { if (!GetTrustedPlatformAssembliesList(szCoreClrDirectory, false, pwszTrustedPlatformAssemblies, cchTrustedPlatformAssemblies)) { printf_s("Failed to find files in the coreclr directory\n"); return E_FAIL; } } // Add the assembly containing the app domain manager to the trusted list errno = wcscat_s(pwszTrustedPlatformAssemblies, cchTrustedPlatformAssemblies, szCurrentDirectory); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); errno = wcscat_s(pwszTrustedPlatformAssemblies, cchTrustedPlatformAssemblies, L"kre.coreclr.managed.dll"); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); //wstring appPaths(szCurrentDirectory); WCHAR wszAppPaths[MAX_PATH]; wszAppPaths[0] = L'\0'; errno = wcscat_s(wszAppPaths, _countof(wszAppPaths), szCurrentDirectory); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); errno = wcscat_s(wszAppPaths, _countof(wszAppPaths), L";"); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); errno = wcscat_s(wszAppPaths, _countof(wszAppPaths), szCoreClrDirectory); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); errno = wcscat_s(wszAppPaths, _countof(wszAppPaths), L";"); CHECK_RETURN_VALUE_FAIL_EXIT_VIA_FINISHED(errno); const wchar_t* property_values[] = { // APPBASE data->applicationBase, // TRUSTED_PLATFORM_ASSEMBLIES pwszTrustedPlatformAssemblies, // APP_PATHS wszAppPaths, }; DWORD domainId; DWORD dwFlagsAppDomain = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP; LPCWSTR szAssemblyName = L"kre.coreclr.managed, Version=0.1.0.0"; LPCWSTR szEntryPointTypeName = L"DomainManager"; LPCWSTR szMainMethodName = L"Execute"; int nprops = sizeof(property_keys) / sizeof(wchar_t*); hr = pCLRRuntimeHost->CreateAppDomainWithManager( L"kre.coreclr.managed", dwFlagsAppDomain, NULL, NULL, nprops, property_keys, property_values, &domainId); if (FAILED(hr)) { wprintf_s(L"TPA %d %S\n", wcslen(pwszTrustedPlatformAssemblies), pwszTrustedPlatformAssemblies); wprintf_s(L"AppPaths %S\n", wszAppPaths); printf_s("Failed to create app domain (%x).\n", hr); return hr; } HostMain pHostMain; hr = pCLRRuntimeHost->CreateDelegate( domainId, szAssemblyName, szEntryPointTypeName, szMainMethodName, (INT_PTR*)&pHostMain); if (FAILED(hr)) { printf_s("Failed to create main delegate (%x).\n", hr); return hr; } SetEnvironmentVariable(L"KRE_FRAMEWORK", L"aspnetcore50"); // Call main data->exitcode = pHostMain(data->argc, data->argv); pCLRRuntimeHost->UnloadAppDomain(domainId, true); pCLRRuntimeHost->Stop(); Finished: if (pwszTrustedPlatformAssemblies != NULL) { free(pwszTrustedPlatformAssemblies); pwszTrustedPlatformAssemblies = NULL; } if (FAILED(hr)) { return hr; } else { return S_OK; } }
extern "C" __declspec(dllexport) bool __stdcall CallApplicationMain(PCALL_APPLICATION_MAIN_DATA data) { HRESULT hr = S_OK; FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = nullptr; ICLRRuntimeHost2* pCLRRuntimeHost = nullptr; TCHAR szCurrentDirectory[MAX_PATH]; TCHAR szCoreClrDirectory[MAX_PATH]; TCHAR lpCoreClrModulePath[MAX_PATH]; GetModuleDirectory(NULL, szCurrentDirectory); HMODULE hCoreCLRModule = LoadCoreClr(); if (!hCoreCLRModule) { printf_s("Failed to locate coreclr.dll.\n"); return false; } // Get the path to the module DWORD dwCoreClrModulePathSize = GetModuleFileName(hCoreCLRModule, lpCoreClrModulePath, MAX_PATH); lpCoreClrModulePath[dwCoreClrModulePathSize] = '\0'; GetModuleDirectory(hCoreCLRModule, szCoreClrDirectory); HMODULE ignoreModule; // Pin the module - CoreCLR.dll does not support being unloaded. if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, lpCoreClrModulePath, &ignoreModule)) { printf_s("Failed to pin coreclr.dll.\n"); return false; } pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(hCoreCLRModule, "GetCLRRuntimeHost"); if (!pfnGetCLRRuntimeHost) { printf_s("Failed to find export GetCLRRuntimeHost.\n"); return false; } hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&pCLRRuntimeHost); if (FAILED(hr)) { printf_s("Failed to get IID_ICLRRuntimeHost2.\n"); return false; } STARTUP_FLAGS dwStartupFlags = (STARTUP_FLAGS)( STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN ); pCLRRuntimeHost->SetStartupFlags(dwStartupFlags); // Authenticate with either CORECLR_HOST_AUTHENTICATION_KEY or CORECLR_HOST_AUTHENTICATION_KEY_NONGEN hr = pCLRRuntimeHost->Authenticate(CORECLR_HOST_AUTHENTICATION_KEY); if (FAILED(hr)) { printf_s("Failed to Authenticate().\n"); return false; } hr = pCLRRuntimeHost->Start(); if (FAILED(hr)) { printf_s("Failed to Start().\n"); return false; } const wchar_t* property_keys[] = { // Allowed property names: // APPBASE // - The base path of the application from which the exe and other assemblies will be loaded L"APPBASE", // // TRUSTED_PLATFORM_ASSEMBLIES // - The list of complete paths to each of the fully trusted assemblies L"TRUSTED_PLATFORM_ASSEMBLIES", // // APP_PATHS // - The list of paths which will be probed by the assembly loader L"APP_PATHS", // // APP_NI_PATHS // - The list of additional paths that the assembly loader will probe for ngen images // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke // }; wstring trustedPlatformAssemblies(L""); // Enumerate the core clr directory and add each .dll or .ni.dll to the list of trusted assemblies wstring pattern(szCoreClrDirectory); pattern += L"*.dll"; WIN32_FIND_DATA ffd; HANDLE findHandle = FindFirstFile(pattern.c_str(), &ffd); if (INVALID_HANDLE_VALUE == findHandle) { printf_s("Failed to find files in the coreclr directory\n"); return -1; } do { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Skip directories } else { trustedPlatformAssemblies += szCoreClrDirectory; trustedPlatformAssemblies += ffd.cFileName; trustedPlatformAssemblies += L";"; } } while (FindNextFile(findHandle, &ffd) != 0); // Add the assembly containing the app domain manager to the trusted list trustedPlatformAssemblies += szCurrentDirectory; trustedPlatformAssemblies += L"klr.core45.managed.dll"; wstring appPaths(szCurrentDirectory); appPaths += L";"; appPaths += szCoreClrDirectory; const wchar_t* property_values[] = { // APPBASE szCurrentDirectory, // TODO: Allow overriding this // TRUSTED_PLATFORM_ASSEMBLIES trustedPlatformAssemblies.c_str(), // APP_PATHS appPaths.c_str(), }; DWORD domainId; DWORD dwFlagsAppDomain = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP | APPDOMAIN_ENABLE_ASSEMBLY_LOADFILE; LPCWSTR szAssemblyName = L"klr.core45.managed, Version=1.0.0.0"; LPCWSTR szDomainManagerTypeName = L"DomainManager"; LPCWSTR szMainMethodName = L"Main"; int nprops = sizeof(property_keys) / sizeof(wchar_t*); hr = pCLRRuntimeHost->CreateAppDomainWithManager( L"klr.core45.managed", dwFlagsAppDomain, szAssemblyName, szDomainManagerTypeName, nprops, property_keys, property_values, &domainId); if (FAILED(hr)) { printf_s("Failed to create app domain (%d).\n", hr); return false; } HostMain pHostMain; hr = pCLRRuntimeHost->CreateDelegate( domainId, szAssemblyName, szDomainManagerTypeName, szMainMethodName, (INT_PTR*)&pHostMain); if (FAILED(hr)) { printf_s("Failed to create main delegate (%d).\n", hr); return false; } // Call main data->exitcode = pHostMain(szCurrentDirectory, data->argc, data->argv); pCLRRuntimeHost->UnloadAppDomain(domainId, true); pCLRRuntimeHost->Stop(); return hr; }