web-dev-qa-db-ja.com

ネイティブ(C ++)コードからテキストを返す方法

ネイティブ(C++)コードとマネージド(C#)コード間の相互運用性のためにPinvokeを使用しています。私が達成したいのは、ネイティブコードからマネージコードにテキストを取り込むことです。このために、私は多くのことを試みます。たとえば、refによるstring/stringbuilderの受け渡し、[IN]と[OUT]の使用、LPSTRへのマーシャリング、関数からの文字列の返送などですが、私の場合は何も機能しません。いくつかの小さなコードの助けをいただければ幸いです。

33
Jame

BSTRを使用して行います。これは、文字列ごとにネイティブを2回呼び出す必要がないことを意味します。1回は長さを取得し、もう1回はコンテンツを取得します。

BSTRを使用すると、マーシャラーが適切なメモリマネージャーを使用してBSTRの割り当てを解除するため、C++コードから安全に渡すことができます。

C++

#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L"Greetings from the native world!");
}

C#

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();

BSTRには1つの小さな欠点があります。つまり、UTF-16ペイロードを伝送しますが、ソースデータはchar*である可能性があります。

これを克服するには、次のようにchar*からBSTRへの変換をまとめることができます。

BSTR ANSItoBSTR(const char* input)
{
    BSTR result = NULL;
    int lenA = lstrlenA(input);
    int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
    if (lenW > 0)
    {
        result = ::SysAllocStringLen(0, lenW);
        ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
    } 
    return result;
}

これは邪魔にならない最も難しい方法であり、他のラッパーを追加して、BSTRstd::stringstd::wstringなどからLPWSTRに変換するのは簡単です。

46
David Heffernan

ここ は、文字列マーシャリングが議論されたトピックです。

パラメータを属性でマークする必要があります

[MarshalAs(UnmanagedType.LPSTR)]
4
Disposer

これをC#で行う例を次に示します。 pInvokingを使用してC#を介してネイティブ関数GetWindowTextを呼び出しています。 GetWindowTextは、handleが渡されたウィンドウのキャプションを返します。

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern int GetWindowTextLength(IntPtr hWnd);

    public static string GetText(IntPtr hWnd)
    {
        // Allocate correct string length first
        int length = GetWindowTextLength(hWnd);
        StringBuilder sb = new StringBuilder(length + 1);
        GetWindowText(hWnd, sb, sb.Capacity);
        return sb.ToString();
    }        

    private void button1_Click(object sender, EventArgs e)
    {
        string str = GetText(this.Handle);
    }
4
Aamir

char *を返し、戻り値にメモリを割り当てるためにC/C++コードを変更する必要がない場合(またはそのコードを変更できない場合)、C#を変更できます。 extern関数-IntPtrを返すプロトタイプであり、自分でマーシャリングを行います。

たとえば、これは私が書いた相互運用機能のスニペットです Pocketsphinx

[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf);

そして、これがC #JsgfGrammarクラスのget-accessorです。 m_pUは、生のjsgf_tオブジェクトを指すIntPtrです。

public string Name
{
    get
    {
        IntPtr pU = jsgf_grammar_name(m_pU);
        if (pU == IntPtr.Zero)
            strName = null;
        else
            strName = Marshal.PtrToStringAnsi(pU);
        return strName;
    }
}

この例を他の文字列形式(Unicodeなど)に変更するのは簡単です。

1
ulatekh