2015-02-05 87 views
1

我使用以下代码从C#调用winapi函数。从C#调用Windows API函数时需要明确释放哪些对象?

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
    public static extern IntPtr PathFindFileName(string p); 

    IntPtr pStr = PathFindFileName("Test"); 
    string str = Marshal.PtrToStringAuto(pStr) 

我想知道如何区分一旦完成需要释放的对象和不需要的对象。

例如:在上面的代码中,我是否需要释放pStr?

还是垃圾收集器自动执行它?

万一我需要释放,它是如何完成的?

谢谢, 迈克尔。

+0

这段代码很糟糕:'pStr'是一个悬挂指针。 – 2015-02-05 15:20:49

+0

@CoryNelson这就是这个人所问的。 – andlabs 2015-02-05 16:42:06

+1

@MichaelEngstler - 对于将来的笔记,您使用的问题标题不太好;更好的一个是“我需要从C#调用Windows API函数时明确释放哪些对象?”或沿着这条线的东西; [阅读本文](http://stackoverflow.com/help/how-to-ask),在“编写一个总结特定问题的标题”下。 (这个问题本身*很好,至少它似乎只是让我醒来) – andlabs 2015-02-05 16:45:29

回答

4

只有这样,才能知道如何处理内存互操作是阅读的文档。

在这种情况下,文档有点弱,并没有明确说明发生了什么。发生的是该函数返回的指针指向您提供的缓冲区中的某个点。这意味着您传递给函数的缓冲区必须在函数返回后才有效,以便能够从中读取。而且,如您使用参数string的自动编组写入p/invoke,传递给非托管函数的缓冲区将不受您的控制。你的代码可能在某些时候似乎工作,但它肯定是坏的,并且可能会因为相当糟糕的运行时错误而失败。

您需要手动封送字符串,以便您可以控制您提供的缓冲区的生存期。

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
public static extern IntPtr PathFindFileName(IntPtr pPath); 

.... 

string path = ...; 
IntPtr pPath = Marshal.StringToHGlobalUni(path); 
try 
{ 
    IntPtr pFileName = PathFindFileName(pPath); 
    string fileName = Marshal.PtrToStringUni(pFileName); 
} 
finally 
{ 
    Marshal.FreeHGlobal(pPath); 
} 

这就是你问的问题的答案。但是,解决问题的最佳方法是避免互操作,并使用内置的.net库功能进行路径操作。

+0

值得一提的是,你需要这样做的原因与原始问题的方式相比,原始文件中由pStr指向的内存可能会在Marshal.PtrToStringAuto返回正确的字符串之前被破坏。看起来它现在可以正常工作,但由于文件名已损坏,未来很可能会出现一些奇怪的错误。 – 2015-02-05 15:55:58

+0

@ Dave76我想我已经在第二段中提到过 – 2015-02-05 15:59:59

+0

你描述了这个问题,但没有具体说明副作用。我认为OP可能不理解使用原始代码的分歧,因为它可能在开发过程中正常工作。我同意你使用.NET Path类是更好的方法。 – 2015-02-05 16:33:12

2

这个特殊的WinAPI函数是一个棘手的函数。返回的指针指向输入缓冲区。这意味着它不被分配,并且不应该由您发布。此外,如果代码正常工作,您就很幸运,因为您需要输入缓冲区为活动状态,直到PtrToStringAuto将其内容复制到受管字符串。

你真的需要调用这个特定的WinAPI函数吗?您可以在.NET Framework库的System.IO.Path类中找到相同的功能。

https://msdn.microsoft.com/en-us/library/system.io.path.aspx

你可能想Path.GetFileName

https://msdn.microsoft.com/en-us/library/system.io.path.getfilename.aspx

+0

感谢您的评论,我注意到了Path.GetFileName,但它与PathFindFileName稍有不同,所以它对我来说不够好。你会如何建议调用这个API?如果我(以某种方式)分配原始字符串并将其自行释放,会更好吗? (为了避免竞争状况) – Michael 2015-02-05 15:13:23

+0

你最初问是否释放内存。答案很简单:不要释放它。当你问如何使用这个函数时,我仍然认为你应该结合Path类的更多功能来模仿你想要的行为,而不是调用WinAPI。 – 2015-02-05 15:16:58

+0

@DavidHeffernan:你说的是错误的。该函数返回的指针指向其他地方分配的内存块的中间。你永远不要使用这个指针来释放内存块。因为它不是一个内存块。只有用作输入参数的内存块必须在这里释放,并且这是通过p/invoke自动完成的。这里的问题是系统可能在用户调用PtrToStringAuto之前释放内存。 – 2015-02-05 16:47:07

相关问题