Visual C++(COM)设置Excel单元格区域颜色报错求助
使用Visual C++通过COM操作Excel,已成功打开Excel并获取单元格区域,但设置单元格区域颜色时返回“Object is not connected to server”错误。相关代码如下:
// AutoWrap函数代码 HRESULT AutoWrap(int autoType, VARIANT* pvResult, IDispatch* pDisp, LPOLESTR ptName, int cArgs...) { // Begin variable-argument list... va_list marker; va_start(marker, cArgs); if (!pDisp) { return E_INVALIDARG; } // Variables used... DISPPARAMS dp = { NULL, NULL, 0, 0 }; DISPID dispidNamed = DISPID_PROPERTYPUT; DISPID dispID; HRESULT hr; char buf[200]; char szName[200]; // Convert down to ANSI WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL); // Get DISPID for name passed... hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID); if (FAILED(hr)) { sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr); return hr; } // Allocate memory for arguments... VARIANT* pArgs = new VARIANT[cArgs + 1]; // Extract arguments... for (int i = 0; i < cArgs; i++) { pArgs[i] = va_arg(marker, VARIANT); } // Build DISPPARAMS dp.cArgs = cArgs; dp.rgvarg = pArgs; // Handle special-case for property-puts! if (autoType & DISPATCH_PROPERTYPUT) { dp.cNamedArgs = 1; dp.rgdispidNamedArgs = &dispidNamed; } // Make the call! hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL); if (FAILED(hr)) { sprintf(buf, "IDispatch::Invoke(\"%s\")=%08lx failed w/err 0x%08lx", szName, dispID, hr); return hr; } // End variable-argument section... va_end(marker); delete[] pArgs; return hr; } // OpenExcel函数代码 HRESULT OpenExcel(const CATUnicodeString usFilePath, CATBoolean bVisibility) { HRESULT hr = E_FAIL; if (!_pExcelApp) { if(FAILED(Initialize(bVisibility))) return hr; } VARIANT vResult,vResult2; VariantInit(&vResult); hr = AutoWrap(DISPATCH_PROPERTYGET, &vResult, _pExcelApp, L"Workbooks", 0); if (FAILED(hr)) return hr; _pExcelBooks = vResult.pdispVal; CATBSTR bstrFilePath; usFilePath.ConvertToBSTR(&bstrFilePath); VARIANT vFileName; vFileName.vt = VT_BSTR; vFileName.bstrVal = bstrFilePath; VariantInit(&vResult2); hr = AutoWrap(DISPATCH_PROPERTYGET, &vResult2, _pExcelBooks, L"Open", 1,vFileName); if (FAILED(hr)) { VariantClear(&vFileName); return hr; } _pExcelBook = vResult2.pdispVal; VariantClear(&vFileName); VariantClear(&vResult2); VariantClear(&vResult); return hr; } // Initialize函数代码 HRESULT Initialize(CATBoolean bVisibility) { CoInitializeEx(NULL, COINITBASE_MULTITHREADED); CLSID clsid; HRESULT hr = E_FAIL; LPCOLESTR progID = L"Excel.Application"; hr = CLSIDFromProgID(progID, &clsid); if (FAILED(hr)) return hr; hr = CoCreateInstance(clsid,NULL,CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&_pExcelApp); if (FAILED(hr)) { _pExcelApp = NULL; return hr; } hr = SetVisibility(bVisibility); return hr; } // SetVisibility函数代码 HRESULT SetVisibility(CATBoolean bVisibility) { HRESULT hr = E_FAIL; if (!_pExcelApp) { return hr; } VARIANT vVar; vVar.vt = VT_I4; vVar.lVal = bVisibility; hr = AutoWrap(DISPATCH_PROPERTYPUT, NULL, _pExcelApp, L"Visible", 1, vVar); return hr; } // SetProperty函数代码 HRESULT SetProperty(CATUnicodeString usRange) { HRESULT hr = E_FAIL; IDispatch* _pExcelRange; CATBSTR bstrRange; usRange.ConvertToBSTR(&bstrRange); VARIANT parm; parm.vt = VT_BSTR; parm.bstrVal = bstrRange; VARIANT result; VariantInit(&result); hr = AutoWrap(DISPATCH_PROPERTYGET, &result, _pExcelSheet, L"Range", 1, parm); VariantClear(&parm); _pExcelRange = result.pdispVal; { IDispatch* pXlInside; { VARIANT result; VariantInit(&result); hr = AutoWrap(DISPATCH_PROPERTYGET, &result, _pExcelRange, L"Interior", 0); pXlInside = result.pdispVal; VariantClear(&result); if (FAILED(hr)) return hr; } { VARIANT color; color.vt = VT_I4; color.lVal = long(RGB(255, 255, 0)); VARIANT result; VariantInit(&result); hr = AutoWrap(DISPATCH_PROPERTYPUT, &result, pXlInside, L"Color", 1, color); //This returns "Object is not connected to server error" VariantClear(&result); VariantClear(&color); if (FAILED(hr)) return hr; } pXlInside->Release(); _pExcelRange->Release(); } return hr; }
调用代码:
int main() { OpenExcel("FilePath"); SetProperty("A1:G1"); }
1. 线程模型不匹配
Excel的COM对象属于单线程单元(STA),但代码中使用COINITBASE_MULTITHREADED(多线程单元MTA)初始化COM库,跨单元调用会导致对象代理连接异常,触发“Object is not connected to server”错误。
修复:将Initialize函数中的COM初始化改为STA模型:
HRESULT Initialize(CATBoolean bVisibility) { HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) return hr; CLSID clsid; LPCOLESTR progID = L"Excel.Application"; hr = CLSIDFromProgID(progID, &clsid); if (FAILED(hr)) return hr; hr = CoCreateInstance(clsid,NULL,CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&_pExcelApp); if (FAILED(hr)) { _pExcelApp = NULL; CoUninitialize(); return hr; } hr = SetVisibility(bVisibility); return hr; }
2. Workbooks.Open调用类型错误
Workbooks.Open是方法而非属性,代码中使用DISPATCH_PROPERTYGET调用会导致返回的Workbook对象引用无效,后续依赖该对象的操作都会失败。
修复:将OpenExcel中调用Open的类型改为DISPATCH_METHOD,同时初始化_pExcelSheet全局变量:
HRESULT OpenExcel(const CATUnicodeString usFilePath, CATBoolean bVisibility) { HRESULT hr = E_FAIL; if (!_pExcelApp) { if(FAILED(Initialize(bVisibility))) return hr; } VARIANT vResult; VariantInit(&vResult); // 获取Workbooks集合 hr = AutoWrap(DISPATCH_PROPERTYGET, &vResult, _pExcelApp, L"Workbooks", 0); if (FAILED(hr)) return hr; _pExcelBooks = vResult.pdispVal; VariantClear(&vResult); CATBSTR bstrFilePath; usFilePath.ConvertToBSTR(&bstrFilePath); VARIANT vFileName; vFileName.vt = VT_BSTR; vFileName.bstrVal = bstrFilePath; // 使用DISPATCH_METHOD调用Open方法 hr = AutoWrap(DISPATCH_METHOD, &vResult, _pExcelBooks, L"Open", 1, vFileName); if (FAILED(hr)) { VariantClear(&vFileName); return hr; } _pExcelBook = vResult.pdispVal; VariantClear(&vFileName); VariantClear(&vResult); // 初始化_pExcelSheet,获取活动工作表 hr = AutoWrap(DISPATCH_PROPERTYGET, &vResult, _pExcelBook, L"ActiveSheet", 0); if (FAILED(hr)) return hr; _pExcelSheet = vResult.pdispVal; VariantClear(&vResult); return hr; }
3. _pExcelSheet未初始化
SetProperty函数中直接使用_pExcelSheet但未赋值,导致获取Range的操作实际是基于空指针,后续对象链全部无效。
修复:在OpenExcel中从打开的Workbook获取工作表对象(如上述代码中的ActiveSheet),确保_pExcelSheet有效。
4. Variant清理与引用计数问题
获取Range和Interior对象后,未及时清理Variant或处理引用计数,可能导致对象提前释放。
修复:调整SetProperty中的Variant清理时机,并增加空指针检查:
HRESULT SetProperty(CATUnicodeString usRange) { HRESULT hr = E_FAIL; if (!_pExcelSheet) return hr; IDispatch* _pExcelRange = nullptr; CATBSTR bstrRange; usRange.ConvertToBSTR(&bstrRange); VARIANT parm; parm.vt = VT_BSTR; parm.bstrVal = bstrRange; VARIANT result; VariantInit(&result); hr = AutoWrap(DISPATCH_PROPERTYGET, &result, _pExcelSheet, L"Range", 1, parm); VariantClear(&parm); if (FAILED(hr)) return hr; _pExcelRange = result.pdispVal; VariantClear(&result); // 保存指针后清理Variant IDispatch* pXlInside = nullptr; VariantInit(&result); hr = AutoWrap(DISPATCH_PROPERTYGET, &result, _pExcelRange, L"Interior", 0); if (FAILED(hr)) { _pExcelRange->Release(); return hr; } pXlInside = result.pdispVal; VariantClear(&result); VARIANT color; color.vt = VT_I4; color.lVal = static_cast<long>(RGB(255, 255, 0)); // 设置Color无需返回值,pvResult传NULL hr = AutoWrap(DISPATCH_PROPERTYPUT, nullptr, pXlInside, L"Color", 1, color); VariantClear(&color); // 释放对象 if (pXlInside) pXlInside->Release(); if (_pExcelRange) _pExcelRange->Release(); return hr; }
5. 程序结束时释放资源
主函数中需释放所有全局COM对象并调用CoUninitialize,避免资源泄漏:
int main() { OpenExcel(L"你的Excel文件路径", TRUE); SetProperty(L"A1:G1"); // 释放全局COM对象 if (_pExcelSheet) _pExcelSheet->Release(); if (_pExcelBook) _pExcelBook->Release(); if (_pExcelBooks) _pExcelBooks->Release(); if (_pExcelApp) _pExcelApp->Release(); CoUninitialize(); return 0; }
内容的提问来源于stack exchange,提问作者usmanharoon

