我应该如何编写正确的 COM 代码?
下面的代码正确地释放了所有内容,但看起来很糟糕。我应该如何编写不会泄漏引用且可读的 COM 代码?
忽略 C++\CLI 位。
IBaseFilter* leftFilter;
if (graph->FindFilterByName(L"Left", &leftFilter) == S_OK)
{
IPin* leftIn;
if (leftFilter->FindPin(L"Audio Input pin (rendered)", &leftIn) == S_OK)
{
IBaseFilter* meterFilter;
if (graph->FindFilterByName(L"Meter", &meterFilter) == S_OK)
{
IPin* meterIn;
if (meterFilter->FindPin(L"Input", &meterIn) == S_OK)
{
IPin* meterOut;
if (meterFilter->FindPin(L"Output", &meterOut) == S_OK)
{
try
{
IntPtr fileName = Marshal::StringToCoTaskMemUni(source->OriginalString);
IBaseFilter* sourceFilter;
if (graph->AddSourceFilter((LPCWSTR)(void*)fileName, L"Source", &sourceFilter) == S_OK)
{
IPin* sourceOut;
if (sourceFilter->FindPin(L"Output", &sourceOut) == S_OK)
{
if (graph->Connect(sourceOut, meterIn) == S_OK)
{
IBaseFilter* rightFilter;
if (graph->FindFilterByName(L"Right", &rightFilter) == S_OK)
{
IPin* rightIn;
if (rightFilter->FindPin(L"Audio Input pin (rendered)", &rightIn) == S_OK)
{
IBaseFilter* splitFilter;
if (graph->FindFilterByName(L"Split", &splitFilter) == S_OK)
{
IPin* splitIn;
if (splitFilter->FindPin(L"Input", &splitIn) == S_OK)
{
IPin* splitLeft;
if (splitFilter->FindPin(L"Left", &splitLeft) == S_OK)
{
IPin* splitRight;
if (splitFilter->FindPin(L"Right", &splitRight) == S_OK)
{
if (graph->ConnectDirect(meterOut, splitIn, NULL) == S_OK
&& graph->ConnectDirect(splitLeft, leftIn, NULL) == S_OK
&& graph->ConnectDirect(splitRight, rightIn, NULL) == S_OK)
{
this->source = source;
OnMediaOpened(EventArgs::Empty);
}
splitRight->Release();
}
splitLeft->Release();
}
splitIn->Release();
}
splitFilter->Release();
}
rightIn->Release();
}
rightFilter->Release();
}
else
{
if (graph->ConnectDirect(meterOut, leftIn, NULL) == S_OK)
{
this->source = source;
OnMediaOpened(EventArgs::Empty);
}
}
}
sourceOut->Release();
}
sourceFilter->Release();
}
Marshal::FreeCoTaskMem(fileName);
}
catch (Exception^) { }
meterOut->Release();
}
meterIn->Release();
}
meterFilter->Release();
}
leftIn->Release();
}
leftFilter->Release();
}
The following code releases everything correctly, but it looks awful. How am I supposed to write COM code that doesn't leak references and is readable?
Ignore the C++\CLI bits.
IBaseFilter* leftFilter;
if (graph->FindFilterByName(L"Left", &leftFilter) == S_OK)
{
IPin* leftIn;
if (leftFilter->FindPin(L"Audio Input pin (rendered)", &leftIn) == S_OK)
{
IBaseFilter* meterFilter;
if (graph->FindFilterByName(L"Meter", &meterFilter) == S_OK)
{
IPin* meterIn;
if (meterFilter->FindPin(L"Input", &meterIn) == S_OK)
{
IPin* meterOut;
if (meterFilter->FindPin(L"Output", &meterOut) == S_OK)
{
try
{
IntPtr fileName = Marshal::StringToCoTaskMemUni(source->OriginalString);
IBaseFilter* sourceFilter;
if (graph->AddSourceFilter((LPCWSTR)(void*)fileName, L"Source", &sourceFilter) == S_OK)
{
IPin* sourceOut;
if (sourceFilter->FindPin(L"Output", &sourceOut) == S_OK)
{
if (graph->Connect(sourceOut, meterIn) == S_OK)
{
IBaseFilter* rightFilter;
if (graph->FindFilterByName(L"Right", &rightFilter) == S_OK)
{
IPin* rightIn;
if (rightFilter->FindPin(L"Audio Input pin (rendered)", &rightIn) == S_OK)
{
IBaseFilter* splitFilter;
if (graph->FindFilterByName(L"Split", &splitFilter) == S_OK)
{
IPin* splitIn;
if (splitFilter->FindPin(L"Input", &splitIn) == S_OK)
{
IPin* splitLeft;
if (splitFilter->FindPin(L"Left", &splitLeft) == S_OK)
{
IPin* splitRight;
if (splitFilter->FindPin(L"Right", &splitRight) == S_OK)
{
if (graph->ConnectDirect(meterOut, splitIn, NULL) == S_OK
&& graph->ConnectDirect(splitLeft, leftIn, NULL) == S_OK
&& graph->ConnectDirect(splitRight, rightIn, NULL) == S_OK)
{
this->source = source;
OnMediaOpened(EventArgs::Empty);
}
splitRight->Release();
}
splitLeft->Release();
}
splitIn->Release();
}
splitFilter->Release();
}
rightIn->Release();
}
rightFilter->Release();
}
else
{
if (graph->ConnectDirect(meterOut, leftIn, NULL) == S_OK)
{
this->source = source;
OnMediaOpened(EventArgs::Empty);
}
}
}
sourceOut->Release();
}
sourceFilter->Release();
}
Marshal::FreeCoTaskMem(fileName);
}
catch (Exception^) { }
meterOut->Release();
}
meterIn->Release();
}
meterFilter->Release();
}
leftIn->Release();
}
leftFilter->Release();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用智能指针,例如 CComPtr。 有关 CComPtr 的 MSDN 文档 有一个示例,展示了 CComPtr 如何使事情变得更轻松与手动生命周期管理相比。
Use a smart pointer, such as CComPtr. The MSDN documentation on CComPtr has an example that shows how CComPtr makes things easier compared to manual lifetime management.
使用智能指针来释放所有的Release(),然后使用失败而不是成功作为条件。
当然,这可以重构为您最喜欢的内联函数或宏。
Use a smart pointer to chuck all the
Release()
s, and then use failure, not success, as the condition.Of course, this can be refactored into your favourite inline function or macro.
您可以使用某种智能指针,它会在析构函数中为您调用 Release。例如,CComPtr 或 Boost 的 侵入性指针。
You can use some sort of smart pointer that will call Release for you in the destructor. For instance, CComPtr, or Boost's intrusive pointer.
使用boost::intrusive_ptr可以摆脱所有对Release的调用。除了让函数抛出异常之外,我想不出其他方法来删除所有这些嵌套的 if 。考虑使用一些检查
S_OK
或throw
的内容来包围您的函数调用。Using
boost::intrusive_ptr
would get ride of all those calls toRelease
. I can't think of another way to remove all those nestedif
s but by having functions throw exceptions. Consider surrounding your function calls with something that checks againstS_OK
or elsethrow
s.