【#CSharp #VCPP】P/Invokeの沼にハマってたぁ~ ガベージがいたずらしまくる(汗 (C#の部)
関連記事
- 【#CSHARP】次の案件のスケルトン書いてgitサーバに登録するなどと 【2023年05月16日(火)】
- 生成AI「仕事で利用」2割 正確性などに懸念(23/5/7) 【2023年05月07日(日)】
- 【#RaspberryPI】ラズパイでsudo起動の.Net6アプリがデバグできない件 【2023年04月22日(土)】
- 【#PostgreSQL】テーブルから取得したレコードセットをCSVへ落とそうとすると,空ファイルが出来る...ドロドロドロ 【2023年03月08日(水)】
- 【#git】TortiseGit ver2.14のGitWCRev.exeを動かすとErrorCD:10を出力して,動かない 【2023年02月08日(水)】
ほいでもって,C#側の実装
構造体&C++呼び出しデリゲートでのP/Invoke = マーシャリングの実装方法がワケワカラン
悩みに悩みまくったべ
そのお答えは↓
まずは,DLL-APIへのインターフェースを定義しましょう
DLLからコールバックで帰ってくる,構造体,デリゲートの定義,DLL-APIの実装
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 8)] public struct TFCALLBACKINFOAPI { public IntPtr ImageBuf; public uint ImageBufLen; public int ImageWidth; public int ImageHeight; public int ImageStride; public int DetectionNum; public IntPtr DetectionX; //int[] public IntPtr DetectionY; //int[] public IntPtr DetectionWidth; //int[] public IntPtr DetectionHeight; //int[] public IntPtr DetectionClassID; //int[] public IntPtr DetectionScore; //float[] public IntPtr DetectionTagName; //LPWSTR[] } //デリゲート型の定義 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int CallbackDelegate_TFCapture([Out] TFCALLBACKINFOAPI argSource, [Out] TFCALLBACKINFOAPI argDest); //デリゲートエントリのポインタを放り込む DLL-API [DllImport("HOGEHOGE.dll")] private static extern long APISetCallbackFuncTFCapture([MarshalAs(UnmanagedType.FunctionPtr)] CallbackDelegate_TFCapture tmpFunc); private CallbackDelegate_TFCapture? m_CalbackFuncBackup; //この変数,メチャクチャ大事!無いとアプリが落ちる!!TFCALLBACKINFOAPIのDetection~な配列(ちゅうかC++側はnewしたポインタが格納されてくる)
をどうやって,マネージメモリに変換すべ?
マーシャリングが自動で良きに計らってくれないの?
良きに計らわない,らしい...ドロドロドロ
手動マーシャリングを実装した
//物体検出情報の子クラス public class ClassTFCallbackInfoDetection { public int DetectionX; public int DetectionY; public int DetectionWidth; public int DetectionHeight; public int DetectionClassID; public float DetectionScore; public string? DetectionTagName; public ClassTFCallbackInfoDetection( int tmpDetectionX, int tmpDetectionY, int tmpDetectionWidth, int tmpDetectionHeight, int tmpDetectionClassID, float tmpDetectionScore, string? tmpDetectionTagName ) { DetectionX = tmpDetectionX; DetectionY = tmpDetectionY; DetectionWidth = tmpDetectionWidth; DetectionHeight = tmpDetectionHeight; DetectionClassID = tmpDetectionClassID; DetectionScore = tmpDetectionScore; DetectionTagName = tmpDetectionTagName; } } //TFCALLBACKINFOAPI構造体の情報をマネージメモリ化格納し管理するクラス public class ClassTFCallbackInfo { public IntPtr ImageBuf; public uint ImageBufLen; public int ImageWidth; public int ImageHeight; public int ImageStride; public List<ClassTFCallbackInfoDetection> Detection; public ClassTFCallbackInfo(TFCALLBACKINFOAPI tmpInfo) { Detection = new List<ClassTFCallbackInfoDetection>(); ImageBuf = IntPtr.Zero; ImageBufLen = tmpInfo.ImageBufLen; ImageWidth = tmpInfo.ImageWidth;; ImageHeight = tmpInfo.ImageHeight; ImageStride = tmpInfo.ImageStride; int tmpDetectionNum = tmpInfo.DetectionNum; int[] tmpDetectionX = new int [tmpDetectionNum]; int[] tmpDetectionY = new int [tmpDetectionNum]; int[] tmpDetectionWidth = new int [tmpDetectionNum]; int[] tmpDetectionHeight = new int [tmpDetectionNum]; int[] tmpDetectionClassID = new int [tmpDetectionNum]; float[] tmpDetectionScore = new float [tmpDetectionNum]; IntPtr[] tmpDetectionTagNameAry = new IntPtr[tmpDetectionNum]; string?[] tmpDetectionTagName = new string[tmpDetectionNum]; Marshal.Copy(tmpInfo.DetectionX, tmpDetectionX, 0, tmpDetectionNum); //int float系の配列はそのままcopy Marshal.Copy(tmpInfo.DetectionY, tmpDetectionY, 0, tmpDetectionNum); Marshal.Copy(tmpInfo.DetectionWidth, tmpDetectionWidth, 0, tmpDetectionNum); Marshal.Copy(tmpInfo.DetectionHeight, tmpDetectionHeight, 0, tmpDetectionNum); Marshal.Copy(tmpInfo.DetectionClassID, tmpDetectionClassID, 0, tmpDetectionNum); Marshal.Copy(tmpInfo.DetectionScore, tmpDetectionScore, 0, tmpDetectionNum); Marshal.Copy(tmpInfo.DetectionTagName, tmpDetectionTagNameAry, 0, tmpDetectionNum); //LPWSTR配列は,いったんIntPtr配列として受け取り for(int i = 0; i < tmpDetectionNum; i++) //IntPtr配列をstring配列に変換 { string? st = Marshal.PtrToStringUni(tmpDetectionTagNameAry[i]); tmpDetectionTagName[i] = st; } for (int i = 0; i < tmpDetectionNum; i++) //マネージメモリの子クラスとして最終的に格納 { ClassTFCallbackInfoDetection tmpItem = new ClassTFCallbackInfoDetection( tmpDetectionX[i], tmpDetectionY[i], tmpDetectionWidth[i], tmpDetectionHeight[i], tmpDetectionClassID[i], tmpDetectionScore[i], tmpDetectionTagName[i] ); Detection.Add(tmpItem); } } }あとは,DLL-APIへのデリゲート情報流し込みやら
デリゲートの定義やら
//--------------------------------------------- //TFCAPTUREイベント(UIへ送信するデリゲート) // //--------------------------------------------- public class TFCaptureEventArgs : EventArgs { public ClassTFCallbackInfo Source; public ClassTFCallbackInfo Dest; public TFCaptureEventArgs(TFCALLBACKINFOAPI tmpSource, TFCALLBACKINFOAPI tmpDest) { Source = new ClassTFCallbackInfo(tmpSource); Dest = new ClassTFCallbackInfo(tmpDest); } } public delegate void OnTFCaptureEventHandler(object sender, TFCaptureEventArgs e); public event OnTFCaptureEventHandler OnTFCaptureEvent = delegate { }; //--------------------------------------------- //コンストラクタ // //--------------------------------------------- public HOGEHOGEDLLCtrl() { m_ActiveFL = true; ErrorCD = APICreate(); //DLL初期化API if (ErrorCD < 0) { return; } m_CalbackFuncBackup = CallBackFuncTFCapture; //DLL-APIへ渡す自デリゲートを必ず変数に代入して保存しておくこと!! ErrorCD = APISetCallbackFuncTFCapture(m_CalbackFuncBackup); //デリゲート登録API } //--------------------------------------------- //DLLから飛んでくるデリゲート // //--------------------------------------------- private int CallBackFuncTFCapture(TFCALLBACKINFOAPI tmpSource, TFCALLBACKINFOAPI tmpDest) { if (m_ActiveFL == false) { return 1; } OnTFCaptureEvent(this, new TFCaptureEventArgs(tmpSource, tmpDest)); //このままじゃUIで使いにくいので一回マネージ側デリゲートを挟んでUIへ伝達 return 1; }
m_CalbackFuncBackup = CallBackFuncTFCapture;
の一文が大事
動き出して,ホンの数秒で
NullReferenceException
でアプリが落ちる
C++側のスレッド内Sleepとか調整したけど
やっぱり落ちる.落ちるタイミングはいつも一緒
てか,Sleep長くなったら,長くなった分正常稼働している時間が比例で長くなる
ん?これ,ガベージコレクタが走るタイミングで落ちてね?
マネージメモリがある程度ゴミ貯めて,GCスタートすると
ひょっとして,C++に渡したデリゲートのポインタ位置まで整理されて移動している予感
調べたら,それっぽいカキコが書いてあって
回避策は,デリゲートポインタを変数で保持しとけ,らしい
それが↑
これで落ちなくなったですわ
ようやっと,スケルトンが固まった
さて,画像周りを書いていこう...モルモルモル
C++側の話 ⇒ こちら
コメント
【#温泉】ちろっと温泉旅に行ってきた.北郷温泉!
高速道路、有料2115年まで 半永久化へ改正法成立
【#梅雨】西日本各地が梅雨入り(23/5/29)
ガソリン補助金、9月末終了 6月から段階的に圧縮―経産省
Intel、64ビット専用アーキテクチャ「x86S」のビジョンを公開
はしか、国内で複数の感染者確認 同じ新幹線車両に乗り合わせ
宮崎の高校で491人がインフルエンザ集団感染15日から休校(23/5/18)