文字列をマーシャリングする規則については、"Default Marshaling for Strings"で説明しています。ネイティブ関数(プラットフォーム相互運用機能)のために、ドキュメントが指定:
プラットフォームは、プラットフォーム管理されていない形式に.NET Frameworkの フォーマット(ユニコード)から変換、コピーした文字列引数を呼び出します。文字列 は不変であり、呼び出しが返されたときにアンマネージドメモリから管理された メモリにコピーバックされません。
自明実験的に確立することができるようしかし、変換はまったく必要ありませんならば、これはない真である、つまり、方法はCharSet.Unicode
で飾られているか、文字列を明示的にMarshalAs(UnmanagedType.LPWStr)
としてマークされています。この場合、文字列の内容へのポインタは直接渡されます。これは非常に効率的だと思いますが、アンマネージ関数が渡された文字列を変更するのを止めるものがないため、危険です。 .NETの文字列は不変であると考えられ、コードはそれに依存する可能性があるので、これは悪いことです。これが文字列のinternプールを上書きすることになると、特に悪いことです。
trample.c
:
__declspec(dllexport) void __stdcall Trample(wchar_t* text) {
memcpy(text, L"Adios", (sizeof L"Adios") - 2);
}
Program.cs
:
static class NativeMethods {
[DllImport("trample.dll", CharSet = CharSet.Unicode)]
public static extern void Trample(string text);
}
class Program {
static void Main(string[] args) {
Console.WriteLine("Hello, world!");
NativeMethods.Trample("Hello, world!");
Console.WriteLine("Hello, world!");
}
}
出力:"Hello, world!"
以来
Hello, world!
Adios, world!
はそれのすべてのインスタンスは、文字列インターンプールで終わる、文字列リテラルです、そして毎回それはあなたです私たちは "同じ"文字列を使用しています。管理されていない関数がこれを上書きするため、"Hello, world!"
をマネージコードで記述していると考えられるときは、代わりに別のものが使用されます。おっとっと。
管理されていない関数が文字列を変更することがわかっている場合は、代わりにStringBuilder
を渡します。ここでは、コピーするかどうかを選択することもできます(InAttribute
/OutAttribute
)。これには、バッファへのコピー/バッファからのコピーが含まれます。具体的には、CoTaskMemAlloc
を使用してアンマネージコード用のメモリを割り当てます(呼び出しが完了するとCoTaskMemFree
が呼び出されます)。このコードは、呼び出しの一部としてマーシャラによって呼び出されます。管理対象の呼び出し元も管理されていない呼び出し先も、これに関わる必要はありません。
ANSI文字列を必要とする関数を呼び出すには、バッファの割り当ても必要ですが、この場合はlocalloc
命令を使用してバッファを割り当てます。CoTaskMemAlloc
ではなく、より効率的です。つまり、実際に効率を上げようとしているのであれば、単純に文字列の受け渡しを最適化するだけでなく、可能であればアンマネージコードを完全に呼び出すことを避けることです。コピーメモリを無視しても、管理/非管理移行にはかなりのオーバーヘッドがあります。ループ内でアンマネージコードを呼び出すと、マネージコードにそのコードを移植できるかどうかを調べることができます。
出典:coreclr/src/vm/ilmarshalers.cpp
、具体的にはILWSTRMarshaler::EmitConvertSpaceAndContentsCLRToNativeTemp
。
偉大な答え!ありがとう。 – bright