プロが教える店舗&オフィスのセキュリティ対策術

現在、Visual Studio 2005の、Visual C#、C++/CLI、MFCを使ったシステムを構築しています。
具体的には、MFCで書かれたライブラリ(DLL)をC++/CLIでラップし、C#から呼び出して使うというものです。
現在は、http://www.atmarkit.co.jp/fdotnet/special/vcppin …
http://msdn2.microsoft.com/ja-jp/library/ac7ay12 …
これらのサイトを参考にしながら、一部のMFCで書かれたメソッドを、C#から呼び出すことに成功しました。しかし、int型やHANDLE型などの、上記サイトに記述されているデータ型を用いたメソッドのラッピングや呼び出しはできたのですが、HDCやLPRECTなどの特殊な型の場合のやり方がよく分かりません。

具体的に現在分からないのは、次の通りです。
HDC(デバイスコンテキスト)、HWND(ウィンドウのハンドル)、CIRCLE,RECT等の構造体、LPRECTなどの構造体のアドレス、POINT*などの構造体のポインタ
これらが、C#やCLIでどのような型になるのか、メソッドに渡す時にどのように記述するのかが分かりません。

以上、どなたか分かる方いらっしゃいましたら、ご回答をお願いします。

A 回答 (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() でも動作する
# ようですし、立ち入ったことであれば申し訳ないですので、流して
# ください。

> 実際に使用してみての結果は後日報告させていただきたいと思います。

無事に動作されることをお祈りいたします。
自己解決されておられるようで、お役に立てなくて申し訳ありませんでした。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>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()とするらしい(どこかのサイトに書いてありました)ので、それと混同していたのだと思います。

>自己解決されておられるようで、お役に立てなくて申し訳ありませんでした。
いえいえ、大変参考になりました。さらに、動作確認までしていただいてありがとうございます。

お礼日時:2008/03/11 13:16

どうもすみません、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 …

この回答への補足

ハードウェアの環境が整い、動作確認を行ったので、結果を報告させていただきます。
すべてのメソッドの動作を確認したわけではないのですが、構造体やハンドルを引数に持つメソッドを呼び出して確認したところ、きちんと動作していることが確認されました。
これで、開発が進められそうです。色々とアドバイスいただき、ありがとうございました。

補足日時:2008/03/29 22:18
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
>DllImport の記載を省略されているのであれば、よろしいかと思います。
DLLのリンクはIJW機構というものを使用して、ヘッダのインクルードと、ライブラリファイルのリンクをするだけでOKなようです。
http://msdn2.microsoft.com/ja-jp/library/ms23528 …
また、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; }
--------------------------------------------------------------

一応これで、エラー、警告無くコンパイルが通ったので、これで試してみようと思います。まだ、実際に試せない(訳あってハードウェアなどの環境が整っていない)ので、実際に使用してみての結果は後日報告させていただきたいと思います。

お礼日時:2008/03/08 12:19

> これらのサイトを参考にしながら



と仰せられるのなら、以下の記述は的外れで無礼かも知れませんので
その節はご容赦ください。

> 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/026 …

C#, VB.NET で API を使用する構造体や関数呼出等の定義
(C# との型の変換について参考になれば宜しいかと)
http://www.pinvoke.net/
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。教えていただいた内容を元にコードをかいてみました。
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;
   }
}

-----------------------------------------------------------

お礼日時:2008/03/06 11:26

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!