c++调用COM组件的坑
最近在工作中,需要用到COM组件来完成任务。再调用过程中,遇到一些平常容易忽略的错误,记录一下,避免自己再犯同样的错误。
首先来看一段代码:
// 查找桌面文件视图
bool FindDesktopFolderView(REFIID riid, void **ppv)
{
CComPtr<IShellWindows> spShellWindows;
HRESULT hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
if (!SUCCEEDED(hr))
{
*ppv = NULL;
return false;
}
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
hr = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
if (!SUCCEEDED(hr))
{
*ppv = NULL;
return false;
}
CComPtr<IShellBrowser> spBrowser;
hr = CComQIPtr<IServiceProvider>(spdisp)->
QueryService(SID_STopLevelBrowser,
IID_PPV_ARGS(&spBrowser));
if (!SUCCEEDED(hr) || NULL == spBrowser)
{
*ppv = NULL;
return false;
}
CComPtr<IShellView> spView;
hr = spBrowser->QueryActiveShellView(&spView);
if (!SUCCEEDED(hr) || NULL == spView)
{
*ppv = NULL;
return false;
}
hr = spView->QueryInterface(riid, ppv);
if (!SUCCEEDED(hr))
{
*ppv = NULL;
return false;
}
return true;
}
上面这段代码的功能是查找桌面视图。咋一看,没什么问题,它确实能在大部分情况下工作。但是当桌面窗口未初始化完成时,问题就出现了(这个也是我们在线上实实在在遇到的问题)。
当执行spShellWindows->FindWindowSW()
函数时,返回的HRESULT结果是S_OK。那么照理说,这次查找桌面窗口应该是成功的。但是实际上,spdisp
这个指针是空的!!!这里没有判断空指针,导致程序崩溃。屋漏偏逢连夜雨!由于,我们的项目是将该工程以动态库的方式提供给golang调用(有点绕了),用c++ dump方式无法定位崩溃原因。用golang 的recover()捕获panic同样无法定位崩溃原因(大哭)。
后面将加上空指针判断以后, 程序终于正常工作了。修复代码如下:
// 查找桌面文件视图
bool FindDesktopFolderView(REFIID riid, void **ppv)
{
CComPtr<IShellWindows> spShellWindows;
HRESULT hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
if (!SUCCEEDED(hr))
{
*ppv = NULL;
return false;
}
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
hr = spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
// 这里既要判断hr 又要判断空指针
if (!SUCCEEDED(hr) || NULL == spdisp)
{
*ppv = NULL;
return false;
}
CComPtr<IShellBrowser> spBrowser;
hr = CComQIPtr<IServiceProvider>(spdisp)->
QueryService(SID_STopLevelBrowser,
IID_PPV_ARGS(&spBrowser));
// 这里既要判断hr 又要判断空指针
if (!SUCCEEDED(hr) || NULL == spBrowser)
{
*ppv = NULL;
return false;
}
CComPtr<IShellView> spView;
hr = spBrowser->QueryActiveShellView(&spView);
// 这里既要判断hr 又要判断空指针
if (!SUCCEEDED(hr) || NULL == spView)
{
*ppv = NULL;
return false;
}
hr = spView->QueryInterface(riid, ppv);
if (!SUCCEEDED(hr))
{
*ppv = NULL;
return false;
}
return true;
}
这里提醒一下自己,不要被SUCCEEDED(hr)
接口麻痹,写c++任何时候都要判断空指针!!!