亚洲精品亚洲人成在线观看麻豆,在线欧美视频一区,亚洲国产精品一区二区动图,色综合久久丁香婷婷

              當(dāng)前位置:首頁 > IT技術(shù) > Windows編程 > 正文

              WPF 通過進(jìn)程實現(xiàn)異常隔離的客戶端
              2021-09-17 19:51:09

              當(dāng) WPF 客戶端需要實現(xiàn)插件系統(tǒng)的時候,一般可以基于容器或者進(jìn)程來實現(xiàn)。如果需要對外部插件實現(xiàn)異常隔離,那么只能使用子進(jìn)程來加載插件,這樣插件如果拋出異常,也不會影響到主進(jìn)程。WPF 元素?zé)o法跨進(jìn)程傳輸,但是窗口句柄(HWND)可以,所以可以將 WPF 元素包裝成 HWND,然后通過進(jìn)程間通信將插件傳輸?shù)娇蛻舳酥?,從而實現(xiàn)插件加載。

              1. 使用 HwndSource 將 WPF 嵌入到 Win32 窗口

              HwndSource 會生成一個可以嵌入 WPF 的 Win32 窗口,使用 HwndSource.RootVisual 添加一個 WPF 元素。

              private static IntPtr ViewToHwnd(FrameworkElement element)
              {
                  var p = new HwndSourceParameters()
                  {
                      ParentWindow = new IntPtr(-3), // message only
                      WindowStyle = 1073741824
                  };
                  var hwndSource= new HwndSource(p)
                  {
                      RootVisual = element,
                      SizeToContent = SizeToContent.Manual,
                  };
                  hwndSource.CompositionTarget.BackgroundColor = Colors.White;
                  return hwndSource.Handle;
              }
              

              2. 使用 HwndHost 將 Win32 窗口轉(zhuǎn)換成 WPF 元素

              Win32 窗口是無法直接嵌入到 WPF 頁面中的,所以 .Net 提供了一個 HwndHost 類來轉(zhuǎn)換。 HwndHost 是一個抽象類,通過實現(xiàn) BuildWindowCore 方法,可以將一個 Win32 窗口轉(zhuǎn)換成 WPF 元素。

              class ViewHost : HwndHost
              {
                  private readonly IntPtr _handle;
              
                  [DllImport("user32.dll", CharSet = CharSet.Auto)]
                  public static extern IntPtr SetParent(HandleRef hWnd, HandleRef hWndParent);
              
                  public ViewHost(IntPtr handle) => _handle = handle;
              
                  protected override HandleRef BuildWindowCore(HandleRef hwndParent)
                  {
                      SetParent(new HandleRef(null, _handle), hwndParent);
                      return new HandleRef(this, _handle);
                  }
              
                  protected override void DestroyWindowCore(HandleRef hwnd)
                  {
                  }
              }
              

              3. 約定插件的入口方法

              可以通過多種方式返回插件的界面。我這里約定每個插件的 dll 都有一個 PluginStartup 類,PluginStartup.CreateView() 可以返回插件的界面。

              namespace Plugin1
              {
                  public class PluginStartup
                  {
                      public FrameworkElement CreateView() => new UserControl1();
                  }
              }
              

              4. 啟動插件進(jìn)程,使用匿名管道實現(xiàn)進(jìn)程間通信

              進(jìn)程間通信有多種方式,需要功能齊全可以使用 grpc,簡單的使用管道就好了。

              • 客戶端通過指定插件 dll 地址來加載插件。加載插件的時候,啟動一個子進(jìn)程,并且通過管道通信,傳輸包裝插件的 Win32 窗口句柄。
              private FrameworkElement LoadPlugin(string pluginDll)
              {
                  using (var pipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable))
                  {
                      var startInfo = new ProcessStartInfo()
                      {
                          FileName = "PluginProcess.exe",
                          UseShellExecute = false,
                          CreateNoWindow = true,
                          Arguments = $"{pluginDll} {pipeServer.GetClientHandleAsString()}"
                      };
              
                      var process = new Process { StartInfo = startInfo };
                      process.Start();
                      _pluginProcessList.Add(process);
                      pipeServer.DisposeLocalCopyOfClientHandle();
                      using (var reader = new StreamReader(pipeServer))
                      {
                          var handle = new IntPtr(int.Parse(reader.ReadLine()));
                          return new ViewHost(handle);
                      }
                  }
              }
              
              • 通過控制臺程序裝載插件 dll 并將插件界面轉(zhuǎn)換成 Win32 窗口,然后通過管道傳輸句柄。
              [STAThread]
              [LoaderOptimization(LoaderOptimization.MultiDomain)]
              static void Main(string[] args)
              {
                  if (args.Length != 2) return
                  var dllPath = args[0];
                  var serverHandle = args[1];
                  var dll = Assembly.LoadFile(dllPath);
                  var startupType = dll.GetType($"{dll.GetName().Name}.PluginStartup");
                  var startup = Activator.CreateInstance(startupType);
                  var view =(FrameworkElement)  startupType.GetMethod("CreateView").Invoke(startup, null);
                
                  using (var pipeCline = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle))
                  {
                      using (var writer = new StreamWriter(pipeCline))
                      {
                          writer.AutoFlush = true;
                          var handle = ViewToHwnd(view);
                          writer.WriteLine(handle.ToInt32());
                      }
                  }
                  Dispatcher.Run();
              }
              

              5 效果

              效果

              參考資料和備注

              本文摘自 :https://www.cnblogs.com/

              開通會員,享受整站包年服務(wù)立即開通 >