web-dev-qa-db-ja.com

C ++ブール関数を使用したC#DllImportが正しく返されません

私は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としてコンパイルされていますが)

31
Shammah

私はあなたの問題の解決策を見つけました。宣言の前に次のマーシャリングを付ける必要があります:[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バイトの値を使用することを指定するマーシャリングを使用して変更する必要があります。

50
SOReader

元の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であることを示しています。

5
Puppy

これは実際には、boolを返す一般的なC++コードによってEAXが完全にクリアされていないことが原因です。 EAXが関数に入るときに偽の値を含むのは一般的であり、return falseに対して、コンパイラーは通常xor al, alを発行します。これにより、EAXのLSBのみがクリアされ、C#コードは結果のゼロ以外の値をtrueではなくfalseとして解釈します。

3
Dina

おそらく、関数の引数をマーシャリングすると役立つ場合があります。

 [MarshalAs(UnmanagedType.LPStr)] 

宣言は次のようになります。

 [DllImport( "Whisper.dll"、EntryPoint = "Exist"、CallingConvention = CallingConvention.Cdecl)] 
 public static extern bool Exist([MarshalAs(UnmanagedType.LPStr)] string name); 
2
SOReader

私はあなたのコードをテストしました、そしてそれは私のためにfalseを返します。したがって、何か他のことが起こっているに違いありません。

DLLを正しく再コンパイルしていますか?.DLLを削除して再構築してみてください。

それ以外は、すべてがうまくいくようです。デフォルトでは、マーシャリングは、DLLがANSIまたはUnicodeとしてコンパイルされているかどうかに関係なく、マーシャリング属性で装飾することなく、.NET文字列をconst char *に処理します。

http://msdn.Microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor5 を参照してください。

0
Simon Brangwin

次のシステムを使用して、ブール変数を送信します

__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を使用します。

https://stackoverflow.com/a/42618042/1687981

0