gpt4 book ai didi

c# - 在 DllImport 中指定 Charset.Unicode 是否分配字符串?

转载 作者:行者123 更新时间:2023-11-30 17:33:58 25 4
gpt4 key购买 nike

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
static extern void Process_utf16(string text, int text_length);

native 函数被编写为接收 utf-16 数据(这也是 .net 字符串使用的。)它返回后不使用字符串数据。因此,我试图确保直接传递指向字符串缓冲区的指针,而不进行任何不必要的分配或复制。

在这个声明中,是否传递了一个指向字符串缓冲区的指针而没有分配?还是分配了临时缓冲区并将字符串复制到其中?如果发生分配,是分配给 native 堆还是托管堆?谁负责解除分配?

请注意,上面的代码已经过测试并且可以工作,我只是想看看它是否会导致分配和复制,如果是,如何避免。

最佳答案

"Default Marshaling for Strings" 中描述了编码字符串的规则。 .对于 native 功能(平台互操作),文档指定:

Platform invoke copies string arguments, converting from the .NET Framework format (Unicode) to the platform unmanaged format. Strings are immutable and are not copied back from unmanaged memory to managed memory when the call returns.

但是,正如可以通过实验简单确定的那样,如果根本不需要转换,即该方法用 CharSet.Unicode 修饰,则这不正确或者该字符串被显式标记为 MarshalAs(UnmanagedType.LPWStr)。在这种情况下,直接传递指向字符串内容的指针。这听起来非常有效,确实如此,但它也很危险,因为没有什么可以阻止非托管函数修改传递给它的字符串。这很糟糕,因为 .NET 字符串应该是不可变的,并且代码可能依赖于此。如果这最终会覆盖字符串实习生池,那就特别糟糕了。

trample.c:

__declspec(dllexport) void __stdcall Trample(wchar_t* text) {
memcpy(text, L"Adios", (sizeof L"Adios") - 2);
}

程序.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!
Adios, world!

因为 "Hello, world!" 是一个字符串文字,它的所有实例最终都在字符串实习池中,并且每次使用它时我们都使用“相同”的字符串。我们的非托管函数覆盖了它,所以现在每当我们认为我们在我们的托管代码中编写 "Hello, world!" 时,我们最终会得到其他东西。糟糕。

如果您知道非托管函数会更改字符串,避免这种情况的方法是改为传递 StringBuilder。在这里您甚至可以选择是否要复制到输入、输出或两者(使用 InAttribute/OutAttribute)。这确实涉及复制到缓冲区/从缓冲区复制——具体来说,CoTaskMemAlloc 将用于为非托管代码分配内存(并且 CoTaskMemFree 将在调用完成时调用)。此代码作为调用的一部分由编码(marshal)拆收器调用;托管调用者和非托管调用者都不需要关心这个。

调用需要 ANSI 字符串的函数也涉及分配缓冲区,但在这种情况下,缓冲区是使用 localloc 指令分配的,而不是 CoTaskMemAlloc,它是 bunches更高效。也就是说——如果您实际上是为了提高效率,那么您要做的是尽可能完全消除调用非托管代码,而不仅仅是优化字符串传递。即使忽略复制内存,托管/非托管转换也涉及相当多的开销。如果您发现自己在循环中调用非托管代码,那么查看该代码是否可以移植到托管代码是值得的。

来源:coreclr/src/vm/ilmarshalers.cpp , 具体来说 ILWSTRMarshaler::EmitConvertSpaceAndContentsCLRToNativeTemp .

关于c# - 在 DllImport 中指定 Charset.Unicode 是否分配字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43077734/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com