C#,DLL导入API在VS2012 .NET Framework 4.5中无法正常工作

时间:2013-08-29 09:49:49

标签: c# visual-studio-2012 pinvoke

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

问题是导入的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中是否不支持自动字符串编组?

2 个答案:

答案 0 :(得分:6)

考虑你原来的p / invoke:

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

编组人员对返回值的处理是关键。这被编组为一个C字符串,它是一个指向以null结尾的字符数组的指针。由于数据来自本机到托管,并且未在托管代码中分配,因此框架假定它不负责解除分配。本机代码无法解除分配,因为它不再执行。

因此,策略是p / invoke marshaller假定字符数组是在共享COM堆上分配的。所以它调用了CoTaskMemFree。我很确定该数组未在共享COM堆上分配。所以你的代码总是坏了。在旧版本的.net中,对CoTaskMemFree的调用碰巧无声地失败。在最新版本中,它失败并出现错误。我不确定更改是在.net框架中还是在底层平台上,但这很重要,因为原始代码在任何地方都被破坏了。

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

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

  1. 在托管代码中,切换为使用IntPtrPtrToStringAnsi。这很容易为您做,因为您将调用移至PtrToStringAnsi封装器内的_GetClassName并显示与以前相同的公共接口。
  2. 在本机代码中,继续调用CoTaskMemAlloc,然后将静态缓冲区复制到该堆分配的缓冲区中。

答案 1 :(得分:2)

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

XP有一个更宽松的堆管理器,当pinvoke marshaller调用CoTaskMemFree()释放字符串并忽略无效指针时,它只是耸了耸肩。从Vista开始,它不再耸耸肩并抛出AccessViolation,因此行为不端的程序会从他们的痛苦中解脱出来。这种严肃的态度是Vista得名的原因之一。在程序员有足够的时间修复程序中的错误之后,它现在被认为是正常的。

您找到的解决方法是正确的,marshaller不会尝试释放IntPtr的内存。请注意,如果C代码返回不需要释放的const char*,这实际上只会达到一个好的结果。如果情况并非如此,则会发生永久性内存泄漏。