私はC++ DLLで次の関数を持っています
extern "C" __declspec(dllexport) bool Exist(const char* name)
{
//if (g_Queues.find(name) != g_Queues.end())
// return true;
//else
// return false;
return false;
}
私のC#クラスには、次のものがあります。
[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)]
public static extern bool Exist(string name);
それでも、関数を呼び出すと、小さな関数をコメントアウトしてfalseを返すようにした場合でも、常にtrueが返されます。呼び出し規約やDLLのP/Invokingに問題があり、おそらく文字列とconst char *に対応していると感じていますが、今のところ完全に無知です。私は何が間違っているのですか? falseではなくtrueを返すのはなぜですか?
編集:問題が続くので、これはconst char *またはstringとは何の関係もないことがわかりました。空の関数。 CdeclとStdCallの間で呼び出し規約を変更しようとしましたが、どちらも正しく機能しません。また、DLLをデバッグすることもできました。正しく呼び出され、実際にfalseが返されますが、C#に戻ると、どういうわけかtrueになります。CharSetを変更しても効果はありませんでした。毎回C#プログラムに最新の正しいバージョンのDLLを提供していることを確認したので、それも問題にはならないはずです。繰り返しになりますが、結果がなぜか完全にわかりません。私が実際にfalseを返しているときはtrueです。
EDIT2:SOReaderは、別の重要な問題を修正する提案を私に提供しました。私のコメントを参照してください。残念ながら、返品の問題は修正されません。
EDIT3:Exist(bool)の戻り値の型を(int)に変更すると、突然正しい値が返されると結論付けました数値(true = 1、false = 0)。これは、C++のブール値とC#のブール値の間に問題がある可能性があることを意味します。 intをboolとして使い続けることはできますが、それでも元の問題を説明することはできません。多分誰か他の人がこれについて私を啓発することができますか?おそらくそれは私がx64を使用しているという事実と関係があります(両方のオブジェクトはx86としてコンパイルされていますが)
私はあなたの問題の解決策を見つけました。宣言の前に次のマーシャリングを付ける必要があります:[return:MarshalAs(UnmanagedType.I1)]
したがって、すべてが次のようになります。
[DllImport("Whisper.dll", EntryPoint="Exist", CallingConvention=CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);
私は非常に単純な例でそれをテストしました、そしてそれはうまくいきました!
[〜#〜]編集[〜#〜]
なぜこれが起こるのですか? Cはboolを4バイトのintとして定義し(一部の人が言っているように)、C++はそれを1バイトとして定義します。 C#チームは、ほとんどのシステムAPI関数が4バイトの値をブールとして使用するため、PInvoke中にデフォルトとして4バイトのブールを使用することを決定しました。この動作を変更する場合は、1バイトの値を使用することを指定するマーシャリングを使用して変更する必要があります。
元のC言語にはブール型がないため、C's bool
は実際にはint
です。つまり、C#のDLLImportがCコードと相互運用するように設計されている場合、C#のbool
がCのint
に対応することを期待します。これはfalseがtrueになる理由をまだ説明していませんが、それを修正すると問題が修正されるはずです。
http://msdn.Microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx
これは、UnmanagedType.BoolがWin32 BOOL
であり、これはint
であることを示しています。
これは実際には、bool
を返す一般的なC++コードによってEAXが完全にクリアされていないことが原因です。 EAXが関数に入るときに偽の値を含むのは一般的であり、return false
に対して、コンパイラーは通常xor al, al
を発行します。これにより、EAXのLSBのみがクリアされ、C#コードは結果のゼロ以外の値をtrue
ではなくfalse
として解釈します。
おそらく、関数の引数をマーシャリングすると役立つ場合があります。
[MarshalAs(UnmanagedType.LPStr)]
宣言は次のようになります。
[DllImport( "Whisper.dll"、EntryPoint = "Exist"、CallingConvention = CallingConvention.Cdecl)] public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name);
私はあなたのコードをテストしました、そしてそれは私のためにfalseを返します。したがって、何か他のことが起こっているに違いありません。
DLLを正しく再コンパイルしていますか?.DLLを削除して再構築してみてください。
それ以外は、すべてがうまくいくようです。デフォルトでは、マーシャリングは、DLLがANSIまたはUnicodeとしてコンパイルされているかどうかに関係なく、マーシャリング属性で装飾することなく、.NET文字列をconst char *に処理します。
http://msdn.Microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor5 を参照してください。
次のシステムを使用して、ブール変数を送信します
__declspec(dllexport) const bool* Read(Reader* instance) {
try {
bool result = instance->Read();
bool* value = (bool*)::CoTaskMemAlloc(sizeof(bool));
*value = result;
return value;
} catch (std::exception exp) {
RegistryException(exp);
return nullptr;
}
}
C#では、
DllImport(WrapperConst.dllName)]
public static extern IntPtr Read(IntPtr instance);
public bool Read() {
IntPtr intPtr = ReaderWrapper.Read(instance));
if(intPtr != IntPtr.Zero) {
byte b = Marshal.ReadByte(intPtr);
Marshal.FreeHGlobal(intPtr);
return b != 0;
} else {
throw new Exception(GetLastException());
}
}
True/falseを正しく返すことができるsigned intを使用します。