• 締切済み

C++で作ったDLLをC#で使うには?

C#でC++で書いたDLLを使いたいと思っていますが、可能でしょうか? 本で調べてみると出来ると思って挑戦しています。 public class Form1 : System.Windows.Forms.Form { [DllImport("test.dll")] public static extern long abc(); ..... と宣言し、使う時には long a = abc(); としています。 しかし、いざデバッグして実行してみると、エントリポイントが見つかりません。と表示されてしまいます。 (そもそもDLLが使えるのならばですが)できればC#でC++のDLLが使いたいため、詳しい方ご教授願います。

みんなの回答

noname#30727
noname#30727
回答No.3

>他に手続きは必要なのでしょうか? 一応こんな感じです。 extern "C" { __declspec(dllexport) int Test(int x, int y) { return 0; } } 関数を上のように定義すると "Test" の名前でエクスポートされます。 __cdecl で問題なければ、このエクスポートの方法がいいでしょう。 extern "C" { __declspec(dllexport) int __stdcall Test(int x, int y) { return 0; } } 上のように __stdcall を追加すると、今度は "_Test@8" の名前でエクスポートされます。 @以降は、引数に使用するスタックのサイズだったはずです。 このような名前が嫌いで、__stdcall でないと困る場合はDEFファイルを作ります。 ---- project.def ---- LIBRARY project EXPORTS Test --------------------- ※↑のTestの前にタブかスペースを入れてください extern "C" { int __stdcall Test(int x, int y) { return 0; } } プロジェクトのプロパティから、リンカ→入力→モジュール定義ファイル と移動し、project.def と入力して OK します。 project.def の名前は自由です。その中のLIBRARY以降の名前は、作成されるDLLと同じ名前にします。 ビルドすると、"Test" の名前でエクスポートされます。 ※説明の為に __stdcall と書きましたが、WINAPI と書いた方がいいと思います。

noname#30727
noname#30727
回答No.2

C#のlongは64ビットで、VC++のlongは32ビットなので、C#からはintと扱って問題ないでしょう。 stringはデフォルトでマルチバイトで渡されるので、それで問題なければそのまま渡せます。 C#のlong,float,doubleは渡した事がなかったりもしますが、C#のlongをVC++の__int64と受け取る以外はそのままいけそうな気がします(役にたたずすいません)。

excistar
質問者

お礼

色々と勉強になりました。 変数のビット数を考慮する必要があるのですね。 これから、色々と試してみたいと思います。 どうもありがとうございました!

excistar
質問者

補足

その後色々と試した結果、過去に作ったDLLは読めるようになってきたのですが、新しく作っているものが読めません。 もしかしたらDLLの作成方法に問題があるのかもしれません。 現在、win32プロジェクトからDLLを作成しています。 例えば、足し算をする関数を作る場合、test.hには TEST_API int __stdcall Tasizan(int a,int b); とし、test.cppには TEST_API int __stdcall Tasizan(int a,int b) { int ans; ans = a + b; return ans; } としてDLLを作っています。 しかし、これだと前述のようにエントリポイントが見つかりませんとなってしまいます。 他に手続きは必要なのでしょうか? 初心者の質問で本当にすみません。

noname#30727
noname#30727
回答No.1

DLLが使えるのは間違いないです。 [DllImport("USER32.DLL", EntryPoint="MessageBoxW")] private static extern bool MBox( int h, [MarshalAs(UnmanagedType.LPWStr)] string m, [MarshalAs(UnmanagedType.LPWStr)] string c, int type); MBox(0, "文字列", "タイトル", 0); あとは、DLLのエントリーポイント名が本当にabcなのかってところでしょう。 私はコマンドラインから、dumpbin /EXPORTS test.dll と確認しています。

excistar
質問者

お礼

ありがとうございます。 試行錯誤した結果、動作するようになりました。 確認方法は初めて知りました。もう少し調べてみたいと思います。 ところで戻値をlongにしていたのですが、これだと正確な値が帰ってこなく、intにした結果うまく動作しました。 stringの場合は手順を踏む必要があると記憶しているのですが、int以外のlong,doubleなども何か手続きが必要なのでしょうか? (それとも、単にプログラムミスだったのかも。。) 私の愚問に答えていただき、ありがとうございました。

関連するQ&A

  • C#からDLLを呼びたいのですが・・・

    C#のASP.NET WebアプリケーションのDLLをC#のWindowsFormのexeで呼び出そうとしています。 すると 'System.EntryPointNotFoundException' のハンドルされていない例外が FTPApi.exe で発生しました。 追加情報 : DLL SampleApi.dll の GetURL というエントリ ポイントが見つかりません。 というエラーメッセージが表示されてしまいます。 クラス上部で [System.Runtime.InteropServices.DllImport("SampleApi.dll", EntryPoint="GetURL")] public static extern string GetURL(); と宣言した後、呼び出したい場所で、 strUrlPath = GetURL(); と呼んでいます。 よろしくお願いします。

  • C# DLL エントリ ポイントが見つかりません。

    C#で作成したDLLをC#から呼出したいのですが、うまくいきません。 // トリガ private void button2_Click(object sender, EventArgs e) { string ans = DLL.moji(); MessageBox.Show(ans); } // DLL呼出し用クラス class DLL { [DllImport("LibCsharp.dll")] public static extern string moji(); } // DLL(LibCsharp.dll内容) class AddClass { public static string moji() { return ("テスト"); } } 上記で実行すると「EntryPointNotFoundException DLL 'LibCsharp.dll' の 'moji' というエントリ ポイントが見つかりません。」となります。 プロジェクトの参照設定にはLibCsharpを追加しています。 Web上で調べたのですが、解決には至っておりません。 申し訳ありませんが、よろしくお願いします。

  • C#からVC++DLLの呼び出しに失敗する

    VS.NET2005のC#からVC++6.0で作成したDLLを呼び出しています。 VCのデバッガでデバッグしてみると呼び出した関数は正常に動作しているようなのですが、最後のreturnを実行しC#に戻るところでエラーが出てしまいます。 C#側では「'System.ExecutionEngineException' のハンドルされていない例外が ConsoleApplication1.exe で発生しました。」となります。 C#での宣言は、 [DllImport("test.dll")] public static extern uint TestFunc(ref byte[] moji, ref uint nagasa); です。 VC++の宣言は、 __declspec(dllexport) unsigned long __stdcall TestFunc(unsigned char* moji, unsigned long* nagasa) です。 C#へ値を返さない関数は問題ないのですが、値を返そうとすると起きるようです。不備などあればご指摘いただけないでしょうか。 ちなみにVB6からは問題なく動作できました。

  • C#からC++のDLLを使いたい時

    C#からC++(unmanaged)のDLLを使いたい時、 DLLをどのように参照すればよいのでしょうか。 [DllImport("名前.dll")] extern static double 関数名(); という指定は分かるのですが、 それだけじゃもちろん、 「DLL (名前.dll) を読み込めません。」 ってなりますよね・・・。

  • C#でWin32 MFC DLLが呼び出せない

    C#で以下のエラーが発生し、非常に困ってます。 色々とサイトを見たのですが、DLLが本当にないのでは?という意見が多かったです。 しかし、私の方はDLLファイルはbin->debugフォルダ , obj->debugフォルダ、System32に置いてます。 問題が分かる方、是非教えてください! using System.Runtime.InteropServices; namespace WindowsFormsApplication { public partial class Form1 : Form { [DllImport("Win32DLL_TEST.dll")] private extern static int func(int x, int y); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int n = func3(5, 7);//ここでエラー! } } } エラー内容 'System.DllNotFoundException' のハンドルされていない例外が WindowsFormsApplication.exe で発生しました。 追加情報: DLL 'Win32DLL_TEST.dll' を読み込めません: 指定されたモジュールが見つかりません。 (HRESULT からの例外: 0x8007007E) 備考 DLLは自作のWin32 DLL です。 extern "C"{ __declspec(dllexport) int __cdecl func(int x, int y) } と宣言し、VC++のMFC dialog上では呼び出しを成功しています。

  • C#の仕様諸々について

    諸事情あって 数時間前 初めてC#の仕様に触れ、初めて実際にいじってみました。 で、細かいとこ全然違うなーとか思いながら 色々と考えてみたのですが とりあえずこれ CやC++での#include とかに当たるものが何も書かずに自動化されてる、ような感じでしょうか この仕様だと 数万行程度の規模のプログラムだったら良いかもしれませんが もっと増えて仮に10万行越えるとかくらいになってくると、開発環境がちゃんと反応できるか心配です。 そう言う場合って 仮に個人で作るとして 尚且つC++だったら普通動的ロードしないような必須機能であったとしても やっぱりアセンブリの分割を(別途dllに)するのが良いということになるのでしょうか? またinternalを使った場合 外部のアセンブリから使えない→開発環境に自動チェックされる量を減らせる と考えて良いのでしょうか? あるいは、C#は、もともとそこまでは大規模なプログラム向けの言語ではないのでしょうか? そんで、分割の時のことなんですが DllImportで 関数ポインタぽいものを作る時に (意味的に、C・C++でいう)ポインタを引数にとるって事が通常考えられるじゃないですか。 その時なんですけど using System; using System.Runtime.InteropServices; namespace WindowsFormsApplication1 { public partial class Form1 : System.Windows.Forms.Form { [DllImport( "kernel32.dll", CallingConvention = CallingConvention.StdCall )] extern static short QueryPerformanceCounter(ref long x); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] extern static short QueryPerformanceFrequency(ref long x); public Form1(){ InitializeComponent(); long cnt = 0, frq = 0; //(1) QueryPerformanceCounter(ref cnt); QueryPerformanceFrequency(ref frq); ・ ・ ・ どうやらこういう風に (1)のところで初期化がいると思うんですよね。 で、あきらかにこれ内部で書きかえられることを想定してるんだから 初期化なしでやりたいんですが 初期化なしでやる場合は outに変える必要があると聞きました。 [DllImport( "kernel32.dll", CallingConvention = CallingConvention.StdCall )] extern static short QueryPerformanceCounter(out long x); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] extern static short QueryPerformanceFrequency(out long x); ・ ・ ・ long cnt, frq; QueryPerformanceCounter(out cnt); QueryPerformanceFrequency(out frq); でもこの場合って、実際には中身は書き変えられないかもしれないですよね? その辺はDllImportの場合(自分のアセンブリ内のことしか)関知しないってことでしょうか? それから ・C++などでいうconst(C#ではメンバに付けるreadonly的)なローカル変数 ・同じく、constあるいはreadonly的な、関数への参照渡し(ref) といったものは言語仕様上ない、のでしょうか?

  • C#から、C++作成dll内の関数を呼び出す方法

    C#から、C++で作成したdll内の関数を呼び出す方法は、以下の方法で実現できました。(メッセージボックスで "10" が表示されました) [C++側のヘッダファイル] #ifdef CPPDLL_EXPORTS #define CPPDLL_API __declspec(dllexport) #else #define CPPDLL_API __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ CPPDLL_API int fnCppDll(); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ [C++側のソースファイル] CPPDLL_API int fnCppDll() { return 10; } [C#側] using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApp1 { public partial class Form1 : Form { [DllImport("CppDll.dll")] public static extern int fnCppDll(); public Form1() { InitializeComponent(); } private void buttonGo_Click(object sender, EventArgs e) { int n = fnCppDll(); MessageBox.Show(n.ToString()); } } } 今回お聞きしたいのは、int型ではなく、C++側で型を定義されたクラスのオブジェクト(の参照)を返す関数を、C#側から呼び出し、それをどうやってC#で受け取るかを教えていただきたいのです。 具体的には以下のように実装してみました。 [C++側のヘッダファイル] #ifdef CPPDLL_EXPORTS #define CPPDLL_API __declspec(dllexport) #else #define CPPDLL_API __declspec(dllimport) #endif class CPPDLL_API CCppDll { public: int data; CCppDll(void); }; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ CPPDLL_API CCppDll& fnCppDll(); // C#が参照渡しということで、参照を返すようにした。 #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ [C++側のソースファイル] CPPDLL_API CCppDll& fnCppDll() { CCppDll* a = new CCppDll(); return *a; } CCppDll::CCppDll() : data(11) // メンバーは 11 で初期化 { } [C#側] using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApp1 { // ビルドエラーが起きるないように、とりあえず、C++と同じ(ような)クラスを定義 public class CCppDll { public int data; } public partial class Form1 : Form { [DllImport("CppDll.dll")] public static extern CCppDll fnCppDll(); public Form1() { InitializeComponent(); } private void buttonGo_Click(object sender, EventArgs e) { int n; CCppDll cls = fnCppDll(); // ※ n = cls.data; MessageBox.Show(n.ToString()); } private void buttonCancel_Click(object sender, EventArgs e) { Close(); } } } 実装しながらも、「これじゃあ、ダメだろうな。いかにもダメだな」と思った通り、上の※の部分で以下の例外が発生しました。 ------- 例外(ここから) ------- マネージド デバッグ アシスタント 'FatalExecutionEngineError' : 'ランタイムの重大なエラーが発生しました。エラーのアドレスは 0xcc9ff5a2、スレッド 0x36c8 です。エラー コードは 0xc0000005 です。これは CLR のバグであるか、またはユーザー コードのアンセーフまたは確認不可能な部分にバグがある可能性があります。このバグの一般的な原因には、スタックが壊れる可能性のある COM-interop または PInvoke のユーザー マーシャリング エラーが含まれています。' ------- 例外(ここまで) ------- 明らかに基本的なことが分かっていないことから起因するエラーと思われますが、具体的にどう実装すれば、正常に動きますか。(メッセージボックスで "11" を表示) よろしくお願いします。

  • 自作DLLの引数について、ポインタ渡しが??

    VS2005 VC++環境でMFCスマートデバイスDLLを作成しています(アプリ側もVS2005 VC++)。 テスト用のアプリからDLL内の文字列を引数とする関数を呼び出すと、 呼ばれた関数に正しく文字列が渡りません。 以下の場合、func1のw1はデバッガで正しく見れません。 でもw2にtcscpyは成功しw2には正しく値がセットされます。 このときにw1のアドレスがなぜか変更されます。 以下の例では引数が1つですが、2つ以上引数がある時には その他の引数のアドレスもとびます。 func2のl1は正常に見れます。 func3のl1は正常に見れません。 ポインタ渡しするときに異常になるのですが、 原因がさっぱりわかりません。 同じことをWindowsXP用のプロジェクトを作り、 XPで確認するとどれも正常に見れます。 どなたかアドバイスをお願いします。 ----------------------------------------DLL側---------------------------------------- extern "C" __declspec(dllexport) void func1(WCHAR * w1) { WCHAR w2[16] ; _tcscpy(w2,w1); return; } extern "C" __declspec(dllexport) void func2(long l1) { long l2 ; l2 = l1 ; return; } extern "C" __declspec(dllexport) void func3(long *l1) { long l2 ; l2 = *l1 ; return; } ----------------------------------------アプリ側---------------------------------------- extern "C" __declspec(dllimport) void func1(WCHAR *); extern "C" __declspec(dllimport) void func2(long ); extern "C" __declspec(dllimport) void func3(long *); ・・・・中略・・・・ WCHAR a[16] ; _tcscpy(a,_T("ABC")); func1(a); func2(123); long l3 = 123 ; func3(&l3);

  • C#で、DLL内の関数へのthisの渡し方

    例えば以下のように、thisを引数として渡して thisに格納されたWitdhを表示するコードを書いてみたのですが DLL側で以下のようなエラーが出てうまくいきません。 何か引数の渡し方などに問題があるのでしょうか?ご指導お願いします(>o<) ※エラー 'ClassLibrary1.Form1.Dispose(bool)': オーバーライドする適切なメソッドが見つかりませんでした。 ■フォームアプリケーションのコード private void Form1_Load(object sender, EventArgs e) {   System.Windows.Forms.Form This_obj = new System.Windows.Forms.Form();   This_obj = this;   GetFormWidth(This_obj); } ■↓DLL内のコード public class DLL {   public static void GetFormWidth(System.Windows.Forms.Form Form_obj) {     //引数のwidthを表示する。     System.Diagnostics.Debug.WriteLine(Form_boj.Width.Tostring());   } }

  • VS2010C#からのDLL使用について

    VS2010 C#からVC6で作成したDLLへint配列を引数として渡したい。 VC++6で作成されたDLLがあります。 ここで定義されている int test(char*fname,int* x,int* y); ファイルのフルパスを渡すと 返り値が関数の成否のコードを返す。 処理内で x,y にファイルから読み込んだ値が それぞれ上限100個としてセットして返す といったような関数を C#から呼び出したいのですが 1 [DllImport("dll名", EntryPoint = "test", CallingConvention = CallingConvention.Cdecl)] public unsafe static extern int test(IntPtr fname, ref int* x,ref int* y); 2 [DllImport("dll名", EntryPoint = "test", CallingConvention = CallingConvention.Cdecl)] public unsafe static extern int test(IntPtr fname, ref IntPtr x,ref IntPtr y); と2種類の呼び出し方法をこころみました。 ファイルのフルパスは IntPtr fnamePtr = Marshal.StringToHGlobalAnsi(string型ファイルフルパス); としております。 1の場合は int[] resX = new int[100]; int[] resY = new int[100]; fixed(int* x= resX) fixed(int* x= resY) として呼び出そうとすると : エラー CS1657: '固定変数' であるため、'x' を ref または out 引数として渡せません となり失敗してしまいます。 2 で試みた場合 int size = sizeof(int)*100; IntPtr x = Marshal.AllocHGlobal(size); IntPtr y = Marshal.AllocHGlobal(size); と引き渡したところ ビルドは通りますが 実行時に System.AccessViolationException' のハンドルされていない例外が 発生しました。 追加情報: 保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。 となってしまいます。 どのようにすれば思惑通りの結果を得られますでしょうか。