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;
}
}
#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
评论
这让我想到了Haskell的monad,但我认为这不会对您有所帮助。仍然完全可以使用它。@Tyr:或C ++ 14的可选