質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3774閲覧

C++作成のDLLをC#から呼びだす時の宣言

thkana

総合スコア7610

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/12/23 00:23

前提・実現したいこと

C++で作成したDLLをC#から呼び出そうとしています。この時、DLL側の関数のシグニチャが

C++

1INT func2(DWORD no, DWORD& serialNo, 2WORD& macCode, LPSTR macName, 3LPSTR groupName, 4ULONG timeOut=DEF_TIMEOUT1);

となっているのですが、これに対応させるC#側の宣言はどう記述するのが正しいのでしょうか。

発生している問題・エラーメッセージ

実行時にPInvokeの例外(後述)が出ます。

マネージド デバッグ アシスタント 'PInvokeStackImbalance' Message=マネージド デバッグ アシスタント 'PInvokeStackImbalance' : 'PInvoke 関数 'DllTest!DllTest.TempertureMeter+UTIL::TdTR7wfCom_GetConctMachInfo' がスタックを不安定にしています。PInvoke シグネチャがアンマネージ ターゲット シグネチャに一致していないことが原因として考えられます。呼び出し規約、および PInvoke シグネチャのパラメーターがターゲットのアンマネージ シグネチャに一致していることを確認してください。'

例外を無視した場合、得られた戻り値(参照で得られた値)は妥当です。
なお、同DLL内の
INT func1(DWORD sNo);
の呼び出しでは例外は出ません。

該当のソースコード

C#

1 [DllImport("Test.DLL", CallingConvention = CallingConvention.StdCall)] 2 public static extern int func1( 3 uint sNo); 4 5 [DllImport("Test.DLL",CallingConvention = CallingConvention.StdCall)] 6 public static extern int func2( 7 uint no, 8 ref uint serialNo, 9 ref short macCode, 10 StringBuilder macName, 11 StringBuilder groupName, 12 ulong timeout=2); 13 void testfunc() 14 { 15 uint sNo=0 16 int n = UTIL.func1(sNo);//例外は出ない 17 int rc = UTIL.func2(0, ref serialNo, ref macCode, macName, groupName);//例外が出る 18 } 19

試したこと

整数型の引数を渡す関数では例外にならないので、参照型の引数が問題なんだろうと見込んでいます。
ネット上を調べてみたのですが、引数がポインタ int *等の場合はいくらでも例があるのですが、参照&の場合が見つけられていません。
実行時はポインタでも参照でも同じと想定して(これが間違いだと考え直さなきゃいけませんが)ポインタへの対応と同様C#側にrefを付けています。

補足情報(FW/ツールのバージョンなど)

VisualStudio2017、updateは11月末にチェックしています。
DLLはバイナリ提供されているもので、中は覗けません。

以上、ご存知の方よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

最後の引数ulong timeoutの型が間違っています。
C#(CLI)の世界のulong は64ビット(System.UInt64) です。DLLのシグネチャはWin32APIでのULONG(=DWORD)なので、32ビット。なので、P/Invokeの際は、ulongではなくて、uint(System.UInt32) にしなければなりません。

C#

1#正しい例 2[DllImport("Test.DLL",CallingConvention = CallingConvention.StdCall)] 3public static extern int func2( 4 uint no, 5 ref uint serialNo, 6 ref short macCode, 7 StringBuilder macName, 8 StringBuilder groupName, 9 uint timeout = 2);

投稿2018/12/23 06:11

dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2018/12/23 11:03

そか...ULONGって32bitなんですね。把握していませんでした。 その修正で例外は出なくなりました。 ありがとうございました。
guest

0

ポインタを受けるときはIntPtrとかそこらへんでは。

C++側の関数の呼び出し規約の指定が必要だったり。
メモリ配置や変数の構造が重要になるのでアドレス値やら指し先がどうなってるかなどを調べてみてはどうでしょう

#「C# マーシャリング」でぐぐるとそこらへんのことがでてきますね。

投稿2018/12/23 00:34

編集2018/12/23 00:38
y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2018/12/23 02:32

> C++側の関数の呼び出し規約の指定が必要 ``` CallingConvention = CallingConvention.StdCall ``` は適切ではないのですか。 C++側引数 int *var をC#側 ref int varとかで受けている例も見かけたので真似したのですが、それはよろしくない、ということでしょうか。
y_waiwai

2018/12/23 02:55

C++側の関数定義で、stdcall指定はされてるでしょうか
thkana

2018/12/23 03:43

DLLのヘッダファイルの宣言では INT WINAPI hoge(...); となっています。 C#のCallingConventionではWinapiを指定するとStdCallになるとのことで、 CallingConvention = CallingConvention.StdCall でよいと思ったのですが。少なくとも CallingConvention = CallingConvention.Winapi としてもこれまで記載した状況に変化はありません。
y_waiwai

2018/12/23 04:09

そのヘッダファイルの宣言が本当に有効になってるのか調べてみては。 こうなってるはず、と言ってるうちは問題の解決はできませんぜ。 #ヘッダのインクルードがされていなかったというのはよくある話
thkana

2018/12/23 04:12

どうやって調べましょう? DLLはバイナリ提供されているもので、中は覗けません。
Zuishin

2018/12/23 04:27

hoge は WINAPI だけど func2 は cdecl というオチはありませんか?
y_waiwai

2018/12/23 04:30

DLLに手を出せないとなれば今のままではどうしようもないという話で。 Stdcallにはなってないというオチなんでしょうな
y_waiwai

2018/12/23 04:33

調べるなら、呼び出し時のスタックからマシン語レベルで追いかけて、引数のアドレスからデータ配置やリターンアドレスなどどうなってるか調べるというはなしになりますね
thkana

2018/12/23 11:00

Zuishinさん 手抜きで済みません、一応ヘッダ内全ての関数がWINAPIと記述されている、というのをちゃんと説明すべきでした。(ヘッダがリンクされていないかも、なんていうとキリがないですけれど) y_waiwaiさん それはしんどいです。間違っている資料で痛い目を見るというのは現実としてありますけれど、すべての資料が間違っているかもしれないから「そのはず」を信頼しないというのは厳しいですね。 とりあえず、C#の側でCallingConvention.cdeclにすると例外の出ていなかったfunc1にも例外が出るようになるので(func1はStdcallでfunc2はcdeclだという可能性は否定出来ないにしても)Stdcallで合っていると判断できそうに思いますがどうでしょう?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問