WPF:マルチプロセスによる例外分離クライアント

WPF:マルチプロセスによる例外分離クライアント

WPFクライアントがプラグインシステムを実装する必要がある場合、通常はコンテナまたはプロセスに基づいて実装することができます。外部プラグインに対して例外分離を実装する必要がある場合、プラグインをロードするために子プロセスのみを使用し、プラグインが例外を投げてもメインプロセスに影響しないようにします。

最后更新 2021/09/22 22:10
鹅群中的鸭霸
预计阅读 3 分钟
分类
WPF
标签
.NET WPF プラグインシステム モジュラー性 Win32

WPFクライアントがプラグインシステムを実装する必要がある場合、通常はコンテナまたはプロセスに基づいて実装することができます。外部プラグインに対して例外分離を実装する必要がある場合、プラグインのロードには子プロセスのみを使用し、プラグインが例外を投げてもメインプロセスに影響しません。WPF要素はプロセス間で転送できないが、ウィンドウハンドルHWNDは転送できるので、WPF要素をHWNDにラップしてから、プロセス間通信でプラグインをクライアントに転送することで、プラグインのロードを実現することができる。

1. HwndSourceを使ってWin 32ウィンドウにWPFを埋め込む

HwndSourceはWPFを埋め込むWin 32ウィンドウを生成し、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を使用してWin 32ウィンドウをWPF要素に変換する

Win 32ウィンドウはWPFページに直接埋め込むことはできないため、. Netは変換するHwndHostクラスを提供している。HwndHostは、BuildWindowCoreメソッドを実装することでWin 32ウィンドウを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. 匿名パイプを使用してプロセス間通信を実現するプラグインプロセスの起動

プロセス間通信には様々な方法があり、GRPCを使用するための完全な機能が必要であり、パイプを使用するのは簡単です。

  • クライアントは、プラグインdllアドレスを指定してプラグインをロードします。プラグインがロードされると、子プロセスが開始され、パイプ通信を介してプラグインをラップするWin 32ウィンドウハンドルが送信されます。
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をロードし、プラグインインターフェイスをWin 32ウィンドウに変換し、ハンドルをパイプで転送します。
[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つの効果

参考文献とコメント

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2025/09/13

WPFからAvaloniaへの移行シリーズ:WPFプログラムをAvaloniaに移行する必要がある理由

ここ数年、当社のホストソフトウェアは主にWPFとWin Formで開発されてきました。これらのテクノロジーはWindowsプラットフォームで非常にうまく機能し、小規模なパイロット生産から今日の大規模なデリバリまでの段階を経てきました。しかし、ビジネスの成長と顧客のニーズの変化に伴い、単一のWindowsテクノロジースタックは私たちが乗り越えなければならないハードルになりました。

继续阅读
同分类 / 同标签 2025/01/26

WPFはカスタムXMLファイルで国際化を実現

この記事では、必要なNuGetパッケージのインストール、言語リストの動的取得、言語の動的切り替え、コードとxamlインターフェイスでの翻訳文字列の使用、開発者がWPFアプリケーションを簡単に国際化できるようにするソースコードへのリンクなど、WPFプログラムでカスタムXMLファイルを使用した国際化の方法について詳しく説明します。

继续阅读