请教 AllocHGlobal 出来的究竟是结构体的指针还是指针的指针

2020-12-06 05:48:50 +08:00
 natsukage

.net core 2.2+版本,在测试时候发现的奇妙问题。测试时是在 5.0 上发现的,但是似乎所有版本.net core 都存在这个特性。

Marshal.AllocHGlobal 时, 对于 UnmanagedType.LPUTF8Str,Alloc 的是指针的指针,PtrToStructure 读取的也是指针的指针 对于 UnmanagedType.ByValTStr,Alloc 的是指针,PtrToStructure 读取的也是指针

上下 2 组,上面是 LPUTF8Str 的测试结果,下面是 ByValTStr 的。 每一组第一行是 Marshal.AllocHGlobal 的返回值,第二行是 Marshal.ReadIntPtr(前面的返回值)的值

两部分只有 MarshalAs(UnmanagedType...)的定义不同,其他部分完全一样。 究竟这里是怎么回事…

完整的测试代码在这里 https://pastebin.com/J5cUmyfm

此外,这个特性似乎还跟.net core 版本有关…2.1 之前的版本在 Console.WriteLine(utf8Struct.Value)时候甚至会乱码,像这样: 3.0 之后直到 5.0 则都可以正常显示。这又是个什么奇怪的问题…

多谢各位大佬的赐教!

1885 次点击
所在节点    C#
2 条回复
geelaw
2020-12-06 07:27:46 +08:00
如果是 LPUTF8Str,对应的非托管字段是 byte *,如果是 ByValTStr 、结构是 Unicode 、设置的 SizeConst 为 30,则对应的非托管字段是 30 个 ushort 。两个结构的非托管形式完全不同。

AllocHGlobal 出来的是一段定长内存的指针,至于怎么解读是代码的事儿。

早期 .NET 在取消封送 LPUTF8Str 时有 bug,见 https://github.com/dotnet/runtime/issues/11968

最后你的代码有内存泄露,在 FreeHGlobal 之前你应该 DestroyStructure,否则运行时不可能知道封送入 structPtr 而产生的 UTF-8 字符串需要释放。
natsukage
2020-12-06 20:02:46 +08:00
@geelaw 解答得非常清楚,万分感谢!
因为之前版本( 2.1 以前),有一段内存结构是固定长度 30 的 utf8 编码字符串,正好可以被 PtrToStructure 按照 LPUTF8Str 进行解析,因此陷入了误区,没有意识到其实 LPUTF8Str 的结构其实应该是个字符数组指针。
而在 2.2 版本修复这个问题后,这么做则会抛出 AccessViolationException,因此才产生了问题。
看到解答才知道原来以前这部分能正常工作其实是歪打正着 OTL

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/732560

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX