<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      cad.net dll動態加載和卸載

      20250320
      不應該loadx時候采用鏈式加載,而是loadx命令只加載目標dll,
      每個加載的dll應該獨立程序域.
      如果加入了同名不同版本的,就可以自動卸載之前版本的.
      當觸發需要之前的版本時候,通過事件加載回來.

      我實現在此處:
      插件式架構最小實現:
      http://www.rzrgm.cn/JJBox/p/18800009

      下面文章為舊文,內容存在錯誤,
      例如我把Assembly類傳輸了,這是一個錯誤,跨域只能傳輸值類型,
      請用上文最小實現.
      _
      _
      _
      _
      _
      _
      _

      需求

      1: 我們cad.net開發都會面臨一個問題,
      cad在一直打開的狀態下netload兩次同名但版本不同的dll,
      它只會用第一次載入的,也就是無法覆蓋.
      也沒法做到熱插拔...
      2: 制作一個拖拉dll到cad加載,
      但是不想通過發送netload到命令欄以明文形式加載...

      已有的資料 明經netloadx 似乎是不二之選...
      利用了程序域進行加載和卸載,程序域本質上是一個輕量級進程.
      真正令我開始研究是因為若海提出的
      netloadx 在 a.dll 引用了 b.dll 時候,為什么不會成功調用...

      我試圖嘗試直接 Assembly.Load(File.ReadAllBytes(path))
      在加載目錄的每個文件,并沒有報錯,
      然后出現了一個情況,能使用單獨的命令,
      卻還是不能跨dll調用,也就是會有運行出錯(runtime error).

      注明: Assembly.Load(byte), 轉為byte是為了實現熱插拔,
      Assembly.LoadForm()沒有byte重載,
      也就無法拷貝到內存中去,故此不考慮.

      img

      南勝寫了一篇文章回答了,
      但是只支持一個引用的dll,而我需要知道引用的引用...的dll.
      所以對他的代碼修改一番.

      工程開始

      項目地址
      目前博客比較新

      首先,共有四個項目.

      1. cad主插件項目:直接netload的項目.
      2. cad次插件:testa,testb [給a引用],testc [給b引用],后面還有套娃也可以...
      graph TD netload命令加載-->cad主插件-->加載-->cad次插件 cad次插件testA-->引用-->testB-->testC-->test....

      cad子插件項目

      // testa項目代碼
      namespace LoadTesta {
          public class MyCommands {
              [CommandMethod(nameof(testa))]
              public static void testa() {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n 自帶函數" + mameof(testa));
              }
      
              [CommandMethod(nameof(gggg))]
              public void gggg()  {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n ****" + nameof(gggg));  
                  testb.MyCommands.TestBHello();
              }
          }
      }
      
      
      // testb項目代碼
      namespace LoadTestb {
          public class MyCommands {
              public static void TestBHello() {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n ****" + nameof(TestBHello));
                  testc.MyCommands.TestcHello();   
              }
      
              [CommandMethod(nameof(testb))]
              public static void testb() {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n 自帶函數" + nameof(testb));
              }
          }
      }
      
      
      // testc項目代碼
      namespace LoadTestc {
          public class MyCommands {
              public static void TestCHello() {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n ****" + nameof(TestCHello));
              }
      
              [CommandMethod(nameof(testc))]
              public static void testc() {
                  var doc = Acap.DocumentManager.MdiActiveDocument;
                  if (doc is null) return;
                  doc.Editor.WriteMessage("\r\n 自帶函數" + nameof(testc));
              }
          }
      }
      

      迭代版本號

      必須更改版本號最后是*,否則無法重復加載(所有)
      如果想加載時候動態修改dll的版本號,需要學習PE讀寫.(此文略)

      net framework要直接編輯項目文件.csproj,啟用由vs迭代版本號:

      <PropertyGroup>
        <Deterministic>False</Deterministic>
      </PropertyGroup>
      

      然后修改AssemblyInfo.cs
      img

      net standard只需要增加.csproj的這里,沒有自己加一個:

      <PropertyGroup>
          <AssemblyVersion>1.0.0.*</AssemblyVersion> 
          <FileVersion>1.0.0.0</FileVersion>
          <Deterministic>False</Deterministic>
      </PropertyGroup>
      

      cad主插件項目

      概念
      先說一下測試環境和概念,
      cad主插件上面寫一個命令,
      調用了WinForm窗體讓它接受拖拽dll文件,獲取dll路徑加載.
      也可以簡化,做一個loadx命令,獲取dll路徑加載.

      這個時候需要直接啟動cad,
      然后調用netload命令加載cad主插件的dll.
      如果采用vs調試cad啟動的話還是會出錯.

      經過若海兩天的Debug發現了: 不能在vs調試狀態下運行cad!應該直接啟動它!
      猜想:這個時候令vs令所有 Assembly.Load(byte) 都進入了vs內存上面,
      vs自動占用到 obj\Debug 文件夾下的dll.
      我開了個新文章寫這個問題

      啟動cad之后,用命令調用出WinForm窗體,
      再利用拖拽testa.dll的方式,就可以鏈式加載到所有的dll了!
      再修改testa.dll重新編譯,再拖拽到WinForm窗體加載,
      再修改testb.dll重新編譯,再拖拽到WinForm窗體加載,
      再修改testc.dll重新編譯,再拖拽到WinForm窗體加載
      ...如此如此,這般這般...

      WinForm窗體拖拽這個函數網絡搜一下基本能搞定,我就不貼代碼了,
      接收拖拽之后就有個testa.dll的路徑,再調用傳給加載函數就好了.

      獲取依賴鏈并加載

      因為此處引用了 nuget 的 Lib.Harmony
      所以單獨分一個工程出來作為cad工程的引用
      免得污染了cad工程的純潔

      #define HarmonyPatch
      #define HarmonyPatch_1
      
      namespace IFoxCAD.LoadEx;
      #if HarmonyPatch_1
      [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")]
      #endif
      public class AssemblyDependent : IEnumerable<Assembly> {
      #if HarmonyPatch
          // 這個是不能刪除的,否則就不執行了
          // HarmonyPatch hook method 返回 false 表示攔截原函數
          public static bool Prefix() { return false; }
      #endif
      
          /// <summary>
          /// 攔截cad的Loader異常:默認是<paramref name="false"/>
          /// </summary>
          public bool PatchExtensionLoader = false;
      
          /// <summary>
          /// 當前程序域運行時解析事件,運行時缺失查找加載路徑
          /// </summary>
          public event ResolveEventHandler AssemblyResolve {
              add { AppDomain.CurrentDomain.AssemblyResolve += value; }
              remove { AppDomain.CurrentDomain.AssemblyResolve -= value; }
          }
      
          /// <summary>
          /// 當前程序域僅反射解析事件,運行時缺失查找加載路徑
          /// </summary>
          public event ResolveEventHandler ReflectionOnlyAssemblyResolve {
              add { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += value; }
              remove { AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= value; }
          }
      
          /// <summary>
          /// 加載成功的程序集
          /// 成功的會被程序域持有
          /// 此處有路徑索引,防止卸載時候有強引用造成卸載失敗
          /// </summary>
          public Dictionary<string, WeakReference<Assembly>> LoadMap = new(StringComparer.OrdinalIgnoreCase);
          Dictionary<string, string> LoadMapError = new();
      
          // 迭代器
          public IEnumerator<Assembly> GetEnumerator()
              // 先收集無效鍵,再移除.
              var invalidKeys = _adep.LoadMap
                  .Where(pair => !pair.Value.TryGetTarget(out _))
                  .Select(pair => pair.Key)
                  .ToList();
              foreach (var key in invalidKeys)
                  _adep.LoadMap.Remove(key);
      
              // 返回剩余的有效程序集
              foreach (var pair in _adep.LoadMap) {
                  if (pair.Value.TryGetTarget(out var assembly) && assembly != null)
                      yield return assembly;
              }
          }
      
          IEnumerator IEnumerable.GetEnumerator() {
              return GetEnumerator();
          }
      
          /// <summary>
          /// 加載程序集
          /// </summary>
          /// <param name="dllFile">dll的文件位置</param>
          /// <param name="byteLoad">true字節加載,false文件加載</param>
          /// <returns>參數加載成功標識<paramref name="dllFile"/></returns>
          [MethodImpl(MethodImplOptions.NoInlining)]
          public bool Load(string dllFile, bool byteLoad = true) {
              if (dllFile is null)
                  throw new ArgumentNullException(nameof(dllFile));
              // 相對路徑要先轉換
              dllFile = Path.GetFullPath(dllFile);
              if (!File.Exists(dllFile))
                  throw new ArgumentException("路徑不存在");
      
              // 獲取加載鏈,Location不一定存在
              // FullName: System.ComponentModel.Primitives, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
              // Location: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.0\System.ComponentModel.Primitives.dll 
      
              HashSet<string> dllFileSet = new(StringComparer.OrdinalIgnoreCase);
              var cadAssembly = AppDomain.CurrentDomain.GetAssemblies()
                  .ToDictionary(ass => ass.FullName, ass => ass, StringComparer.OrdinalIgnoreCase);
              var cadAssemblyRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
                  .ToDictionary(ass => ass.FullName, ass => ass, StringComparer.OrdinalIgnoreCase);
              GetDependencyLink(cadAssembly, cadAssemblyRef, dllFile, dllFileSet);
      
              // 加載鏈進行加載
              foreach(var mfile in dllFileSet) {
                  // 名稱和版本號完全一致,不進行加載
                  if (cadAssembly.ContiansKey(AssemblyName.GetAssemblyName(mfile).FullName)) {
                      LoadMapError[mfile] = "版本號已經存在";
                      continue;
                  }
                  try {
                      var ass = GetPdbAssembly(mfile);
                      ass ??= byteLoad
                          ? Assembly.Load(File.ReadAllBytes(mfile))
                          : Assembly.LoadFile(mfile);
                      LoadMap[mfile] = ass;
                  } catch(Exception ex) { LoadMapError[mfile] = "{ex.Message}"; }
              }
              return LoadMap.ContainsKey(dllFile);
          }
      
          // 為了實現Debug時候出現斷點
          // http://www.rzrgm.cn/DasonKwok/p/10510218.html
          // http://www.rzrgm.cn/DasonKwok/p/10523279.html
          /// <summary>
          /// 在debug模式的時候才獲取PBD調試信息
          /// </summary>
          /// <param name="path"></param>
          /// <param name="byteLoad"></param>
          /// <returns></returns>
          [MethodImpl(MethodImplOptions.NoInlining)]
          [Conditional("DEBUG")]
          Assembly? GetPdbAssembly(string file) {
              var dir = Path.GetDirectoryName(file);
              var pdbName = Path.GetFileNameWithoutExtension(file);
              var pdbFile = Path.Combine(dir, $"{pdbName}.pdb");
              if (!File.Exists(pdbFile)) return null;
              return Assembly.Load(File.ReadAllBytes(file), File.ReadAllBytes(pdbFile));
          }
      
          /// <summary>
          /// 遞歸獲取加載鏈
          /// </summary>
          /// <param name="cadAssembly">程序集運行解析</param>
          /// <param name="cadAssemblyRef">程序集反射解析</param>
          /// <param name="dllFile">dll文件位置</param>
          /// <param name="dllFileSet">dll文件集合返回用</param>
          /// <returns></returns>
          [MethodImpl(MethodImplOptions.NoInlining)]
          void GetDependencyLink(
              Dictionary<string, Assembly> cadAssembly,
              Dictionary<string, Assembly> cadAssemblyRef,
              string dllFile, HashSet<string> dllFileSet) {
              if (dllFile is null)
                  throw new ArgumentNullException(nameof(dllFile));
      
              if (!File.Exists(dllFile)) return;
              if (!dllFileSet.Add(dllFile)) return;
      
              // 路徑轉程序集名
              var assName = AssemblyName.GetAssemblyName(dllFile).FullName;
      
              Assembly assRef;
              // 運解和反解都沒有,
              // 就把dll加載到反解以及更新緩存,用來找依賴路徑,
              if (!cadAssembly.TryGetValue(assName, out assRef)) {
                  if (!cadAssemblyRef.TryGetValue(assName, out assRef)) {
                      assRef = TryOnlyLoad(dllFile);
                      if (assRef is not null) {
                          if (assName != assRef.FullName) throw new("理論上它們是一樣");
                          cadAssemblyRef[assRef.FullName] = assRef;
                      }
                  }
              }
              if (assRef is null) return;
      
              // 遞歸獲取依賴路徑
              // 拖拽加載dll的目錄,過濾得到為程序集名的dll.
              var dir = Path.GetDirectoryName(dllFile);
              var files = assRef.GetReferencedAssemblies()
                  .Select(ass => Path.Combine(dir, $"{ass.Name}.dll"))
                  .Where(file => !dllFileSet.Contains(file) && File.Exists(file))
                  .ToArray();
              foreach(var file in files) {
                  GetDependencyLink(cadAssembly, cadAssemblyRef, file, dllFileSet);
              }
          }
      
          /// <summary>
          /// 反射解析加載文件
          /// </summary>
          /// <param name="dllFile">dll文件位置</param>
          /// <returns>反射解析內的程序集</returns>
          [MethodImpl(MethodImplOptions.NoInlining)]
          Assembly? TryOnlyLoad(string dllFile) {
              try {
              var byteRef = File.ReadAllBytes(dllFile);
              // 若用名稱參數,沒有依賴會報錯,所以用字節參數.
              // 不能重復加載同一個dll,用hashset排除.
              if (!PatchExtensionLoader) {
                  return Assembly.ReflectionOnlyLoad(byteRef);
              }
      
      #if HarmonyPatch_1
              // QQ1548253108: 我這里會報錯,提供了解決方案.
              // 方案一: 在類上面加 [HarmonyPatch("Autodesk.AutoCAD.ApplicationServices.ExtensionLoader", "OnAssemblyLoad")]
              const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader";
              Harmony hm = new(ext);
              hm.PatchAll();
              result = Assembly.ReflectionOnlyLoad(byteRef);
              hm.UnpatchAll(ext);
      #endif
      
      #if HarmonyPatch_2
              // 方案二:跟cad耦合了
              const string ext = "Autodesk.AutoCAD.ApplicationServices.ExtensionLoader";
              var docAss = typeof(Autodesk.AutoCAD.ApplicationServices.Document).Assembly;
              var a = docAss.GetType(ext);
              var b = a.GetMethod("OnAssemblyLoad");
              Harmony hm = new(ext);
              hm.Patch(b, new HarmonyMethod(GetType(), "Dummy"));
              result = Assembly.ReflectionOnlyLoad(byteRef);
              hm.UnpatchAll(ext);
      #endif
              return result;
              } catch { return null; }
          }
      
          /// <summary>
          /// 加載信息
          /// </summary>
          public string PrintMessage() {
              var sb = new StringBuilder();
              foreach (var pair in LoadMap) {
                  sb.AppendLine($"++  {pair.Key}");
              }
              if (sb.Count > 0) 
                  sb.Insert(0, "**  這些文件加載成功:");
      
              int num = sb.Length;
              foreach (var pair in LoadMapError) {
                  sb.AppendLine($"--  {pair.Key},  錯誤消息:{pair.Value}");
              }
              if (sb.Length > num) 
                  sb.Insert(num, "**  這些文件已被加載過,同時重復名稱和版本號,跳過:");
              return sb.ToString();
          }
      }
      

      運行域事件

      最重要是這個事件,
      它會在運行的時候找已經載入內存上面的程序集.

      關于AppDomain.CurrentDomain.AssemblyResolve事件,
      它用于在程序集加載失敗時重新加載程序集,
      通常用于動態加載程序集的場景,
      你可以通過該事件自定義程序集的查找和加載邏輯,確保程序集能夠正確加載.

      0x01 動態加載要注意所有的引用外的dll的加載順序
      0x02 指定版本: Acad2008若沒有這個事件.
      1,動態編譯的命令執行時候無法引用當前的程序集函數,彈出一個報錯.
      2,動態編譯之后要加載程序域內,就能實現觸發默認檢索機制,不需要這個事件.
      所以這是兩種方案,事件進行動態檢索可以動態更換程序域順序,從而更換dll.
      0x03 目錄構成: 動態加載時,dll的地址會在系統的動態目錄里,而它所處的程序集(運行域)是在動態目錄里.
      0x04 命令構成: cad自帶的netload會把所處的運行域給改到cad自己的,而動態加載不通過netload,所以要自己去改.

      調用

      AppDomain.CurrentDomain.AssemblyResolve += AssemblyHelper.AssemblyResolve;
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += AssemblyHelper.ReflectionOnlyAssemblyResolve;
      

      封裝

      using System;
      using System.Diagnostics;
      using System.Reflection;
      using System.Runtime.CompilerServices;
      using System.Text;
      using System.IO;
      using System.Linq;
      using System.Configuration;
      
      namespace IFoxCAD.LoadEx;
      
      public class AssemblyHelper {
          // 僅反射加載時解析程序集,不觸發靜態構造函數等
          [MethodImpl(MethodImplOptions.NoInlining)]
          public static Assembly? ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs e)
          {
              var cadAss = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();
              return Resolve(cadAss, sender, e);
          }
      
          // 常規程序集解析失敗處理
          [MethodImpl(MethodImplOptions.NoInlining)]
          public static Assembly? AssemblyResolve(object sender, ResolveEventArgs e)
          {
              var cadAss = AppDomain.CurrentDomain.GetAssemblies();
              return Resolve(cadAss, sender, e);
          }
      
          [MethodImpl(MethodImplOptions.NoInlining)]
          public static Assembly? Resolve(Assembly[] cadAss, object sender, ResolveEventArgs e)
          {
              // 精確匹配全名(名稱/版本/公鑰等)
              var exactMatch = cadAss.FirstOrDefault(ass => e.Name == ass.FullName);
              if (exactMatch != null) return exactMatch;
      
              // 解析請求的程序集基本信息
              var requestedName = new AssemblyName(e.Name);
      
              // 模糊匹配: 名稱相同且版本最高的程序集
              var highestVersionMatch = cadAss
                  .Where(ass => ass.GetName().Name == requestedName.Name)
                  .OrderByDescending(ass => ass.GetName().Version)
                  .FirstOrDefault();
      
              if (highestVersionMatch != null) return highestVersionMatch;
      
              // 嘗試在文件系統中查找程序集
              string? assemblyPath = FindAssemblyPath(e.Name);
              if (!string.IsNullOrEmpty(assemblyPath) && File.Exists(assemblyPath))
                  return Assembly.LoadFrom(assemblyPath);
      
              // 處理資源程序集找不到的情況(參考:https://stackoverflow.com/a/43818961)
              if (requestedName.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)
                  && requestedName.CultureInfo?.IsNeutralCulture == false)
                  return null;
      
              // 記錄詳細的錯誤信息
              var errorLog = new StringBuilder();
              errorLog.AppendLine($"程序集解析失敗 [{DateTime.Now:yyyy-MM-dd HH:mm:ss}]");
              errorLog.AppendLine($"請求的程序集全名: {e.Name}");
              errorLog.AppendLine($"已加載的程序集列表({cadAss.Length}個):");
              foreach (var ass in cadAss.OrderBy(a => a.FullName))
                  errorLog.AppendLine($"  - {ass.FullName}");
      
              Trace.TraceError(errorLog.ToString());
      
              // 調試時中斷,發布環境建議記錄日志后終止
              Debugger.Break();
              return null;
          }
      
      
          private static string? FindAssemblyPath(string assemblyFullName) {
              var requestedName = new AssemblyName(assemblyFullName);
              string[] searchPaths = {
                  AppDomain.CurrentDomain.BaseDirectory,
                  Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"),
                  ConfigurationManager.AppSettings["CustomAssemblyPath"],
                  @"C:\Program Files\IFoxCAD\Assemblies"
              };
      
              // 查找程序集路徑
              var foundPath = searchPaths
                  .Where(Directory.Exists) // 過濾存在的目錄
                  .Select(dir => Path.Combine(dir, $"{requestedName.Name}.dll")) // 生成目標路徑
                  .FirstOrDefault(File.Exists); // 返回第一個存在的文件路徑
      
              // 如果找到路徑,直接返回
              if (foundPath != null) return foundPath;
      
              // WPF 程序集特殊處理(調試時使用本地版本)
              if (Debugger.IsAttached && requestedName.Name.StartsWith("PresentationCore")) {
                  string wpfLocation = Path.Combine(
                      Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                      @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8",
                      $"{requestedName.Name}.dll");
      
                  if (File.Exists(wpfLocation)) return wpfLocation;
              }
      
              // 如果都找不到
              return null;
          }
      
      
          // 從嵌入資源加載程序集(適用于單文件發布)
          private static Assembly? OnResolveAssembly(object sender, ResolveEventArgs args)
          {
              var resourceName = $"{new AssemblyName(args.Name).Name}.dll";
      
              // 精確匹配資源名稱(考慮命名空間路徑)
              var fullResourceName = Assembly.GetExecutingAssembly()
                  .GetManifestResourceNames()
                  .FirstOrDefault(name => name.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase));
      
              if (fullResourceName is null) return null;
      
              using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(fullResourceName);
              if (stream is null) return null;
      
              byte[] buffer = new byte[stream.Length];
              stream.Read(buffer, 0, buffer.Length);
              return Assembly.Load(buffer);
          }
      }
      
              // Acad21+vs22 容易觸發這個資源的問題
              // https://stackoverflow.com/questions/4368201
              // if (an.Name.EndsWith(".resources") && !an.CultureName.EndsWith("neutral"))
              //     return null;
              // Process.GetCurrentProcess().Kill();
      

      動態編譯

      using System;
      using System.CodeDom.Compiler;
      using Microsoft.CSharp;
      
      public class DynamicCompiler {
          public static void Main() {
              // 動態編譯代碼
              string code = @"
                  using System;
                  public class HelloWorld {
                      public static void SayHello() {
                          Console.WriteLine(""Hello, World!"");
                      }
                  }";
      
              var provider = new CSharpCodeProvider();
              var compilerParams = new CompilerParameters
              {
                  GenerateInMemory = true, // 在內存中生成程序集
                  ReferencedAssemblies = { "System.dll" } // 添加引用
              };
      
              CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, code);
      
              if (results.Errors.HasErrors)
              {
                  foreach (var error in results.Errors)
                      Console.WriteLine(error.ToString());
                  return;
              }
      
              // 顯式加載生成的程序集,就不用使用事件了.
              var compiledAssembly = results.CompiledAssembly;
              AppDomain.CurrentDomain.Load(compiledAssembly.GetName());
      
              // 調用動態編譯的方法
              var type = compiledAssembly.GetType("HelloWorld");
              var method = type.GetMethod("SayHello");
              method.Invoke(null, null);
          }
      }
      

      新程序域并執行代碼

      // 可跨應用程序域調用的類
      public class RemoteObject : MarshalByRefObject {
          public void SayHello() {
              Console.WriteLine("Hello from " + AppDomain.CurrentDomain.FriendlyName);
          }
      }
      
      class Program {
          static void Main() {
              // 創建新的應用程序域
              AppDomain newDomain = AppDomain.CreateDomain("NewDomain");
      
              // 在新的應用程序域中執行代碼
              newDomain.DoCallBack(() => {
                  Console.WriteLine("Executing in new AppDomain: " + AppDomain.CurrentDomain.FriendlyName);
              });
      
              // 在新的應用程序域中創建對象
              RemoteObject obj = (RemoteObject)newDomain.CreateInstanceAndUnwrap(
                  typeof(RemoteObject).Assembly.FullName,
                  typeof(RemoteObject).FullName);
      
              // 調用方法
              obj.SayHello();
      
              // 卸載應用程序域
              AppDomain.Unload(newDomain);
          }
      }
      

      新域內切換上下文:
      這本質上還是跨域傳遞,需要序列化特性才行.

      using System;
      using System.Runtime.Remoting.Messaging;
      
      class Program {
          static void Main() {
              // 設置上下文數據
              CallContext.LogicalSetData("MyKey", "Hello from Main Context!");
      
              // 獲取上下文數據
              var data = CallContext.LogicalGetData("MyKey");
              Console.WriteLine("Main Context Data: " + data);
      
              // 創建一個新的上下文
              using (new MyContext()) {
                  // 在新上下文中設置數據
                  CallContext.LogicalSetData("MyKey", "Hello from New Context!");
      
                  // 獲取新上下文中的數據
                  var newData = CallContext.LogicalGetData("MyKey");
                  Console.WriteLine("New Context Data: " + newData);
              }
      
              // 回到主上下文后,檢查數據
              var originalData = CallContext.LogicalGetData("MyKey");
              Console.WriteLine("Back to Main Context Data: " + originalData);
          }
      }
      
      // 自定義上下文類
      public class MyContext : IDisposable {
          private readonly object _oldContext;
          public MyContext() {
              // 保存當前上下文
              _oldContext = CallContext.LogicalGetData("MyKey");
              // 設置新的上下文數據
              CallContext.LogicalSetData("MyKey", "Initialized in New Context");
              Console.WriteLine("New Context Created.");
          }
          public void Dispose() {
              // 恢復原來的上下文數據
              CallContext.LogicalSetData("MyKey", _oldContext);
              Console.WriteLine("Context Restored.");
          }
      }
      

      創建程序域兩個函數是不同的
      CreateInstance
      CreateInstanceAndUnwrap

      using System;
      using System.IO;
      using System.Reflection;
      
      class Program {
          static void Main() {
              // 讀取DLL到字節數組
              byte[] dllBytes = File.ReadAllBytes("YourDll.dll"); // 替換為實際路徑
      
              // ----------- 主程序域加載 -----------
              Console.WriteLine("【主程序域加載】");
              Assembly mainAssembly = Assembly.Load(dllBytes); // 字節流加載[1](@ref)
              Type mainType = mainAssembly.GetType("Namespace.ClassName"); // 替換為實際類名
              dynamic mainInstance = Activator.CreateInstance(mainType);
              mainInstance.YourMethod(); // 調用目標方法
      
              // ----------- 新域加載 -----------
              Console.WriteLine("\n【新域加載】");
              AppDomain newDomain = AppDomain.CreateDomain("NewDomain");
              try
              {
                  // 使用Loader類實現跨域字節流加載
                  ObjectHandle loaderHandle = newDomain.CreateInstance(
                      typeof(CrossDomainLoader).Assembly.FullName,
                      typeof(CrossDomainLoader).FullName
                  );
                  
                  CrossDomainLoader loader = (CrossDomainLoader)loaderHandle.Unwrap();
                  loader.LoadAndExecute(dllBytes, "Namespace.ClassName", "YourMethod");
              }
              finally
              {
                  AppDomain.Unload(newDomain); // 卸載新域[2](@ref)
              }
          }
      }
      
      // 跨域加載輔助類(需繼承MarshalByRefObject)
      public class CrossDomainLoader : MarshalByRefObject
      {
          public void LoadAndExecute(byte[] dllBytes, string className, string methodName)
          {
              Assembly assembly = Assembly.Load(dllBytes); // 在新域中加載[3](@ref)
              Type type = assembly.GetType(className);
              dynamic instance = Activator.CreateInstance(type);
              instance.GetType().GetMethod(methodName).Invoke(instance, null);
          }
      }
      

      調試

      另見 cad.net dll動態加載之后如何調試

      卸載DLL

      項目結構

      1. cad主插件工程,引用-->通訊類工程
      2. 通訊類工程(繼承MarshalByRefObject接口的)
      3. 其他需要加載的子插件工程:cad子插件項目作為你測試加載的dll,
        里面有一個cad命令gggg,它將會用來驗證我們是否成功卸載.

      和以往的工程都不一樣的是,
      我們需要復制一份acad.exe目錄的所有文件到一個非C盤目錄,
      如下:

      修改 主工程,屬性頁:

      生成,輸出: G:\AutoCAD 2008\ <--由于我的工程是.net standard,所以這里將會生成各類net文件夾

      調試,可執行文件: G:\AutoCAD 2008\net35\acad.exe <--在net文件夾放acad.exe目錄所有文件

      為什么?因為通訊類.dll必須要在acad.exe旁邊,否則無法通訊,
      會報錯,似乎是權限問題,至于有沒有其他方法,我不知道.

      通訊結構圖

      graph TD cad主插件工程-->主域-->新域-->創建域-->創建通訊類-->通訊類 通訊類-->執行鏈式加載子插件工程-->新域程序集 通訊類-->新域程序集 新域-->卸載域-->破壞通訊類-->通訊類 主域-->跨域執行方法-->檢查通訊類生命-->通訊類

      當時我搞卸載的時候怎么沒看到下面鏈接,
      新版net出來微軟就弄了?拒絕內聯之類的,我net3.5怎么阻止.
      卸載的鏈接參考

      acad卸載程序域是成功的,
      但是存在COM的GCHandle問題:
      在新域上面創建通訊類--卸載成功,不過關閉cad程序的時候彈出了報錯:
      System.ArgumentException:“無法跨 AppDomain 傳遞 GCHandle。”

      我查詢這個錯誤,大部分是講COM的,而我工程上面剛好使用了COM,
      所以最好就是檢測是不是有靜態字段持有COM,最好每次使用就執行釋放,不要靜態持有.
      甚至懷疑是cad的Acap的文檔集合本身就會創建COM導致異常...

      你可以做做實驗,實現一個沒引用cad.dll的dll,
      我用了winform,是可以卸載,且不會報GC錯誤.

      舊工程:
      Loadx分支呈現舊代碼

      最新工程:
      http://www.rzrgm.cn/JJBox/p/18800009

      (完)

      posted @ 2020-10-18 03:58  驚驚  閱讀(10206)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 亚洲一区在线成人av| 欧美肥老太牲交大战| 国产无遮挡吃胸膜奶免费看| 亚洲av无码精品色午夜蛋壳| 日本边添边摸边做边爱| 成人免费无码大片A毛片抽搐色欲 成人啪精品视频网站午夜 | 日本道精品一区二区三区| 欧洲码亚洲码的区别入口| 18禁无遮挡啪啪无码网站| 成人午夜看黄在线尤物成人| 天天狠天天透天天伊人| 亚洲高清WWW色好看美女| 潮喷失禁大喷水av无码| 国产激情电影综合在线看| 开心一区二区三区激情| 国产av一区二区午夜福利| 精品一二三四区在线观看| 欧美 亚洲 日韩 在线综合| 亚洲av日韩av永久无码电影| 亚洲乱码一区二区三区视色| 亚洲人妻系列中文字幕| 99久久久无码国产精品免费 | 国产一级黄色片在线播放| 铁岭市| 久久99日韩国产精品久久99| 一亚洲一区二区中文字幕 | 亚洲各类熟女们中文字幕| 国产欧美国日产高清| 国产精品成熟老女人| V一区无码内射国产| 国产精品七七在线播放| 色偷偷www.8888在线观看| 国产99久久精品一区二区| 国产高清在线不卡一区| 国产亚洲精品AA片在线爽| 国内自拍第一区二区三区| 久久精品亚洲精品国产色婷| 在线日韩日本国产亚洲| 欧美老熟妇乱子伦牲交视频| 国产成人高清亚洲综合| 亚欧乱色精品免费观看|