3

我在 VS2005 .NET Framework 2.0 中创建的 WinForms 项目存在问题,我刚刚升级到 VS2012 .NET Framework 4.5。在我的项目中,我使用了第三方 DLLDllImport并使用了它的功能,因为我拥有它们的所有文档。

问题是导入的 DLL 中在 VS2005 .NET Framework 2.0 中运行良好的函数之一在 VS2012 .NET 4.5 中不起作用。

以下是我的项目中的代码片段:

[DllImport("W5EditLD.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "K5GetClassName")]
public static extern string GetClassName();//Dll import definition

public string _GetClassName()
{
    return GetClassName();//wrapper function to DLL import function 
}

string sClassName = _GetClassName();//where i call API via wrapper method,**

上面的代码片段在 VS2005 .NET Framework 2.0 中运行良好但是当我将我的项目升级到 VS2012 .NET Framework 4.5 时,我必须按照以下方式进行:

[DllImport("W5EditLD.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "K5GetClassName")]

public static extern IntPtr GetClassName();//Dll import definition

public IntPtr _GetClassName()
{
    return GetClassName();//wrapper function to DLL import function 
}

IntPtr ptr = _GetClassName();//where i call API via wrapper method,    
string sClassName = System.Runtime.InteropServices.Marshal. PtrToStringAnsi(ptr);

这是为什么?VS2012 .NET Framework 4.5 不支持自动字符串编组吗?

4

2 回答 2

6

考虑您的原始 p/invoke:

[DllImport(...)]
public static extern string GetClassName();

编组器对返回值的处理是关键。这被编组为 C 字符串,即指向以空字符结尾的字符数组的指针。因为数据是从本机到托管的,并且没有在托管代码中分配,所以框架假定它不负责释放它。本机代码无法释放它,因为它不再执行。

因此,策略是 p/invoke 编组器假定字符数组是在共享 COM 堆上分配的。所以它调用CoTaskMemFree. 我很确定该数组未在共享 COM 堆上分配。所以你的代码总是被破坏。在旧版本的 .net 中,调用CoTaskMemFree碰巧以静默方式失败。在最新版本中,它因错误而失败。我不确定更改是在 .net 框架中还是在底层平台中,但这无关紧要,因为原始代码到处都被破坏了。

.net 4.5 中支持自动字符串编组的方式与以前版本完全相同。但你必须做对。如果要使用string默认编组的返回值,请在 COM 堆上分配字符数组并调用CoTaskMemAlloc.

如果返回的字符串实际上是静态分配的,并且不需要解除分配,那么您有两个明显的选择:

  1. 在托管代码中,切换到使用IntPtrand PtrToStringAnsi。这对您来说很容易做到,因为您将调用移动到包装器PtrToStringAnsi内部_GetClassName并呈现与以前相同的公共接口。
  2. 在本机代码中,继续调用CoTaskMemAlloc,然后将静态缓冲区复制到该堆分配的缓冲区中。
于 2013-08-29T09:58:03.753 回答
2

这与框架无关,与 Windows 版本有关。您的旧 2.0 项目可能在 XP 上运行。我们知道您现在有了更新的操作系统,因为 4.5 不适用于 XP。

XP 有一个更宽松的堆管理器,当 pinvoke 编组器调用 CoTaskMemFree() 以释放字符串并忽略无效指针时,它只是耸耸肩。从 Vista 开始,它不再耸耸肩并抛出 AccessViolation,从而使行为不端的程序摆脱困境。这种严肃的态度是 Vista 名声如此糟糕的原因之一。现在认为这是正常的,因为程序员有足够的时间来修复程序中的错误。

您找到的解决方法是正确的,编组器不会尝试释放 IntPtr 的内存。const char*请注意,只有当 C 代码返回不需要发布的 a 时,这才会真正结束。如果不是这种情况,您将发生永久性内存泄漏。

于 2013-08-29T13:08:10.623 回答