-
Notifications
You must be signed in to change notification settings - Fork 0
MS_ManagedUnmanagedBridge
-
先ずは「マーシャリング」を参照して下さい。
-
.NET Standard、.NET Coreでの
サポート状況について- P/Invoke(
DllImport)はサポートされている模様。 - しかし、COMは、現時点では、
互換機能パック(Windows Desktop Packsへの移行)でも
サポートされていない模様。
- P/Invoke(
移行メモ(最新化:COM は後にサポートされた): 「COM は
サポートされていない模様」は .NET Core 2.x 時点の記述であり、
現在は状況が変わっている。
現状 P/Invoke 全プラットフォームでサポート(Windows / Linux / macOS) COM 相互運用(.NET → COM) .NET Core 3.0 以降でサポート(Windows 限定) COM 公開(.NET → COM サーバー) .NET 5 以降でサポート( ComVisible+EnableComHosting)C++/CLI .NET Core 3.1 以降でサポート(Windows 限定) ただし、いずれも Windows 専用機能であり、
.NETのクロスプラットフォーム対応の観点では
「Linux では使えない」という制約は変わっていない。
TFM もnet8.0-windowsが必要になる。
以下の 2 つの方法があります。
| 対象 | 方法 |
|---|---|
| DLL |
P/Invoke([DllImport])で関数を宣言して呼ぶ |
| COM |
RCW(Runtime Callable Wrapper)を生成して参照する(tlbimp.exe / VS の参照追加) |
以下の 2 つの方法があります。
| 対象 | 方法 |
|---|---|
| DLL インターフェイス | .NET のアンマネージド拡張(C++/CLI)でブリッジ DLL を作り、DLL/COMユーザに公開する |
| COMインターフェイス | .NET アセンブリに ComVisible を付け、CCW(COM Callable Wrapper)として公開する |
補足(4 象限で整理する): 上記 4 つを 1 表にすると全体像が掴みやすい。
→ アンマネージド → マネージド マネージドから呼ぶ P/Invoke(DLL)
RCW(COM)- アンマネージドから呼ぶ - C++/CLI ブリッジ(DLL)
CCW(COM)「どちらが呼ぶ側か」と「DLL か COM か」の 2 軸で決まる。
上記の逆(VC、VB6(unmanage)→ .NET(manage))を行う相互運用。
-
Win32 DLL と C++/CLI のブリッジ DLL を使用して、
アンマネージコードからマネージコードを呼び出す。
- P/Invoke(相互運用マーシャラー)に標準実装されている模様。
- 恐らく、自動的にマーシャリングされるのだと思います。
- 行きはマーシャリング(デリゲートからポインタ)
- 帰りもマーシャリング(多分 C にラッパのポインタが渡る)
補足(デリゲートの寿命に注意): この方向(.NET のデリゲートを
関数ポインタとしてネイティブに渡す)は動作するが、
最も事故が多い箇所でもある。
- マーシャラーはデリゲートに対する**ネイティブのサンク(thunk)**を生成する。
- しかし、デリゲート本体を GC が回収するとサンクも無効になる。
- ネイティブ側があとから呼ぶと
AccessViolationExceptionで落ちる。// NG: 呼び出しから抜けた瞬間、GC の対象になる NativeApi.Register(new MyCallback(OnEvent)); // OK: フィールドで保持して寿命を延ばす private static MyCallback _cb = OnEvent; NativeApi.Register(_cb);「デバッグ ビルドでは動くが、本番でしばらく動かすと落ちる」という
典型的な症状は、ほぼこれである。
- 上記の逆
- マーシャリングの動作のイメージができていないと、
正しい処理を実装できない可能性があります。
- C++ の EXE から C#のメソッドを呼び出します。
- このときの引数で C++ 側のコールバック関数のアドレスを渡します。
- このあと、外的要因で C# の DLL から上記のコールバックを
呼び出そうとしていますが、コールバック呼び出しで例外エラーとなります。
- C++ から C# は直接呼べないので、アンマネージド拡張(C++/CLI)で
DLL のエクスポート関数経由で .NET に接続する必要があると思います。 - また、.NET に C の関数ポインタを直接渡しても .NET からの呼び出しは
できないので、C++/CLI 経由にする必要があると思います。
そこで思いついたのは、
- C++/CLI 内部でコールバックするメソッドを持ったオブジェクトを作成し、
C# のメソッド経由でそのオブジェクトを渡す。
C# からこのオブジェクトのメソッドを経由して C++ にコールバックする。 - C++/CLI で C# から呼ぶための DLL エクスポート関数を作る。
このエクスポート関数に C の関数ポインタを渡してコールバックする。
という 2 つの方法です。
上記の 1. で解決しました。
補足(現在なら別解もある): 「.NET に C の関数ポインタを直接渡しても
呼び出せない」という当時の前提は、現在では緩和されている。// .NET 5 以降:関数ポインタを直接扱える delegate* unmanaged<int, int> fp = (delegate* unmanaged<int, int>)ptr; int r = fp(42);C# 9 の 関数ポインタ(
delegate*) と
[UnmanagedCallersOnly]属性により、
C++/CLI を挟まずに双方向のコールバックを実装できるようになった。
従来 現在(.NET 5 以降) ネイティブ → .NET C++/CLI ブリッジが必要 [UnmanagedCallersOnly]で直接エクスポート可.NET → ネイティブの関数ポインタ Marshal.GetDelegateForFunctionPointerdelegate*(unsafe だが高速)ただし
unsafeが必要で、GC やスレッドの扱いを誤ると
容易にプロセスごと落ちる。C++/CLI 経由の方が安全である点は変わらない。
- アンマネージドコードのクロスプラットフォーム化
- マネージドコード・インターフェイスを Linux アンマネージドコードに公開
(DNET_DotNetCoreOnLinux.md)- 現時点では、できない模様。.NET Core の C++/CLI が Linux で
サポートされていない。 - JCOBridge と言う謎のソフトウェアもあるようです。https://www.jcobridge.com/
- 現時点では、できない模様。.NET Core の C++/CLI が Linux で
補足(Linux 側の現在の解): C++/CLI が Linux 非対応であることは
現在も変わらない。Linux でネイティブとの相互運用が必要な場合は、
次のいずれかを使う。
方向 手段 .NET → ネイティブ P/Invoke( .soをDllImport)。Linux でも動作するネイティブ → .NET [UnmanagedCallersOnly]+NativeAOT/nethostでエクスポート関数を公開後者は、.NET のクラス ライブラリを
NativeAOTで
共有ライブラリ(.so/.dll)としてビルドし、
C/C++ から普通の関数として呼ぶ、という方式である。
C++/CLI の代替として、現在はこれが標準的な答えになっている。
- CLR Inside Out: Marshaling between Managed and Unmanaged Code
https://learn.microsoft.com/archive/msdn-magazine/2008/january/clr-inside-out-marshaling-between-managed-and-unmanaged-code
-
ネイティブ相互運用性 - .NET | Microsoft Learn
https://learn.microsoft.com/dotnet/standard/native-interop/ -
プラットフォーム呼び出し (P/Invoke)
https://learn.microsoft.com/dotnet/standard/native-interop/pinvoke -
.NET コンポーネントを COM に公開する
https://learn.microsoft.com/dotnet/core/native-interop/expose-components-to-com
- .NET TIPS Win32 API や DLL 関数を呼び出すには?
http://www.atmarkit.co.jp/fdotnet/dotnettips/024w32api/w32api.html
- P/Invoke - Wikipedia
https://ja.wikipedia.org/wiki/P/Invoke - pinvoke.net the interop wiki!
http://www.pinvoke.net/
Tags: 移行, Windows, プログラミング, .NET開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。