我无法在 Windows Server 2003 x64(这是我目前使用的唯一 64 位环境)上使 C# 演示崩溃,但代码有问题,因此您看到意外行为是有道理的。
编辑:使用原始代码重现了 Windows Server 2008 R2 x64 中的崩溃,并验证了修复的有效性。
正如 Christian.K 所指出的,这个问题之前已经注意到了。调用该方法时,仅当指定的内存块不包含有效数据时才Marshal.StructureToPtr
应传递true
第三个fDeleteOld
参数。这在文档中非常明确地指出,所以我不确定原作者是如何弄错的。
在这种情况下,由于数据只是通过调用前一行分配的Marshal.AllocHGlobal
,它不包含有效数据,不应该被删除/释放。更改很简单:将第三个参数更改true
为false
. 不幸的是,由于互操作代码分散在示例项目中的三个不同类中,您必须在多个地方进行更改。您正在寻找的模式是这样的:
IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ti));
Marshal.StructureToPtr(ti, ptrStruct, false /* <-- change this from true to false */);
正如一般观察:代码尝试手动处理许多互操作内容(通过使用Marshal
类的方法),而不是让 CLR 自动处理它。我更喜欢后一种方法。尽管我完全了解如何手动完成所有互操作,但让系统为我管理它可以减少我犯的错误数量和导致堆损坏的次数。
RhysW说他以前从未遇到过堆损坏,但是当您开始在 .NET 代码和 Win32 API 之间进行互操作时,它变得非常普遍。.NET Framework 不再保护您。
对于我的意思的一个例子,请注意该FMSBalloonTip.SetToolTip
方法使用该Marshal.StringToHGlobalAuto
方法来编组包含工具提示标题的字符串作为指针。SendMessage
虽然这确实有效(谢天谢地,作者在完成后很小心地释放了指针),但声明接受string
对象作为第四个参数的函数的重载会容易得多,也不容易出错。这样,框架将为您透明地处理所有必要的互操作内容。
当然,真正的问题是为什么你需要这段代码。使用从一开始就可用的内置类ToolTip
要容易得多。我不确定您是否只是没有提及您需要的某些ToolTip
未提供的功能,或者您只是不知道,但我强烈建议您重新考虑您的设计,以便您可以使用内置类并让微软程序员处理所有互操作的东西。
如果它是您要查找的气球部分,请确保设置类的IsBalloon
属性ToolTip
。这直到 .NET 2.0 才引入,但这与示例项目的目标版本相同。