我该如何清理?

std::wstring LinkResolve::ResolveLink( const std::wstring& source ) const
{
    HRESULT errorCheck;
 wchar_t linkTarget[MAX_PATH];
 wchar_t expandedTarget[MAX_PATH];
 wchar_t arguments[INFOTIPSIZE];
    ATL::CComPtr<IPersistFile> ipf;
    errorCheck = ipf.CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER);
    if (!SUCCEEDED(errorCheck))
    {
        throw _com_error(errorCheck);
    }
    errorCheck = ipf->Load(source.c_str(), 0);
    ATL::CComPtr<IShellLink> shellLink;
    errorCheck = ipf->QueryInterface(&shellLink);
    if (!SUCCEEDED(errorCheck))
    {
        throw _com_error(errorCheck);
    }
    errorCheck = shellLink->Resolve(0, SLR_NO_UI);
    if (!SUCCEEDED(errorCheck))
    {
        throw _com_error(errorCheck);
    }
    errorCheck = shellLink->GetPath(linkTarget, MAX_PATH, 0, SLGP_RAWPATH);
    if (!SUCCEEDED(errorCheck))
    {
        throw _com_error(errorCheck);
    }
    ExpandEnvironmentStringsW(linkTarget, expandedTarget, MAX_PATH);
    errorCheck = shellLink->GetArguments(arguments, INFOTIPSIZE);
    if (SUCCEEDED(errorCheck))
    {
        return std::wstring(expandedTarget) + L" " + arguments;
    }
    else
    {
        return expandedTarget;
    }
}


评论

这让我想到了Haskell的monad,但我认为这不会对您有所帮助。仍然完全可以使用它。

@Tyr:或C ++ 14的可选。 :)

#1 楼

就我个人而言,我将编写一个简单的函数:

void ThrowOnFail( HRESULT hrcode )
{
    if (FAILED(hrcode))
        throw _com_error(hrcode);
}


然后函数调用将变为:

ThrowOnFail( ipf.CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER) );
ThrowOnFail( ipf->Load(source.c_str(), 0) );
ATL::CComPtr<IShellLink> shellLink;
ThrowOnFail( ipf->QueryInterface(&shellLink) );
ThrowOnFail( shellLink->Resolve(0, SLR_NO_UI) );
ThrowOnFail( shellLink->GetPath(linkTarget, MAX_PATH, 0, SLGP_RAWPATH) );


顺便说一句,您在errorCheck之后错过了对Load的支票。使用检查功能可以更容易发现该问题。

评论


\ $ \ begingroup \ $
是否有可能或合理地将整个代码块合并为一个ThrowOnFail(...);包括CComPtr?这也将减少代码重复。对C ++和CComPtr不太熟悉。
\ $ \ endgroup \ $
–WernerCD
2011年1月27日13:58



\ $ \ begingroup \ $
@WernerCD:不,那不可能。 (此外,通过所有这些方法调用,您将拥有500个字符的行!)
\ $ \ endgroup \ $
–比利·奥尼尔(Billy ONeal)
2011年1月27日15:16

\ $ \ begingroup \ $
Err ...您知道这就是为什么我不应该在凌晨3点编写代码的原因:叹气:。
\ $ \ endgroup \ $
–比利·奥尼尔(Billy ONeal)
2011年1月27日15:19



\ $ \ begingroup \ $
问:在此返回的错误是否可以肯定地确定错误的来源?
\ $ \ endgroup \ $
–鹰锯
2011年1月27日17:48

#2 楼

如果我发现自己一遍又一遍地写同样的东西,我通常会将其放在某个地方的功能中。即使您的情况下该函数是如此简单:

void check(HRESULT result) {
    if (FAILED(result)) {
        throw _com_error(result);
    }
}


我认为,如果重用错误检查代码,代码看起来会很简单。我不熟悉您使用的API,因此无法评论是否存在另一种使用它的方法,这可能会导致代码更简洁。

评论


\ $ \ begingroup \ $
我可以要求您将!Succeeded替换为FAILED吗?后者的求值与!SUCCEEDED完全相同。
\ $ \ endgroup \ $
–锋利的牙齿
2011年1月28日下午6:35

#3 楼

至少在使用DirectX时,我使用了宏。

#define D3DCALL(a) { auto __ = a; if (FAILED(__)) DXTrace(__FILE__, __LINE__, __, WIDEN(#a), TRUE); }


您可能会更喜欢并使用带有operator =(HRESULT)的类型进行检查。

评论


\ $ \ begingroup \ $
为什么局部变量在任何地方都保留了名称?我将其命名为_local_D3DCALL(保留在根名称空间范围中,但未在其他位置保留)或类似名称。此宏是调用(内联)函数,传递(a),__ FILE __,__ LINE__和#a的理想选择。
\ $ \ endgroup \ $
– Fred Nurk
2011年1月27日8:27



\ $ \ begingroup \ $
不知道,我写它已经很久了,它只是一个示例。
\ $ \ endgroup \ $
– DeadMG
2011-1-27在11:34

\ $ \ begingroup \ $
一些宏只适合您! ->codereview.stackexchange.com/questions/281/…:)
\ $ \ endgroup \ $
–比利·奥尼尔(Billy ONeal)
2011-1-27的15:51

#4 楼

我记得以前曾经看到过这样的事情:

class XHR
{
public:
   XHR() {};
   ~XHR() {};

   operator HRESULT() { return hr };

   HRESULT& operator =(const HRESULT& rhs)
   {
       hr = rhs;
       if (FAILED(hr)
          throw _com_error(hr);
   }

private:
   HRESULT hr;
}


然后您可以编写:

std::wstring LinkResolve::ResolveLink( const std::wstring& source ) const
{
    XHR errorCheck;   // instead of HRESULT errorCheck

    wchar_t linkTarget[MAX_PATH];
    wchar_t expandedTarget[MAX_PATH];
    wchar_t arguments[INFOTIPSIZE];
    ATL::CComPtr<IPersistFile> ipf;


    errorCheck = ipf.CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER);
    errorCheck = ipf->Load(source.c_str(), 0);

    ATL::CComPtr<IShellLink> shellLink;

    errorCheck = ipf->QueryInterface(&shellLink);
    errorCheck = shellLink->Resolve(0, SLR_NO_UI);
    errorCheck = shellLink->GetPath(linkTarget, MAX_PATH, 0, SLGP_RAWPATH);

    ExpandEnvironmentStringsW(linkTarget, expandedTarget, MAX_PATH);

    try {
       errorCheck = shellLink->GetArguments(arguments, INFOTIPSIZE);
       return std::wstring(expandedTarget) + L" " + arguments;
    }
    catch (const XHR&)
    {
        return expandedTarget;
    }
}


所以随时指示失败的HRESULT,它将为您自动将其转换为_com_error并将其抛出。

评论


\ $ \ begingroup \ $
这很有趣(因此我将为您+1),但是如果您忽略错误检查的类型,则看起来好像您正在忽略错误。我认为,如果您正在查看语句而仅浏览类型,则我倾向于采用一种似乎更明确的方法。
\ $ \ endgroup \ $
–asveikau
2011年6月14日在8:54

#5 楼

错误检查并不完全相同。
有时您会根据返回的特殊HResult采取行动。有时会有ELSE。
有时您想记录错误,有时则不希望。.

也-尽管在com / atl世界中不太可能相关-呼叫函数具有性能成本。

所以我更喜欢在调用后使用if,而不是调用函数。
您节省了多少?输入10个字符?

评论


\ $ \ begingroup \ $
问题中的示例非常清楚,它是一遍又一遍的相同代码。不对其进行功能化只会造成维护麻烦。如果需要以不同的方式处理特定案例,则可以为它编写特定代码。但是以它为借口而不首先编写通用功能是没有效率的,而且IMO不建议这样做。
\ $ \ endgroup \ $
– LRE
11年1月27日在8:24



\ $ \ begingroup \ $
如果是c ++,则始终可以内联设置方法或使用具有相同功能的宏
\ $ \ endgroup \ $
– Pedro Loureiro
2011年1月27日,11:50

\ $ \ begingroup \ $
不要使用宏。您会失去所有类型的安全性。这就是内联函数/方法的原因。
\ $ \ endgroup \ $
– Mark Loeser
2011年1月27日14:06

\ $ \ begingroup \ $
@MarkLoesser-有时,宏是完成任务的正确工具。这是那些时代之一。 FILE和LINE扩展对故障排除非常有帮助,如果使用内联函数或方法,这些扩展将丢失。
\ $ \ endgroup \ $
– Jere.Jones
2011-1-27 18:49

#6 楼

我认为您可以通过使用Compiler com支持来做很多事情,这是一个示例。

#import "CLSID:lnkfile" //use the clsid of the ShellLink class.

IPersistFilePtr ptr = IPersistFilePtr.CreateInstance(...);


_com_ptr_t::CreateInstance()将引发异常(如果调用失败,则为_com_error类型)

使用#import生成的智能指针可以替换代码中的所有其他if。我知道我对细节有些轻率,但是距离接触COM已有很长时间了。

评论


\ $ \ begingroup \ $
据此不正确; _com_ptr_t :: CreateInstance()似乎返回HRESULT,并具有throw()规范。
\ $ \ endgroup \ $
–比利·奥尼尔(Billy ONeal)
2011年1月27日15:54