使用 MarshalAs(UnmanagedType.LPWStr) 会清理内存吗?
我正在尝试为 ITaskTrigger::GetTriggerString 方法编写一个 pinvoke (定义于 http://msdn.microsoft.com/en-us/library/windows/desktop/aa381866(v=vs.85).aspx)。如果您查看该页面,它会显示该方法的调用者负责释放通过第一个参数引用的 LPSTR 的内存(通过 CoTaskMemFree)。虽然我可以在 .NET 中手动执行此操作,或者可以使用 ICustomMarshaler 编写自定义封送拆收器,但我想知道对该特定参数使用 MarshalAs(UnmanagedType.LPStr) 属性是否会适当释放内存。
任何人都可以提供一些见解吗?
I'm trying to write a pinvoke for the ITaskTrigger::GetTriggerString method (defined at http://msdn.microsoft.com/en-us/library/windows/desktop/aa381866(v=vs.85).aspx). If you look at the page, it says that the caller of the method is responsible for freeing the memory (via CoTaskMemFree) of the LPSTR referenced via the first argument. While I could do that manually in .NET or could write my custom marshaler using ICustomMarshaler, I was wondering if using the MarshalAs(UnmanagedType.LPStr) attribute for that particular argument will free the memory appropriately.
Can anyone provide some insight?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您要讨论的是 COM Interop(
ITaskTrigger
是 COM 接口),而不是 P/Invoke。两者有不同的互操作规则,因此保持它们的一致性很重要。例如,您需要为整个接口定义 C# 互操作包装器,而不仅仅是您想要的方法。这些应该可以帮助您入门: pinvoke.net简短的答案是,您很幸运,因为 CLR 应该为您妥善处理事情。
较长的答案涉及 COM 互操作代码执行的不同类型的编组,具体取决于参数类型、方向以及添加到互操作签名中的属性。
在这种情况下,您在调用中获得的参数类型是“
out string
”参数,带有MarshalAs(UnmanagedType.LPWSTR)
属性。当 COM 服务器公开具有LPWSTR
字符串类型的“out”参数的调用时,假设服务器正在完成其处理,它将使用CoTaskMemAlloc( )
并将其返回给您。 (如果是不同的字符串类型,例如 BSTR,具体的内存分配调用可能会有所不同,但基本概念是相同的。)此时,您负责清理该内存当您不再需要它时,使用匹配的CoTaskMemFree()
调用。这是一种称为“引用更改”的特殊操作类型:您发送的参数已经是引用参数,但 COM 服务器将替换它为不同的 em>参考。有关此过程的一个很好的解释可以在此内容的“内存所有权”部分中找到MSDN 杂志文章。正如您从该文章中看到的,当 CLR 从引用类型的“out”参数接收回数据时,它会认识到它正在负责释放该内存。在封送该回调到托管代码时,它使用
MarshalAs
属性来确定这是 COM 中的LPWSTR
字符串类型指针,因此应该已对其进行分配使用CoTaskMemAlloc()。从数据中创建托管字符串后,它将代表您在原始缓冲区上调用CoTaskMemFree()
。您取回的数据将得到全面管理,您无需处理任何所有权问题。First things first: you're talking about COM Interop here (
ITaskTrigger
is a COM interface), not P/Invoke. There are different interop rules for the two, so it's important to keep them straight. For example, you'll need to define C# interop wrappers for the entire interface, not just the method you want. These should get you started: pinvoke.netThe short answer is, you're in luck, becase the CLR should take care of things properly for you.
The longer answer involves the different types of marshalling the COM interop code does, depending on the parameter types, directions, and what attributes you add to your interop signatures.
In this case, the parameter type you will get on the call is an "
out string
" parameter, with aMarshalAs(UnmanagedType.LPWSTR)
attribute. When a COM server exposes a call that has an "out" parameter ofLPWSTR
string type, assuming the server is keeping up its end of the deal, it will allocate a memory buffer withCoTaskMemAlloc()
and return it to you. (If it was a different string type, like aBSTR
, the specific memory allocation call might be different, but the basic concept is the same.) At this point, you are responsible for cleaning up that memory when you no longer need it, using the matchingCoTaskMemFree()
call.This is a special type of operation called a "reference change": the parameter you are sending in is already a reference parameter, but the COM server is going to replace it with a different reference. A good explanation for this process is found in the "Memory Ownership" section of this MSDN magazine article. As you can see from that article, when the CLR receives data back from an "out" parameter on a reference type, it recognizes that it is taking responsibility for freeing that memory. While marshaling that call back to managed code, it uses the
MarshalAs
attribute to determine that this is aLPWSTR
string-type pointer in COM, and that it should therefore have been allocated usingCoTaskMemAlloc()
. After creating a managed string out of the data, it will callCoTaskMemFree()
on the original buffer on your behalf. The data you get back will be fully managed and you won't have to deal with any ownership problems.