COM内存管理
我有一些关于 COM 内存管理的问题:
我有一个 COM 方法:
STDMETHODIMP CWhitelistPolicy::GetWebsitesStrings(SAFEARRAY** result)
result = SAFEARRAY(BSTR)。如果我从另一个接口方法收到另一个 SAFEARRAY(BSTR)(为了设置 *结果),我是否必须复制收到的字符串才能将它们传递给 *结果和外部客户端?或者考虑到我不会自己使用字符串,我可以将它们传递给客户(并传递所有权)?
2.
STDMETHODIMP CWhitelistPolicy::SetWebsitesStrings(SAFEARRAY* input)
这里我收到一个 BSTR 数组作为输入。我的方法再次负责输入中分配的内存吗?
3.
STDMETHOD(SetUsers)(SAFEARRAY* input);
在这里,我调用另一个接口 (SetUsers) 上的方法,并为输入 SAFEARRAY 分配内存。在我调用 SetUsers 后,我可以处置 SAFEARRAY 吗?当进行编组时,内存总是被复制,不是吗? (在我的例子中,SetUsers 方法是在我的进程内作为 COM DLL 托管的接口上调用的)
I have some questions regarding COM memory management:
I have a COM method:
STDMETHODIMP CWhitelistPolicy::GetWebsitesStrings(SAFEARRAY** result)
result = SAFEARRAY(BSTR). If I receive another SAFEARRAY(BSTR) from another interface method(in order to set *result) do I have to make copies of the strings received in order to pass them to *result and outside client? Or considering I will not use the strings for myself I can just pass them to the client (and passing out the ownership)?
2.
STDMETHODIMP CWhitelistPolicy::SetWebsitesStrings(SAFEARRAY* input)
Here I receive a BSTR array as input. Again my method is responsible for the memory allocated in input?
3.
STDMETHOD(SetUsers)(SAFEARRAY* input);
Here I call a method on another interface (SetUsers) and I allocate memory for the input SAFEARRAY. After I call SetUsers I can dispose of the SAFEARRAY? Memory is always copied when marshaling takes place isn't it? (in my case SetUsers method is called on an interface that is hosted as a COM DLL inside my process)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我思考回答此类问题的方式是考虑跨机器的 COM 调用。那么 [out] 参数就很明显了;我是调用者拥有的内存,并且必须释放内存,因为远程封送层无法执行此操作。对于 [in] 参数,很明显编组层必须复制我的数据,并且远程编组层无法释放我传入的内容。COM
的核心原则是位置中立性,在同一单元中调用时的规则就是规则跨机器使用 DCOM 时。
您有责任释放 - 当您调用下一个 fnc 时,您不会传递所有权,因为它可能是远程的并获取副本,而不是您的原始数据。
不 - 作为被调用者,您不必释放它。如果是在公寓内,则它是调用者提供的内存,并且调用者必须释放它。如果是远程调用,服务器存根会分配它,并在方法返回时释放它。
是的,你释放了它 - 不,它并不总是被复制(可能是),这就是为什么 2 的答案是否定的。如果它被复制,就会有一个分配的存根,并且存根将释放它。
请注意,我对您问题的回答并未涵盖 [in,out] 参数的情况 - 请参阅问题 谁拥有退回的 BSTR? 了解有关此案例的更多详细信息。
Com 分配规则复杂但合理。如果您想了解/查看所有案例的示例,请获取 Don Box 所著的《essential com》一书。但你还是会犯错误,所以你应该制定一个策略来检测它们。我使用 gflags ( Windbg) 及其堆检查标志,以在发生双重释放时进行捕获(显示调试消息并在使用 INT 3 进行调用时停止执行)。 Vstudio 的调试器过去常常在启动可执行文件时为您打开它们(很可能仍然如此),但您可以使用图像选项选项卡下的 gflags 强制打开它们。
您还应该知道如何使用 UMDH(也是windbg的一部分)来检测泄漏。 DebugDiag 是用于此目的的较新工具,似乎更容易使用,但遗憾的是,您只能安装 32 位或 64 位版本,而不能同时安装两者。
问题是 BSTR 被缓存,使得检测双重释放和泄漏变得棘手,因为与堆的交互被延迟。您可以通过设置环境变量 OANOCACHE 为 1 或调用函数 SetOaNoCache。该函数未在头文件中定义,因此请参阅此问题Where is SetOaNoCache Defined?。请注意,接受的答案显示了通过 GetProcAddress() 调用它的困难方法。接受的答案下面的答案显示您所需要的只是一个 extern“C”,因为它位于 oleaut32 导出库中。最后,请参阅这篇 Larry Osterman 博客文章有关查找泄漏时缓存造成的困难的更详细描述。
The way I think about it to answer questions like this is to think about a COM call that crosses machines. Then it's obvious for an [out] param; I the caller own and have to free the memory because the remote marshaling layer can't do it. For [in] parameters, it's obvious the marshaling layer must copy my data and again the remote marshaling layer can't free what I passed in.
A core tenet in COM is location neutrality, the rules when calling in the same apartment are the rules when using DCOM across machines.
You're responsible to free - you don't pass ownership when you call the next fnc because it could be remote and getting a copy, not your original data.
No - as the callee, you don't have to free it. If it's intra-apartment, it's the memory the caller provided and the caller has to free it. If it's a remote call, the server stub allocates it and will free it when the method returns.
Yes, you free it - no, it's not always copied (it might be), which is why the answer to 2 is no. If it's copied, there's a stub that allocated and the stub will free it.
Note my answers to your questions didn't cover the case of [in,out] parameters - see the so question Who owns returned BSTR? for some more details on this case.
Com allocation rules are complicated but rational. Get the book "essential com" by Don Box if you want to understand/see examples of all the cases. Still you're going to make mistakes so you should have a strategy for detecting them. I use gflags (part of Windbg) and its heap checking flags to catch when a double free occurs (a debug message is displayed and execution halted at the call with an INT 3). Vstudio's debugger used to turn them on for you when it launched the executable (it likely still does) but you can force them on with gflags under the image options tab.
You should also know how to use UMDH (also part of windbg) to detect leaks. DebugDiag is the newer tool for this and seems easier to use, but sadly, you can only have the 32 bit or 64 bit version installed, but not both.
The problem then are BSTRs, which are cached, make detecting double frees and leaks tricky because interacting with the heap is delayed. You can shut off the ole string cache by setting the environment variable OANOCACHE to 1 or calling the function SetOaNoCache. The function's not defined in a header file so see this SO question Where is SetOaNoCache defined?. Note the accepted answer shows the hard way to call it through GetProcAddress(). The answer below the accepted one shows all you need is an extern "C" as it's in the oleaut32 export lib. Finally, see this Larry Osterman blog post for a more detailed description of the difficulties caused by the cache when hunting leaks.