Here is the source code for the InvokeHelper replacement function:
// -----------------------------------------------------------------------------
// WordInvokeHelper
//
// Replacement for MFC COleDispatchDriver::InvokeHelper for this generated
// Word wrapper. It builds explicit VARIANTARG / DISPPARAMS structures and then
// calls IDispatch::Invoke directly. VARIANT* parameters that were previously
// described by VTS_PVARIANT are copied as real VARIANTARG values via
// VariantCopyInd instead of being forwarded as VT_VARIANT | VT_BYREF. This is
// the important compatibility workaround for Word late binding after KB5094126.
// -----------------------------------------------------------------------------
static int WordParamCount(const BYTE* pbParamInfo)
{
int nCount = 0;
if (pbParamInfo == NULL)
return 0;
while (pbParamInfo[nCount] != 0)
++nCount;
return nCount;
}
static void WordFreeExcepInfo(EXCEPINFO* pExcepInfo)
{
if (pExcepInfo == NULL)
return;
if (pExcepInfo->bstrSource)
SysFreeString(pExcepInfo->bstrSource);
if (pExcepInfo->bstrDescription)
SysFreeString(pExcepInfo->bstrDescription);
if (pExcepInfo->bstrHelpFile)
SysFreeString(pExcepInfo->bstrHelpFile);
pExcepInfo->bstrSource = NULL;
pExcepInfo->bstrDescription = NULL;
pExcepInfo->bstrHelpFile = NULL;
}
static HRESULT WordCopyVariantArgOrMissing(VARIANTARG* pDest, VARIANT* pSrc)
{
VariantInit(pDest);
if (pSrc == NULL)
{
pDest->vt = VT_ERROR;
pDest->scode = DISP_E_PARAMNOTFOUND;
return S_OK;
}
return VariantCopyInd(pDest, pSrc);
}
static HRESULT WordBuildVariantArg(VARIANTARG* pArg, BYTE bParamType, va_list* pArgList)
{
VariantInit(pArg);
if (bParamType == (BYTE)VTS_PVARIANT[0])
{
VARIANT* pVar = va_arg(*pArgList, VARIANT*);
return WordCopyVariantArgOrMissing(pArg, pVar);
}
if (bParamType == (BYTE)VTS_BSTR[0])
{
LPCTSTR psz = va_arg(*pArgList, LPCTSTR);
pArg->vt = VT_BSTR;
if (psz == NULL)
{
pArg->bstrVal = NULL;
return S_OK;
}
pArg->bstrVal = CString(psz).AllocSysString();
return pArg->bstrVal ? S_OK : E_OUTOFMEMORY;
}
if (bParamType == (BYTE)VTS_BOOL[0])
{
BOOL bValue = va_arg(*pArgList, BOOL);
pArg->vt = VT_BOOL;
pArg->boolVal = bValue ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
if (bParamType == (BYTE)VTS_I4[0])
{
long lValue = va_arg(*pArgList, long);
pArg->vt = VT_I4;
pArg->lVal = lValue;
return S_OK;
}
if (bParamType == (BYTE)VTS_I2[0])
{
int nValue = va_arg(*pArgList, int);
pArg->vt = VT_I2;
pArg->iVal = (short)nValue;
return S_OK;
}
if (bParamType == (BYTE)VTS_R4[0])
{
double dblValue = va_arg(*pArgList, double);
pArg->vt = VT_R4;
pArg->fltVal = (float)dblValue;
return S_OK;
}
if (bParamType == (BYTE)VTS_DISPATCH[0])
{
LPDISPATCH pDisp = va_arg(*pArgList, LPDISPATCH);
pArg->vt = VT_DISPATCH;
pArg->pdispVal = pDisp;
if (pArg->pdispVal != NULL)
pArg->pdispVal->AddRef();
return S_OK;
}
return DISP_E_BADVARTYPE;
}
static HRESULT WordGetVariantValue(VARIANT* pDest, VARIANT* pSrc)
{
VariantInit(pDest);
if (pSrc == NULL)
return E_POINTER;
return VariantCopyInd(pDest, pSrc);
}
static HRESULT WordStoreReturnValue(VARTYPE vtRet, void* pvRet, VARIANT* pVarResult)
{
HRESULT hr = S_OK;
VARIANT value;
VARIANT converted;
if (vtRet == VT_EMPTY)
{
if (pVarResult != NULL)
VariantClear(pVarResult);
return S_OK;
}
if (pvRet == NULL || pVarResult == NULL)
{
if (pVarResult != NULL)
VariantClear(pVarResult);
return S_OK;
}
VariantInit(&value);
VariantInit(&converted);
hr = WordGetVariantValue(&value, pVarResult);
VariantClear(pVarResult);
if (FAILED(hr))
return hr;
switch (vtRet)
{
case VT_DISPATCH:
*((LPDISPATCH*)pvRet) = NULL;
if (value.vt == VT_DISPATCH)
{
*((LPDISPATCH*)pvRet) = value.pdispVal;
value.vt = VT_EMPTY;
}
VariantClear(&value);
return S_OK;
case VT_UNKNOWN:
*((LPUNKNOWN*)pvRet) = NULL;
if (value.vt == VT_UNKNOWN)
{
*((LPUNKNOWN*)pvRet) = value.punkVal;
value.vt = VT_EMPTY;
}
VariantClear(&value);
return S_OK;
case VT_VARIANT:
{
VARIANT* pOut = (VARIANT*)pvRet;
VariantInit(pOut);
*pOut = value;
VariantInit(&value);
return S_OK;
}
case VT_BSTR:
if (value.vt == VT_EMPTY || value.vt == VT_NULL)
{
*((CString*)pvRet) = _T("");
VariantClear(&value);
return S_OK;
}
hr = VariantChangeType(&converted, &value, 0, VT_BSTR);
if (SUCCEEDED(hr))
{
if (converted.bstrVal != NULL)
*((CString*)pvRet) = converted.bstrVal;
else
*((CString*)pvRet) = _T("");
}
VariantClear(&converted);
VariantClear(&value);
return hr;
case VT_BOOL:
hr = VariantChangeType(&converted, &value, 0, VT_BOOL);
if (SUCCEEDED(hr))
*((BOOL*)pvRet) = (converted.boolVal != VARIANT_FALSE);
VariantClear(&converted);
VariantClear(&value);
return hr;
case VT_I4:
hr = VariantChangeType(&converted, &value, 0, VT_I4);
if (SUCCEEDED(hr))
*((long*)pvRet) = converted.lVal;
VariantClear(&converted);
VariantClear(&value);
return hr;
case VT_I2:
hr = VariantChangeType(&converted, &value, 0, VT_I2);
if (SUCCEEDED(hr))
*((short*)pvRet) = converted.iVal;
VariantClear(&converted);
VariantClear(&value);
return hr;
case VT_R4:
hr = VariantChangeType(&converted, &value, 0, VT_R4);
if (SUCCEEDED(hr))
*((float*)pvRet) = converted.fltVal;
VariantClear(&converted);
VariantClear(&value);
return hr;
default:
VariantClear(&value);
return DISP_E_BADVARTYPE;
}
}
static void WordInvokeHelper(LPDISPATCH lpDispatch, DISPID dispid, WORD wFlags,
VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ...)
{
int nArgCount = WordParamCount(pbParamInfo);
VARIANTARG* pArgs = NULL;
HRESULT hr = S_OK;
va_list argList;
DISPPARAMS dispParams;
DISPID dispidNamed = DISPID_PROPERTYPUT;
VARIANT varResult;
VARIANT* pVarResult = NULL;
EXCEPINFO excepInfo;
UINT argErr = 0;
int i;
if (lpDispatch == NULL)
AfxThrowOleException(E_POINTER);
if (nArgCount > 0)
{
pArgs = new VARIANTARG[nArgCount];
for (i = 0; i < nArgCount; ++i)
VariantInit(&pArgs[i]);
}
va_start(argList, pbParamInfo);
for (i = 0; i < nArgCount; ++i)
{
hr = WordBuildVariantArg(&pArgs[nArgCount - 1 - i], pbParamInfo[i], &argList);
if (FAILED(hr))
break;
}
va_end(argList);
if (FAILED(hr))
{
if (pArgs != NULL)
{
for (i = 0; i < nArgCount; ++i)
VariantClear(&pArgs[i]);
delete [] pArgs;
}
AfxThrowOleException(hr);
}
memset(&dispParams, 0, sizeof(dispParams));
dispParams.rgvarg = pArgs;
dispParams.cArgs = nArgCount;
if ((wFlags & DISPATCH_PROPERTYPUT) || (wFlags & DISPATCH_PROPERTYPUTREF))
{
dispParams.rgdispidNamedArgs = &dispidNamed;
dispParams.cNamedArgs = 1;
}
VariantInit(&varResult);
pVarResult = (vtRet == VT_EMPTY) ? NULL : &varResult;
memset(&excepInfo, 0, sizeof(excepInfo));
hr = lpDispatch->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
wFlags,
&dispParams,
pVarResult,
&excepInfo,
&argErr);
if (pArgs != NULL)
{
for (i = 0; i < nArgCount; ++i)
VariantClear(&pArgs[i]);
delete [] pArgs;
}
if (FAILED(hr))
{
WordFreeExcepInfo(&excepInfo);
if (pVarResult != NULL)
VariantClear(pVarResult);
AfxThrowOleException(hr);
}
WordFreeExcepInfo(&excepInfo);
hr = WordStoreReturnValue(vtRet, pvRet, pVarResult);
if (FAILED(hr))
AfxThrowOleException(hr);
}
static BOOL WordIsMissingVariant(VARIANT* pSrc)
{
VARIANT value;
BOOL bMissing = FALSE;
if (pSrc == NULL)
return TRUE;
VariantInit(&value);
if (FAILED(VariantCopyInd(&value, pSrc)))
return FALSE;
bMissing = (value.vt == VT_ERROR && value.scode == DISP_E_PARAMNOTFOUND);
VariantClear(&value);
return bMissing;
}
static HRESULT WordCopyCloseQuitArg(VARIANTARG* pDest, VARIANT* pSrc, BOOL bSaveChanges)
{
HRESULT hr;
VARIANT_BOOL boolValue;
short shortValue;
hr = WordCopyVariantArgOrMissing(pDest, pSrc);
if (FAILED(hr))
return hr;
/*
Word Document.Close/Application.Quit SaveChanges is an enum value.
When callers pass COleVariant(FALSE, VT_BOOL), pass VT_I4/0 to Word.
When callers pass COleVariant(TRUE, VT_BOOL), pass VT_I4/-1.
*/
if (bSaveChanges)
{
if (pDest->vt == VT_BOOL)
{
boolValue = pDest->boolVal;
VariantClear(pDest);
pDest->vt = VT_I4;
pDest->lVal = (boolValue != VARIANT_FALSE) ? -1L : 0L;
}
else if (pDest->vt == VT_I2)
{
shortValue = pDest->iVal;
VariantClear(pDest);
pDest->vt = VT_I4;
pDest->lVal = (long)shortValue;
}
}
return S_OK;
}
static void WordInvokeCloseQuit(LPDISPATCH lpDispatch, DISPID dispid,
VARIANT* SaveChanges, VARIANT* OriginalFormat, VARIANT* RouteDocument)
{
VARIANTARG args[3];
VARIANT* srcArgs[3];
int nArgCount = 3;
int i;
HRESULT hr = S_OK;
DISPPARAMS dispParams;
EXCEPINFO excepInfo;
UINT argErr = 0;
/*
Close/Quit may be called more than once during cleanup. If the
wrapper has already released its dispatch pointer via ReleaseDispatch(),
there is nothing left to close here. Treat that case as success instead
of throwing E_POINTER. This guard is intentionally limited to
Close/Quit and is not used by the general WordInvokeHelper.
*/
if (lpDispatch == NULL)
return;
/*
Do not send trailing Optional parameters to Word. For calls such as
Close(FALSE, Optional, Optional) or Quit(FALSE, Optional, Optional), this
means cArgs == 1 and rgvarg[0] == SaveChanges.
*/
if (WordIsMissingVariant(RouteDocument))
{
nArgCount = 2;
if (WordIsMissingVariant(OriginalFormat))
{
nArgCount = 1;
if (WordIsMissingVariant(SaveChanges))
nArgCount = 0;
}
}
for (i = 0; i < 3; ++i)
VariantInit(&args[i]);
srcArgs[0] = SaveChanges;
srcArgs[1] = OriginalFormat;
srcArgs[2] = RouteDocument;
for (i = 0; i < nArgCount; ++i)
{
hr = WordCopyCloseQuitArg(&args[nArgCount - 1 - i], srcArgs[i], (i == 0));
if (FAILED(hr))
break;
}
if (FAILED(hr))
{
for (i = 0; i < 3; ++i)
VariantClear(&args[i]);
AfxThrowOleException(hr);
}
memset(&dispParams, 0, sizeof(dispParams));
dispParams.rgvarg = (nArgCount > 0) ? args : NULL;
dispParams.cArgs = nArgCount;
memset(&excepInfo, 0, sizeof(excepInfo));
hr = lpDispatch->Invoke(
dispid,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispParams,
NULL,
&excepInfo,
&argErr);
for (i = 0; i < 3; ++i)
VariantClear(&args[i]);
if (FAILED(hr))
{
WordFreeExcepInfo(&excepInfo);
AfxThrowOleException(hr);
}
WordFreeExcepInfo(&excepInfo);
}
Replace all InvokerHelper calls like this:
From:
LPDISPATCH Documents::Open(VARIANT* FileName, VARIANT* ConfirmConversions, VARIANT* ReadOnly, VARIANT* AddToRecentFiles, VARIANT* PasswordDocument, VARIANT* PasswordTemplate, VARIANT* Revert, VARIANT* WritePasswordDocument, VARIANT* WritePasswordTemplate, VARIANT* Format)`
{
LPDISPATCH result;
static BYTE parms[] =
VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT;
InvokeHelper(0xc, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,
FileName, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format);
return result;
}
to
LPDISPATCH Documents::Open(VARIANT* FileName, VARIANT* ConfirmConversions, VARIANT* ReadOnly, VARIANT* AddToRecentFiles, VARIANT* PasswordDocument, VARIANT* PasswordTemplate, VARIANT* Revert, VARIANT* WritePasswordDocument, VARIANT* WritePasswordTemplate, VARIANT* Format)
{
LPDISPATCH result;
static BYTE parms[] =
VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT;
WordInvokeHelper(m_lpDispatch, 0xc, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,
FileName, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format);
return result;
}
In case of Documents.Close, Document.Close or Application.Quit, use the new helper like this:
void Documents::Close(VARIANT* SaveChanges, VARIANT* OriginalFormat, VARIANT* RouteDocument)
{
WordInvokeCloseQuit(m_lpDispatch, 0x451, SaveChanges, OriginalFormat, RouteDocument);
}
void _Document::Close(VARIANT* SaveChanges, VARIANT* OriginalFormat, VARIANT* RouteDocument)
{
WordInvokeCloseQuit(m_lpDispatch, 0x451, SaveChanges, OriginalFormat, RouteDocument);
}
void _Application::Quit(VARIANT* SaveChanges, VARIANT* OriginalFormat, VARIANT* RouteDocument)
{
WordInvokeCloseQuit(m_lpDispatch, 0x451, SaveChanges, OriginalFormat, RouteDocument);
}
HTH,
Justin