V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
natsukage
V2EX  ›  C#

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

  •  
  •   natsukage · 2020-12-06 05:48:50 +08:00 · 1844 次点击
    这是一个创建于 1209 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    B53Y32_51V_06S`E~~7NEFI.png

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

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

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

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

    多谢各位大佬的赐教!

    2 条回复    2020-12-06 20:02:46 +08:00
    geelaw
        1
    geelaw  
       2020-12-06 07:27:46 +08:00 via iPhone   ❤️ 1
    如果是 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
        2
    natsukage  
    OP
       2020-12-06 20:02:46 +08:00
    @geelaw 解答得非常清楚,万分感谢!
    因为之前版本( 2.1 以前),有一段内存结构是固定长度 30 的 utf8 编码字符串,正好可以被 PtrToStructure 按照 LPUTF8Str 进行解析,因此陷入了误区,没有意识到其实 LPUTF8Str 的结构其实应该是个字符数组指针。
    而在 2.2 版本修复这个问题后,这么做则会抛出 AccessViolationException,因此才产生了问题。
    看到解答才知道原来以前这部分能正常工作其实是歪打正着 OTL
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1431 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 23:48 · PVG 07:48 · LAX 16:48 · JFK 19:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.