excelvisual-c++comexcel-interopcom-automation

Set color to cell ranges in excel with Visual C++ (COM)


I'm trying to do some excel functionalities using visual C++. Able to achieve opening the excel, get the cell ranges but setting color to the cell range is failed by returning error "Object is not connected to server".

Below is the code:

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;
}

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;
}

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;
}

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;
}

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;
}

Caller function:

int main()
{
    OpenExcel("FilePath");
    SetProperty("A1:G1");
}

What am I doing wrong here? Please suggest your answers.


Solution

  • "Object is not connected to server" (0x800401FD / CO_E_OBJNOTCONNECTED) means the COM proxy is dead/corrupted. But your code also can cause "simple" crashes.

    It often looks like this:

    VariantInit(&vResult2);
    hr = AutoWrap(DISPATCH_PROPERTYGET, &vResult2, _pExcelBooks, L"Open", 1, vFileName);
    ...
    _pExcelBook = vResult2.pdispVal; // same with pXlInside, etc.
    ...
    VariantClear(&vResult2);
    

    The problem is VariantClear will call pdispVal->Release() (if the VARIANT is of VT_DISPATCH type) and _pExcelBook becomes a dangling pointer that points to deallocated memory.

    So you can either remove the VariantClear call, or do this (preferred so you can keep balanced VariantInit/VariantClear calls):

    ...
    vResult2.pdispVal->AddRef();
    _pExcelBook = vResult2.pdispVal;
    ...
    

    or this:

    ...
    vResult2.pdispVal->QueryInterface(&_pExcelBook);
    ...