我应该如何编写正确的 COM 代码?

发布于 2024-12-05 10:29:15 字数 5102 浏览 1 评论 0原文

下面的代码正确地释放了所有内容,但看起来很糟糕。我应该如何编写不会泄漏引用且可读的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

忘羡 2024-12-12 10:29:15

使用智能指针,例如 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.

臻嫒无言 2024-12-12 10:29:15

使用智能指针来释放所有的Release(),然后使用失败而不是成功作为条件。

if (graph->FindFilterByName(L"Left", &leftFilter) != S_OK)
    throw ...;

当然,这可以重构为您最喜欢的内联函数或宏。

Use a smart pointer to chuck all the Release()s, and then use failure, not success, as the condition.

if (graph->FindFilterByName(L"Left", &leftFilter) != S_OK)
    throw ...;

Of course, this can be refactored into your favourite inline function or macro.

行至春深 2024-12-12 10:29:15

您可以使用某种智能指针,它会在析构函数中为您调用 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.

酒废 2024-12-12 10:29:15

使用boost::intrusive_ptr可以摆脱所有对Release的调用。除了让函数抛出异常之外,我想不出其他方法来删除所有这些嵌套的 if 。考虑使用一些检查 S_OKthrow 的内容来包围您的函数调用。

Using boost::intrusive_ptr would get ride of all those calls to Release. I can't think of another way to remove all those nested ifs but by having functions throw exceptions. Consider surrounding your function calls with something that checks against S_OK or else throws.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文