經過修改CoreCLR中的ClrHost實現自託管程序

上一篇咱們講了如何在windows和Linux上編譯CoreClr的問題 雖然文章使用的是windows 10 (Bash)環境,可是也能夠作爲ubuntu環境的參考。git

成功編譯CoreCLR的源代碼以後,會在**\coreclr\bin\Product\Windows_NT.x64.{*}**目錄生成對應的二進制文件,這裏包含了基本的CLR運行時文件。其中就有咱們此次想要修改的CoreRun.exe文件,它就是CLRHost的入口可執行程序,等同於dotnet命令。github

固然本篇文章主要是以windows環境爲例,經過修改Windowst版本的CoreRun爲例來介紹,如何實現一個本身的自託管程序入口。shell

要想編輯Windows環境的源代碼首先也是一樣的須要編譯CoreCLR源代碼的。成功編譯後會在coreclr\bin\obj\Windows_NT.x64.Debug 目錄下看到VC++的項目和解決方案。打開CoreCLR.sln解決方案,能夠看到其中的CoreRun項目。ubuntu

首先它是一個Win32項目,我在這裏只簡單的講幾處關鍵的代碼段,有興趣的同窗能夠到Github上去看看CoreRun源代碼windows

先說一下咱們想要達到的效果吧:
想要使用CoreRun啓動一個dotnet程序集只須要以下命令:數組

corerun  demo.dll

固然想真正執行起來,還須要在系統環境變量裏添加CORE_ROOT來指定已經安裝的CoreCLR目錄。app

但此次想達到的目標是不須要指定Runtime目錄也不須要指定dll文件名,以下:dom

demo.exe

這樣是否是寫發佈一個自託管程序是同樣的?接下來,咱們來經過修改代碼來實現這一目標。函數

首先找到HostEnvironment類,看下它的代碼段第112行學習

StackSString coreRoot;
m_coreCLRModule = NULL; // Initialize this here since we don't call TryLoadCoreCLR if CORE_ROOT is unset.
if (WszGetEnvironmentVariable(W("CORE_ROOT"), coreRoot) > 0 && coreRoot.GetCount() > 0)
{
    coreRoot.Append(W('\\'));
    m_coreCLRModule = TryLoadCoreCLR(coreRoot);
}

它經過獲取系統環境變量CORE_ROOT的值來定位CoreCLR目錄,並傳遞給TryLoadCoreCLR函數,來加載CoreCLR.dll文件。

下面來到主函數TryRun:

//獲取命令行參數數組的指針
const wchar_t* exeName = argc > 0 ? argv[0] : nullptr;
if(exeName == nullptr)
{
    log << W("No exename specified.") << Logger::endl;
    return false;
}

StackSString appPath;
StackSString appNiPath;
StackSString managedAssemblyFullName;
StackSString appLocalWinmetadata;

wchar_t* filePart = NULL;

COUNT_T size = MAX_LONGPATH;
//獲取可執行文件路徑,如:src\coreclr\hosts\corerun\Debug\CoreRun.exe
wchar_t* appPathPtr = appPath.OpenUnicodeBuffer(size - 1);
DWORD length = WszGetFullPathName(exeName, size, appPathPtr, &filePart);
if (length >= size)
{
    appPath.CloseBuffer();
    size = length;
    //獲取程序集名稱,如:Demo.dll
    appPathPtr = appPath.OpenUnicodeBuffer(size - 1);
    length = WszGetFullPathName(exeName, size, appPathPtr, &filePart);
}
if (length == 0 || length >= size) {
    log << W("Failed to get full path: ") << exeName << Logger::endl;
    log << W("Error code: ") << GetLastError() << Logger::endl;
    return false;
} 
//設置程序集名稱變量
managedAssemblyFullName.Set(appPathPtr);

中間的代碼就省略了,無非是建立ICLRRuntimeHost2接口,加載參數如gc_server等以後就是建立AppDomain生成domainId。

//這裏啓動的就是上面設置的程序集的全路徑
hr = host->ExecuteAssembly(domainId, managedAssemblyFullName, argc-1, (argc-1)?&(argv[1]):NULL, &exitCode);
if (FAILED(hr)) {
    log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << Logger::hresult << hr << Logger::endl;
    return false;
}

ExecuteAssembly函數會真正的經過domainId執行這個程序集。

其實講到這裏有的朋友應該已經明白了,想要達到咱們的目標,只須要作兩件事兒。

  • 1.修改CORE_ROOT的加載方式
    首先修改HostEnvironment類,將獲取環境CORE_ROOT的代碼去掉,而後修改構造函數將路徑做爲參數(coreRoot)傳入。
HostEnvironment(StackSString coreRoot, Logger *logger)
        : m_log(logger), m_CLRRuntimeHost(nullptr) {
        //......省略代碼
         //
         m_coreCLRModule = TryLoadCoreCLR(coreRoot);
這裏我使用的方式爲不加載環境變量,而是指向加載目錄(也就是程序執行目錄appPath或是指向子目錄),我使用的是後者指向了一個名爲**Runtimes**的子目錄。
  • 2.修改程序集路徑的獲取方式
//聲明程序集路徑變量
StackSString assemblyPath;
//獲取可執行文件路徑
assemblyPath.Set(appPathPtr);
SString::CIterator lastBackslash = assemblyPath.End();
assemblyPath.FindBack(lastBackslash, W('\\'));
//分離路徑與文件名,如 ../corerun/bin/debug/  和  corerun.exe
managedAssemblyFullName.Set(assemblyPath, assemblyPath.Begin(), lastBackslash + 1);
//聲明臨時變量計算程序集文件名
StackSString tempName;
StackSString assemblyName;
tempName.Set(filePart);
auto endofName = tempName.End();
//查找到擴展名標誌"."位置
tempName.FindBack(endofName, W('.'));
assemblyName.Set(tempName, tempName.Begin(), endofName + 1);
//替換exe爲dll
assemblyName.Append(W("dll"));
managedAssemblyFullName.Append(assemblyName);

*(filePart) = W('\0');
appPath.CloseBuffer(DWORD(filePart - appPathPtr));
//打印完整的dll路徑
log << W("Loading: ") << managedAssemblyFullName.GetUnicode() << Logger::endl;

想實現自託管的方式,就能夠參考dotnet publish的生成文件,它生成是將可執行文件.exe與程序集文件同名如: demo.exe 、 demo.dll 這樣的文件組織方式。其實解決方案就是獲得exeName後,獲取當前執行文件的全路徑,提取出路徑和文件名兩個部分,並將文件名進行替換,這樣可執行文件在加載時就會默認加載與它同名的程序集文件,來作爲ExecuteAssembly的參數來執行些程序集。

Demo和修改的源代碼,已經上傳到QQ羣文件中(Demos\CoreCLRDemo.zip),僅供參考。

GitHub:https://github.com/maxzhang1985/YOYOFx 若是覺還能夠請Star下, 歡迎一塊兒交流。

.NET Core 開源學習羣:214741894

相關文章
相關標籤/搜索