2013-08-29 32 views
3

我在我刚刚升级到VS2012 .NET Framework 4.5的VS2005 .NET Framework 2.0中创建的WinForms项目存在问题。在我的项目中,我使用了DllImport的第三方DLL,并使用了它的功能,因为我有他们的所有文档。C#,DLL导入API在VS2012中无法正常工作.NET Framework 4.5中

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

+0

你说你“有”以不同的方式做到这一点。为什么?如果您尝试以相同的方式执行操作,您是否收到错误消息?如果是这样,那么错误信息是什么?如果不是,那么是什么导致你断定你“有”以不同的方式去做? –

+0

尝试将CharSet定义添加到DllImport。顺便说一句,你没有写,究竟是“不工作” - 崩溃,不正确的结果? –

+0

@AlexFarber没有CharSet定义,默认是ANSI。很明显,返回的文本是使用'PtrToStringAnsi'的ANSI文件。 –

回答

6

考虑你的原始的P/Invoke:返回值的

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

编组的治疗是关键。这是编组为C字符串,即指向以空字符结尾的字符数组的指针。由于数据是从本地到托管的,并未在托管代码中分配,因此框架假定它不负责解除分配。本机代码不能释放它,因为它不再执行。

因此,策略是p/invoke编组人员假定字符数组是在共享COM堆上分配的。所以它叫CoTaskMemFree。我很确定数组没有被分配到共享的COM堆上。所以你的代码总是被破坏。在早期的.net版本中,CoTaskMemFree的调用恰好失败了。在最新版本中,它出现错误。我不确定这个改变是在.net框架还是在底层平台上,但是由于原始代码在任何地方都被破坏,这一点很重要。

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

如果返回的字符串,其实是静态分配的,并不需要释放你有两个明显的选择:

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

OMG,这意味着世界上90%的互操作代码是以错误的方式编写的。有没有关于这个的一些微软文档?现在,我发现这个:http://blogs.msdn.com/b/dsvc/archive/2009/06/22/troubleshooting-pinvoke-related-issues.aspx,这只是博客。 –

+0

@AlexFarber也许在某处有一些文档,但是Hans Passant教了我这个事实。你可以找到数百万SO帖子,说出我在这里说的话。 –

+0

没有问题,如果这来自Hans Passant ... –

2

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

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

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

+0

谢谢先生,对我有帮助的是,你与我分享非常有用的信息,肯定会增加我的知识基础。 –

相关问题