2017-03-28 3 views
3
[DllImport("foo.dll", CharSet = CharSet.Unicode)] 
static extern void Process_utf16(string text, int text_length); 

ネイティブ関数は、utf-16データ(これは.net文字列が使用するものです)を受け取るように記述されています。返された後、文字列データは使用されません。したがって、私は文字列のバッファへのポインタが不要な割り当てやコピーをせずに直接渡されるようにしようとしています。DllImportでCharset.Unicodeを指定すると文字列が割り当てられますか?

この宣言では、割り当てなしで渡された文字列のバッファへのポインタですか?または、一時バッファが割り当てられ、文字列がそのバッファにコピーされていますか?割り当てが発生した場合は、それをネイティブまたはマネージヒープに割り当てますか?誰がそれを解放する責任がありますか?

上記のコードはテスト済みで、動作していることに注意してください。割り当てとコピーが発生しているかどうかを確認しています。

答えて

1

文字列をマーシャリングする規則については、"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

+0

偉大な答え!ありがとう。 – bright

関連する問題