首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 動態載入 AppDomain 實作(一):應用程式域隔離與動態組件載入/卸載機制

     編輯:關於C語言

在作業系統中,利用進程隔離執行中的應用程式,每個應用程式載入獨立進程並分配虛擬記憶體,進程無法直接存取實體記憶體,僅能透過 OS 映射,確保進程間實體記憶體不重疊。但進程最大缺點是效率問題,尤其是切換開銷大,且進程間無法共享記憶體,無法傳遞指標。

.NET 引入新概念:AppDomain(應用程式域)。所有 .NET 應用程式皆需在託管環境中執行,OS 僅提供進程,故 .NET 程式需透過 AppDomain 這個媒介運行於進程中,同時使用該進程提供的記憶體空間。只要是 .NET 應用,都會運行在某個 AppDomain 中。

執行 .NET 應用程式或執行庫宿主時,OS 先建立進程,再在進程中載入 CLR(通常透過 _CorExeMain 或 _CorBindToRuntimeEx),載入 CLR 時會建立預設 AppDomain,它是 CLR 的執行單元,程式的 Main 方法即在這裡執行。預設 AppDomain 唯一且無法卸載,進程結束時才隨之消失。

一個進程可擁有多個 AppDomain,彼此隔離。組件無法單獨執行,必須載入某個 AppDomain;要卸載組件只能卸載其 AppDomain。

近期專案需實作一個模組:定制作業管理器,可定時以不同頻率(一次、每天、每週、每月)執行某些 .NET 應用程式或預存程序。調用預存程序無難度,但調用 .NET 應用程式時需考慮:

一旦組件被作業管理器服務調用(如某執行計劃到期),調用前會將組件載入預設 AppDomain 再執行。問題來了:若需替換或刪除組件,因已被預設 AppDomain 載入,更改會被拒絕,拋出錯誤:

除非關閉作業管理服務再操作,顯然不合理。且預設 AppDomain 無法卸載,怎麼辦?解決方案:動態載入組件,建立新 AppDomain,讓組件載入新域執行,執行完畢卸載新域即可。步驟如下:

1、建立組件載入類別 AssemblyDynamicLoader

用來建立新 AppDomain,並產生用於執行 .NET 程式的 RemoteLoader 類別:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using Ark.Log;

/// <summary> The local loader. </summary>
public class AssemblyDynamicLoader
{
    private static ILog log = LogManager.GetLogger(typeof(AssemblyDynamicLoader));
    private AppDomain appDomain;
    private RemoteLoader remoteLoader;

    public AssemblyDynamicLoader()
    {
        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationName = "ApplicationLoader";
        setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
        setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
        setup.CachePath = setup.ApplicationBase;
        setup.ShadowCopyFiles = "true";
        setup.ShadowCopyDirectories = setup.ApplicationBase;

        this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
        String name = Assembly.GetExecutingAssembly().GetName().FullName;
        this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);
    }

    public String InvokeMethod(String fullName, String className, String argsInput, String programName)
    {
        this.remoteLoader.InvokeMethod(fullName, className, argsInput, programName);
        return this.remoteLoader.Output;
    }

    public void Unload()
    {
        try { AppDomain.Unload(this.appDomain); this.appDomain = null; }
        catch (CannotUnloadAppDomainException ex) { log.Error("To unload assembly error!", ex); }
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved