C++ builder EAccessViolation when calling Variant.OlePropertySet() for Excel Automation

After migrating an application from C++ Builder 4 to C++builder 6, the excel COM automation started to crash with error EAccessViolation at address 00000800 when calling the Variant.OlePropertySet(), OlePropertyGet() or OleFunction() methods, as in the following code :

[code lang=cpp]
Variant xlApp = Variant::CreateObject(“Excel.Application”);
xlApp.OlePropertySet(“Visible”, true); // causes the EAccessViolation
Variant workbooks = xlApp.OlePropertyGet(“Workbooks”);
Variant currentWorkbook ClasseurCourant = workbooks.OleFunction(“Open”, L”C:\\MyExcelFile.xls”);
[/code]
Excel-OlePropertySet-EAccessViolation
This code was working fine with Builder 4, it was also working fine when used in a brand new application, but it always crashed in the converted app. Given that many developers reported the same error, with no known fix, and that one couldn’t expect support for a 12 years old IDE, I eventually fixed the issue by bypassing the Borland Wrappers and using directly COM.

COM is always a nightmare to work with in C++, but Microsoft had my back and provided a code sample at https://support.microsoft.com/fr-fr/kb/216686

[code lang=cpp]
// AutoWrap() – Automation helper function…
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) {
MessageBox(NULL, “NULL IDispatch passed to AutoWrap()”, “Error”, 0x10010);
_exit(0);
}

// 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);
MessageBox(NULL, buf, “AutoWrap()”, 0x10010);
_exit(0);
return hr;
}

// Allocate memory for arguments…
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments…
for(int i=0; iInvoke(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);
MessageBox(NULL, buf, “AutoWrap()”, 0x10010);
_exit(0);
return hr;
}
// End variable-argument section…
va_end(marker);

delete [] pArgs;

return hr;
}
[/code]

Then, using the AutoWrap function, the Excel Workbook opening code cand eventually be rewritten as :

[code lang=cpp]
TVariant xlApp(Variant::CreateObject(“Excel.Application”));

// Based on sample from https://support.microsoft.com/fr-fr/kb/216686
AutoWrap(DISPATCH_PROPERTYPUT, NULL, xlApp, L”Visible”, 1, TVariant(1));

// Get Workbooks collection
TVariant workbooks;
{
TVariant result;
AutoWrap(DISPATCH_PROPERTYGET, &result, xlApp, L”Workbooks”, 0);
workbooks = result.pdispVal;
}

TVariant currentWorkbook;
{
TVariant result;
AutoWrap(DISPATCH_METHOD, &result, workbooks, L”Open”, 1, TVariant(L”C:\\Users\\nicolas\\Desktop\\TACHES NON PREVUES.xls”));
currentWorkbook = result.pdispVal;
}
[/code]

Leave a Reply

Your email address will not be published. Required fields are marked *