ネイティブ(C++)コードとマネージド(C#)コード間の相互運用性のためにPinvokeを使用しています。私が達成したいのは、ネイティブコードからマネージコードにテキストを取り込むことです。このために、私は多くのことを試みます。たとえば、refによるstring/stringbuilderの受け渡し、[IN]と[OUT]の使用、LPSTRへのマーシャリング、関数からの文字列の返送などですが、私の場合は何も機能しません。いくつかの小さなコードの助けをいただければ幸いです。
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;
}
これは邪魔にならない最も難しい方法であり、他のラッパーを追加して、BSTR
、std::string
、std::wstring
などからLPWSTR
に変換するのは簡単です。
これを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);
}
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など)に変更するのは簡単です。