【问题标题】:Do I need to call Marshal.DestroyStructure when marshalling structures containing ByValTStr strings编组包含 ByValTStr 字符串的结构时是否需要调用 Marshal.DestroyStructure
【发布时间】:2021-04-17 02:59:35
【问题描述】:

我正在为从 C#/.NET 到非托管 DLL 的互操作做一些手动封送处理。

考虑以下结构:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct LockInfo
{
    ushort lockVersion;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string lockName;
}

我将其编组到非托管内存:

var lockInfo = new LockInfo();
var lockInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lockInfo));
Marshal.StructureToPtr(lockInfo, lockInfoPtr, false);

完成后,是否需要在lockInfoPtr 上致电Marshal.DestroyStructure

我知道需要致电Marshal.FreeHGlobal,但在此之前,在这种情况下是否真的需要Marshal.DestroyStructure

我发现很难理解微软的documentation 围绕这个问题。 Google 搜索没有帮助,可能是因为我还不太了解编组(但我正在学习)。

类似问题...

我已经查看了类似的问题“Marshal.DestroyStructure vs Marshal.FreeHGlobal in .Net”,但是这个问题没有解决 content 结构应该包含的问题,该结构需要使用 DestroyStructure。我有限的理解是 DestroyStructure 并不总是需要调用,只有当结构包含某些类型的字段时。在我的情况下,我不确定一个被编组为 ByValTStr 的字符串是否需要使用 DestroyStructure

【问题讨论】:

  • @CoolBots 不幸的是没有。我已参考该问题和解释更新了我的问题。
  • 根据您的解释,我撤回了作为重复关闭的投票。我的理解是你需要调用Marshal.DestroyStructure,因为它是一个引用类型string。不过,我不是 100% 的。我也不确定在不需要调用此方法的情况下调用此方法是否有害。我将把实际发布答案的任务留给更精通此事的人。我同意你的观点,文档并不完全清楚。
  • 您可能会发现这些测试很有用(第 1 到第 3 部分):Passing a Pointer to a Structure from C# to C++ Part 1。在第 2 部分之后,您可能会认为需要调用该方法。完成第 3 部分(注意使用了哪些类型:现在您需要清楚地了解在这种情况下 blittable 和 non-blittable 类型之间的区别,以及为什么提到 SafeArrays 和 COM BSTR)。可能的话,自己做一个测试(之后你可能会得到不同的结论,但你知道为什么)。
  • @Jimi 谢谢你的链接 - 我怎么还没找到这个。几乎我需要知道的一切都在那里。我会彻底研究它,如果几天后这个问题仍然没有答案,我会根据我的学习提供答案。
  • ByValTStr + SizeConst 是预分配的(lockInfoPtr 指向的内存的一部分),所以不,您不需要调用 Marshal.DestroyStructure。实际上这种方法很少(或应该)很少使用,这取决于您的原生端,或者您希望如何在托管和原生之间进行协作。如有疑问,请在循环中运行它,有时调用 GC.Collect,并检查内存(Perfmon 的私有字节)是否随时间稳定。

标签: c# .net winapi marshalling


【解决方案1】:

编组是复杂的东西,一个完整的答案可能会写满一本书的一整章,这在这里当然不合适。所以,简而言之:

通常,当从托管代码调用本机函数时,.NET 会将字符串和数组编组为本机 BSTR 字符串和 SafeArray 数组。

为此,封送拆收器分别调用SysAllocStringSafeArrayCreate

在某些时候,当不再需要这些本机端字符串和数组时,编组器将分别调用SysFreeStringSafeArrayDestroy 来释放内存。

如果您接管 .NET 的自动封送处理,并调用像 Marshal.StructureToPtr 这样的方法来手动封送结构,将负责释放/销毁那些本机端 BSTR 和 SafeArray。这正是Marshal.DestroyStructure 的用途。

不过……

通过将[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 属性添加到您的字符串字段,您指示编组器将字符串编组为BSTR 字符串,而是在本地分配一个固定长度的字符数组-侧面结构本身。

既然如此,就不需要调用Marshal.DestroyStructure,因为没有BSTR 字符串可以释放。当然,您仍然需要致电Marshal.FreeHGlobal,我知道您已经意识到这一点。

感谢 @SimonMourier 的评论,他的评论让一切都得到了点击。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 1970-01-01
    • 2011-06-01
    • 2020-04-20
    相关资源
    最近更新 更多