- ベストアンサー
C#, C++/CLI, MFCにおけるデータ型の対応とメソッドの渡し方
- C#, C++/CLI, MFCにおけるデータ型の対応とメソッドの渡し方について説明します。
- 具体的には、MFCで書かれたメソッドをC++/CLIでラップし、C#から呼び出す方法について解説します。
- また、HDC、HWND、構造体など特殊なデータ型の扱い方についても詳しく説明します。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
> DLLのリンクはIJW機構というものを使用して そのようですね、勉強不足で申し訳ありません。 >> RECT lprcDC, lprcDib; > >> return dllfunction(hDC, lprcDC, hDib, lprcDib); > -------------------------------------------------------------- > この記述ですと、lprcDCとlprcDibがポインタに変換されていないと思う > のですが、これは記述ミスでしょうか? DllImport でマネージドとアンマネージドに変換されるので構造体を含め C++/CLI のマネージドコードで記述してます。 # 他のと同じように lp- というプリフィクスにしたのが誤解を招いたかも # 知れないです。 念のために拙いコードなのですが、 C# の GUI でコードを書いて動作 確認をしてみました。 TestDll.dll 側の関数実装には TextDraw と Bitblt を記述して任意の 文字列を bmp1(C#) の HDC に描画して bmp2(C#) の HDC には Bitblt で bmp1(C#) の HDC を反転したものを転送するようにしました。 TextDraw と BitBlt のどちらかで失敗した場合はその時点で FALSE を 返すようになっています。 HANDLE は void* の typdef なので HDC にキャストしました。 TestDll.dll にポインタが確実に渡っているか調べるのだけならそれで 問題ないのではないかと思いましたので。 ------------- 呼出用プログラム(C#) -------------- // PictureBox サイズの Bitmap を作成 Bitmap bmp1 = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height); Bitmap bmp2 = new Bitmap(pictureBox2.ClientSize.Width, pictureBox2.ClientSize.Height); // Bitmap から Graphics を得る Graphics g1 = Graphics.FromImage(bmp1); Graphics g2 = Graphics.FromImage(bmp2); // Graphics から HDC を得る IntPtr hDC = g1.GetHdc(); IntPtr hDBI = g2.GetHdc(); // DLL 呼び出し if (!wrapper.function(hDC, pictureBox1.ClientRectangle, hDBI, pictureBox2.ClientRectangle) ) MessageBox.Show("失敗"); // PictureBox へ描画 pictureBox1.Image = bmp1; pictureBox2.Image = bmp2; g1.ReleaseHdc(); g2.ReleaseHdc(); その他の部分は ANo.2 に記述したものと同じです。 ---------------------------------------------------- それと、一点お尋ねしたいのですが > BOOL result = Mvc10_PaintDIB( (HDC)hDC.ToPointer(), LPrcDC , (HANDLE)hDib.ToInt32(), LPrcDib ); HANDLE は typedef void* かと思うのですが hDib.ToInt32() として hDib.ToPointer() としてないのは何か理由があってのことでしょうか? # こちらで試したところ ToPointer() でも ToInt32() でも動作する # ようですし、立ち入ったことであれば申し訳ないですので、流して # ください。 > 実際に使用してみての結果は後日報告させていただきたいと思います。 無事に動作されることをお祈りいたします。 自己解決されておられるようで、お役に立てなくて申し訳ありませんでした。
その他の回答 (2)
- x415f484f
- ベストアンサー率71% (57/80)
どうもすみません、MFC DLL を C++ でラップしたかったのでしたね? C# から DllImport で直接呼び出せば手間が省けてよろしいかと思いましたで。 > 渡されたRECT構造体をLPRECT型に変換 LP は long pointer (32 ビットポインタ)の略なので「RECT *rc」として も可能です。 マイクロソフトの流儀にするなら「#define LPRECT RECT* 」でしょうけど windef.h の定義だと RECTtag をポインタにしているだけかと思います。 > これで合っているのでしょうか? DllImport の記載を省略されているのであれば、よろしいかと思います。 あまり参考にはならないかもしれないですが、コード書いてみました。 ちゃんと動作確認はしてないです、すみません。 もっと良い方法があると思いますが、一般人ですのでご容赦ください。 こんな感じにすれば、C# からラッパー用 DLL を参照するだけで使えるかと 思うのですが? いかがでしょうか? ※ インデントに全角文字が使ってあります。 ------------- TestDll.dll DLL(MFC) -------------- // TestDll.h プロトタイプ public: static BOLL dllfunction(HDC hDC, LPRECT lprcDC, HANDLE hDib, LPRECT lprcDib); // TestDll.def に エクスポート関数名追加 EXPORTS dllfunction ------------- DllWrapper.dll ラッパー用 DLL(CLI+C++) -------------- // using namespace 部分に追記 using namespace System::Drawing; // 参照で System.Drawing 追加 using namespace System::Runtime::InteropServices; struct RECT { int left; int top; int right; int bottom; }; [DllImport("TestDll.dll")] bool dllfunction(IntPtr hDC, RECT %lprcDC, IntPtr hDib, RECT %lprcDib); namespace DllWrapper { public ref class WrapperClass { public: static bool func(IntPtr hDC, Rectangle ^rcDC, IntPtr hDib, Rectangle ^rcDib) { RECT lprcDC, lprcDib; lprcDC.top = rcDC->Top; lprcDC.left = rcDC->Left; lprcDC.bottom = rcDC->Bottom; lprcDC.right = rcDC->Right; lprcDib.top = rcDib->Top; lprcDib.left = rcDib->Left; lprcDib.bottom = rcDib->Bottom; lprcDib.right = rcDib->Right; return dllfunction(hDC, lprcDC, hDib, lprcDib); } }; } ------------- 呼出用プログラム(C#) -------------- using DllWrapper; // 参照に DllWrapper.dll 追加 // IntPtr hDC, hDIB // Rectangle rcDC, rcDib WrapperClass.function(hDC, rcDC, hDIB, rcDib); ------------------------------------------------- また、既知かも知れませんが DllImport せずに C++/CLI にネイティブ コードを取り込む方法もあるようです。 @IT:特集:Visual C++ 2005 いままたC++が熱い!「C++/CLI」として大進化したVisual C++ 2005 http://www.atmarkit.co.jp/fdotnet/special/cppcli/cppcli_01.html
お礼
ご回答ありがとうございます。 >DllImport の記載を省略されているのであれば、よろしいかと思います。 DLLのリンクはIJW機構というものを使用して、ヘッダのインクルードと、ライブラリファイルのリンクをするだけでOKなようです。 http://msdn2.microsoft.com/ja-jp/library/ms235282(VS.80).aspx また、MFCのRECT構造体やなども、 #include <afxwin.h> をStdafx.hに記述することで使用可能なようです。 ------------------------------------------------------------ > RECT lprcDC, lprcDib; > return dllfunction(hDC, lprcDC, hDib, lprcDib); -------------------------------------------------------------- この記述ですと、lprcDCとlprcDibがポインタに変換されていないと思うのですが、これは記述ミスでしょうか? あと、以前記述したものだと、lprcDCやlprcDibに別の値などが代入された場合に、呼び出しもとの変数が変更されないと思われるので、以下のような記述を追加しました。 -------------------------------------------------------------- //関数を呼び出し、結果を格納 BOOL result = Mvc10_PaintDIB( (HDC)hDC.ToPointer(), LPrcDC , (HANDLE)hDib.ToInt32(), LPrcDib ); //渡した構造体の値が変更された場合を考えて、渡された引数に値を格納しなおす lprcDC.top = rcDC.top; lprcDC.bottom = rcDC.bottom; lprcDC.left= rcDC.left; lprcDC.right = rcDC.right; lprcDC.top = rcDC.top; lprcDC.bottom = rcDC.bottom; lprcDC.left= rcDC.left; lprcDC.right = rcDC.right; //結果を返す if(result == FALSE){ return false; } else{ return true; } -------------------------------------------------------------- 一応これで、エラー、警告無くコンパイルが通ったので、これで試してみようと思います。まだ、実際に試せない(訳あってハードウェアなどの環境が整っていない)ので、実際に使用してみての結果は後日報告させていただきたいと思います。
補足
ハードウェアの環境が整い、動作確認を行ったので、結果を報告させていただきます。 すべてのメソッドの動作を確認したわけではないのですが、構造体やハンドルを引数に持つメソッドを呼び出して確認したところ、きちんと動作していることが確認されました。 これで、開発が進められそうです。色々とアドバイスいただき、ありがとうございました。
- x415f484f
- ベストアンサー率71% (57/80)
> これらのサイトを参考にしながら と仰せられるのなら、以下の記述は的外れで無礼かも知れませんので その節はご容赦ください。 > HDC(デバイスコンテキスト)、HWND(ウィンドウのハンドル)、 C# から HDC, HWND を使う場合 IntPtr を用います。 > CIRCLE,RECT等の構造体、LPRECTなどの構造体のアドレス、 > POINT*などの構造体のポインタ > これらが、C#やCLIでどのような型になるのか Windows API の RECT 構造体を例として話をすすめますが RECT 構造体は「[StructLayout(LayoutKind.Sequential)]」を 書いてから自前で定義する必要があります。 [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } LPRECT などは ref RECT として渡します。 参考になりそうな URL http://www.atmarkit.co.jp/fdotnet/dotnettips/026w32struct/w32struct.html C#, VB.NET で API を使用する構造体や関数呼出等の定義 (C# との型の変換について参考になれば宜しいかと) http://www.pinvoke.net/
お礼
ご回答ありがとうございます。教えていただいた内容を元にコードをかいてみました。 C++/CLIの実装部分で、渡されたRECT構造体をLPRECT型に変換する方法が分からなかったので、一度MFCのRECT型変数を作成してから、LPRECT型の変数にアドレスを渡してみました。これで合っているのでしょうか? ----MFCで書かれたメソッド---------------------------------- BOOL function( HDC hDC, LPRECT lprcDC, HANDLE hDib, LPRECT lprcDib ); ----------------------------------------------------------- ----C#インタフェイス部分----------------------------------- bool Function(IntPtr hDC, ref SoccerStruct.RECT lprcDC, IntPtr hDib, ref SoccerStruct.RECT lprcDib ); ----------------------------------------------------------- ----C++/CLI宣言部分---------------------------------------- virtual bool Function( IntPtr hDC, SoccerStruct::RECT% lprcDC, IntPtr hDib, SoccerStruct::RECT% lprcDib ); ----------------------------------------------------------- ----C++/CLI実装部分---------------------------------------- bool DocumentClass::Function(IntPtr hDC, SoccerStruct::RECT% lprcDC, IntPtr hDib, SoccerStruct::RECT% lprcDib ) { RECT rcDC; rcDC.top=lprcDC.top; rcDC.bottom = lprcDC.bottom; rcDC.left= lprcDC.left; rcDC.right = lprcDC.right; RECT rcDib; rcDib.top = lprcDib.top; rcDib.bottom = lprcDib.bottom; rcDib.left = lprcDib.left; rcDib.right = lprcDib.right; LPRECT LPrcDC = &rcDC; LPRECT LPrcDib = &rcDib; if( function( (HDC)hDC.ToPointer(), LPrcDC, (HANDLE)hDib.ToInt32(), LPrcDib ) == FALSE ) { return false; } else { return true; } } -----------------------------------------------------------
お礼
ご回答ありがとうございます。 >DllImport でマネージドとアンマネージドに変換されるので構造体を含めC++/CLI のマネージドコードで記述してます。 [DllImport("TestDll.dll")] bool dllfunction(IntPtr hDC, RECT %lprcDC, IntPtr hDib, RECT %lprcDib); ここでアドレスを渡していたのですね。見落としていました。 >HANDLE は typedef void* かと思うのですが hDib.ToInt32() として >hDib.ToPointer() としてないのは何か理由があってのことでしょうか? すいません。これは記述ミスですね。他のメソッドでは.ToPointer()としていました。HWND型の場合は.ToInt32()とするらしい(どこかのサイトに書いてありました)ので、それと混同していたのだと思います。 >自己解決されておられるようで、お役に立てなくて申し訳ありませんでした。 いえいえ、大変参考になりました。さらに、動作確認までしていただいてありがとうございます。