We have an old C++ COM .dll that a customer calls from their C++ client.
We are trying to replace our old .dll with a new COM registered one written in .NET.
The C++ client can call our new .dll but crashes after a particular method call to us.
It seems we are returning something wrong in our variable "outNoOfChildren" or in the outChildList array below.
(The error message in the client reads: "Unknown exception caught.
Number of childs Measured: -2147467259 Limits: = 2", it expects 2 which we are trying to return.)
Strange thing is that the method call in our new .NET .dll seems to work from another test client we have (VB6).
This is the function in the C++ .dll that we are trying to replace:
STDMETHODIMP marcomBase::XgetChildren(VARIANT inUser,
VARIANT inId,
VARIANT *outNoOfChildren,
VARIANT *outChildList,
VARIANT *outErrMsg,
VARIANT *status)
long stat= 1;
char user[255+1];
char id[255+1];
long noOfChildren = 0;
char errMsg[255+1] = "";
_bstr_t temp_bstr;
long inparamType;
long dumInt;
SAFEARRAY *pArray;
long ix[2];
VARIANT var;
SAFEARRAYBOUND rgsabound[2];
ChildT childList;
ChildT *childListTemp = NULL;
ChildT *childListTempNext = NULL;
childList.nextChild = NULL;
getInParam( inUser, &inparamType, user, &dumInt);
if (inparamType != VT_BSTR)
{
strcpy(errMsg, "Parameter 1 incorrect, type must be VT_BSTR");
stat = 0;
}
if (stat == 1)
{
getInParam( inId, &inparamType, id, &dumInt);
if (inparamType != VT_BSTR)
{
strcpy(errMsg, "Parameter 2 incorrect, type must be VT_BSTR");
stat = 0;
}
}
if (stat == 1)
{
stat = barApiObj.getChildren(user, id, &noOfChildren, childList, errMsg);
}
outNoOfChildren->vt = VT_I4;
outNoOfChildren->lVal = noOfChildren;
rgsabound[0].lLbound = 1;
rgsabound[0].cElements = noOfChildren;
rgsabound[1].lLbound = 1;
rgsabound[1].cElements = 3;
pArray = ::SafeArrayCreate(VT_VARIANT, 2, rgsabound);
outChildList->vt = VT_ARRAY|VT_VARIANT;
outChildList->parray = pArray;
ix[0] = 1;
childListTemp = childList.nextChild;
while (childListTemp != NULL)
{
temp_bstr = childListTemp->child;
var.vt = VT_BSTR;
var.bstrVal = temp_bstr.copy();
ix[1] = 1;
::SafeArrayPutElement(pArray, ix, &var);
temp_bstr = childListTemp->prodNo;
var.vt = VT_BSTR;
var.bstrVal = temp_bstr.copy();
ix[1] = 2;
::SafeArrayPutElement(pArray, ix, &var);
temp_bstr = childListTemp->rev;
var.vt = VT_BSTR;
var.bstrVal = temp_bstr.copy();
ix[1] = 3;
::SafeArrayPutElement(pArray, ix, &var);
ix[0] = ix[0] + 1;
childListTempNext = childListTemp->nextChild;
delete childListTemp;
childListTemp = childListTempNext;
}
temp_bstr = errMsg;
outErrMsg->vt = VT_BSTR;
outErrMsg->bstrVal = temp_bstr.copy();
status->vt = VT_I4;
status->lVal = stat;
return S_OK;
}
This is the .NET .dll that we have written:
.NET Interface:
...
[DispId(12)]
[Description("method XgetChildren")]
object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg);
...
.NET Class
public object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg)
{
outNoOfChildren = 0;
outChildList = null;
outErrMsg = "";
List<IndividualInfo> children = null;
try
{
PrevasMesExternalServiceClient client = getClient();
children = client.GetChildren(new SerialNo() { Number = inId.ToString() });
}
catch (Exception ex)
{
return Constants.STATUS_FAIL;
}
int[] myLengthsArray = { children.Count, 3 }; //Length of each array
int[] myBoundsArray = { 1, 1 }; //Start index of each array
Array myArray = Array.CreateInstance(typeof(string), myLengthsArray, myBoundsArray); //Create 1-based array of arrays
for (int i = 0; i < children.Count; i++)
{
myArray.SetValue(Utils.getStrValue(children[i].SerialNumber, Constants.pmesIDNO_LENGTH), i+1, 1);
myArray.SetValue(Utils.getStrValue(children[i].ProductNumber, Constants.pmesPRODUCTNO_LENGTH), i+1, 2);
myArray.SetValue(Utils.getStrValue(children[i].Revision, Constants.pmesRSTATE_LENGTH), i+1, 3);
}
outChildList = myArray;
outNoOfChildren = children.Count;
return Constants.STATUS_OK;
}
Update:
The original C++ .dll looks like this in OLE/COM Object Viewer:
[id(0x0000000c), helpstring("method XgetChildren")]
HRESULT XgetChildren(
[in] VARIANT inUser,
[in] VARIANT inId,
[out] VARIANT* outNoOfChildren,
[out] VARIANT* outChildList,
[out] VARIANT* outErrMsg,
[out, retval] VARIANT* status);
After registering our new .NET .dll it looks like this in OLE/COM Object Viewer:
[id(0x0000000c), helpstring("method ")]
HRESULT XgetChildren(
[in] VARIANT inUser,
[in] VARIANT inId,
[out] VARIANT* outNoOfChildren,
[out] VARIANT* outChildList,
[out] VARIANT* outErrMsg,
[out, retval] VARIANT* pRetVal);
Update 2: I'm beginning to suspect the error may not be in "outNoOfChildren" but in the array "outChildList". I have updated the question and modified the code samples for this.
Any ideas much appreciated!
Measured: -2147467259 Limits: = 2"
That's a magic number. Convert it to hex and you get 0x80004005. That's an error code, the infamous E_FAIL or "Unspecified error". So somewhere in the dots that we can't see, you are interpreting an error return value as a number. Possibly a variant of type vtError and ignoring the variant type. That's all that can be concluded from the question, review the error handling in your code.
Related
I have a COM object which exposes a function. I would like to pass parameters to this function and receive a return value. I'm using C++ with CoCreateInstance(). The error I receive is:
hr = 0x8002000e : Invalid number of parameters.
I'm reasonably sure that I have the correct number of parameters, which I can view in OleView:
[id(0x68030001), propget]
double My_function(
[in, out] double* PdblPrice,
[in, out] DATE* PdateStartDate,
[in, out] short* PintFlag,
[in, out] VARIANT_BOOL* PbolXP,
[in, out] SAFEARRAY(double)* PdblScale),
[out, retval] double*);
A summary of my code is as follows, and it works up to the point marked below:
#include <windows.h>
#include <objbase.h>
#include <comutil.h>
#include <vector>
#include <atlcomcli.h>
int main()
{
HRESULT hr;
// Create an instance of COM object
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT nResult1 = CLSIDFromProgID(OLESTR("My_Library.clsMy_Library"), &clsid);
IUnknown* pUnknown;
hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (void**)&pUnknown);
// Get the IDispatch interface
IDispatch* pDispatch;
hr = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDispatch);
// Call the Invoke method
DISPID dispid;
BSTR bstrFunction = SysAllocString(L"My_function");
hr = pDispatch->GetIDsOfNames(IID_NULL, &bstrFunction, 1, LOCALE_USER_DEFAULT, &dispid);
// ALL OF THE ABOVE WORKS.
// Prepare the arguments for the method call
// first convert a std::vector to SAFEARRAY
std::vector<double> _PdblScale = { 0, 0.25, 0.5, 0.75, 1.0, 0, 0, 0.5, 1, 1 };
SAFEARRAY* psa = SafeArrayCreateVector(VT_R8, 0, _PdblScale.size());
int* pData;
HRESULT hr_ = SafeArrayAccessData(psa, (void**)&pData);
if (SUCCEEDED(hr_))
{
for (unsigned int i = 0; i < _PdblScale.size(); i++)
{
pData[i] = _PdblScale[i];
}
SafeArrayUnaccessData(psa);
}
DISPPARAMS dispparams;
dispparams.cArgs = 5;
dispparams.rgvarg = new VARIANT[5];
dispparams.cNamedArgs = 5;
VARIANT PdblPrice;
PdblPrice.vt = VT_R8;
PdblPrice.dblVal = 28.0;
dispparams.rgvarg[0] = PdblPrice;
VARIANT PdateStartDate;
PdateStartDate.vt = VT_DATE;
PdateStartDate.date = 41052;
dispparams.rgvarg[1] = PdateStartDate;
VARIANT PintFlag;
PintFlag.vt = VT_I2;
PintFlag.iVal = 1;
dispparams.rgvarg[2] = PintFlag;
VARIANT PbolXP;
PbolXP.vt = VT_BOOL;
PbolXP.boolVal = false;
dispparams.rgvarg[3] = PbolXP;
VARIANT PdblScale;
PdblScale.vt = VT_SAFEARRAY;
PdblScale.pvRecord = psa;
dispparams.rgvarg[4] = PdblScale;
VARIANT varResult;
VariantInit(&varResult);
EXCEPINFO excepinfo;
memset(&excepinfo, 0, sizeof(excepinfo));
UINT nArgErr = (UINT)-1;
// Invoke the method ## THIS IS WHERE hr returns 0x8002000e : Invalid number of parameters
hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &varResult, &excepinfo, &nArgErr);
if (FAILED(hr))
{
printf("Failed to invoke method.");
pDispatch->Release();
pUnknown->Release();
CoUninitialize();
return 1;
}
// Print the result
printf("Result: %d\n", varResult.intVal);
// Clean up
VariantClear(&varResult);
pDispatch->Release();
pUnknown->Release();
CoUninitialize();
return 0;
}
I see from IDispatch Invoke() returns Type mismatch that the arguments should be in reverse order. I have tried that, ie. used [4], [3], [2], etc instead of [0], [1], etc above, but this still gives the error.
Any suggestions as to where I might be going wrong?
By the way, the COM is from a 32bit DLL, and I am compiling my code to x86.
There are many problems with your code:
lack of error handling.
when creating the COM object, you don't need to get its IUnknown just to immediately query it for IDispatch. You can get its IDispatch directly.
memory leaks on bstrFunction and dispparams.rgvarg.
you are creating a SAFEARRAY of VT_R8 (double) elements, but you are using an int* pointer to populate its values. You need to use a double* pointer instead.
you are not populating the DISPPARAMS or the VARIANTs correctly. The parameters have to be stored in the DISPPARAMS in revere order. And the VARIANTs need to use the VT_BYREF flag, which means they need to point at external variables that hold the actual values, rather than storing the values inside the VARIANTs themselves.
calling IDispatch::Invoke() with the wrong flag. You need to use DISPATCH_PROPERTYGET instead of DISPATCH_METHOD since the method is marked as propget in the IDL.
not using VARIANT_BOOL correctly for the bolXP parameter.
after invoking the method, you are printing out the wrong field of varResult. The method is declared as returning a double in the IDL, not an int.
With all of that said, try something more like this:
#include <windows.h>
#include <objbase.h>
#include <comutil.h>
#include <comdef.h>
#include <atlcomcli.h>
#include <vector>
// tweaked from https://devblogs.microsoft.com/oldnewthing/20040520-00/?p=39243
class CCoInitializeEx {
HRESULT m_hr;
public:
CCoInitializeEx() : m_hr(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) { }
~CCoInitializeEx() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
};
int main()
{
HRESULT hr;
// Initialize COM
CCoInitializeEx init;
hr = init;
if (FAILED(hr)) {
printf("Failed to init COM.");
return -1;
}
// Create an instance of COM object and get its IDispatch interface
CLSID clsid;
hr = CLSIDFromProgID(OLESTR("My_Library.clsMy_Library"), &clsid);
if (FAILED(hr)) {
printf("Failed to get CLSID.");
return -1;
}
IDispatchPtr pDispatch;
hr = pDispatch.CreateInstance(clsid);
if (FAILED(hr)) {
printf("Failed to create COM object.");
return -1;
}
// Call the Invoke method
DISPID dispid;
_bstr_t bstrFunction = OLESTR("My_function");
LPOLESTR pbstrFunction = bstrFunction;
hr = pDispatch->GetIDsOfNames(IID_NULL, &pbstrFunction, 1, LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) {
printf("Failed to get DispID.");
return -1;
}
// ...
// first convert a std::vector to SAFEARRAY
// TODO: wrap the SAFEARRAY inside a RAII class...
std::vector<double> vecDblScale = { 0, 0.25, 0.5, 0.75, 1.0, 0, 0, 0.5, 1, 1 };
SAFEARRAY* psa = SafeArrayCreateVector(VT_R8, 0, vecDblScale.size());
if (!psa) {
printf("Failed to allocate SAFEARRAY.");
return -1;
}
double* pData;
hr = SafeArrayAccessData(psa, reinterpret_cast<void**>(&pData));
if (FAILED(hr))
printf("Failed to access SAFEARRAY data.");
SafeArrayDestroy(psa);
return -1;
}
for (size_t i = 0; i < vecDblScale.size(); ++i)
{
pData[i] = vecDblScale[i];
}
// alternatively:
//
// #include <algorithm>
// std::copy(vecDblScale.begin(), vecDblScale.end(), pData);
SafeArrayUnaccessData(psa);
// Prepare the arguments for the method call
std::vector<VARIANT> vecRgvarg(5);
DOUBLE dblPrice = 28.0;
vecRgvarg[4].vt = VT_R8 | VT_BYREF;
vecRgvarg[4].pdblVal = &dblPrice;
DATE dateStartDate = 41052;
vecRgvarg[3].vt = VT_DATE | VT_BYREF;
vecRgvarg[3].pdate = &dateStartDate;
short intFlag = 1;
vecRgvarg[2].vt = VT_I2 | VT_BYREF;
vecRgvarg[2].piVal = &intFlag;
VARIANT_BOOL bolXP = VARIANT_FALSE;
vecRgvarg[1].vt = VT_BOOL | VT_BYREF;
vecRgvarg[1].pboolVal = &bolXP;
vecRgvarg[0].vt = VT_R8 | VT_ARRAY | VT_BYREF;
vecRgvarg[0].pparray = &psa;
DISPPARAMS dispparams = {};
dispparams.cArgs = vecRgvarg.size();
dispparams.rgvarg = vecRgvarg.data();
// Invoke the method
_variant_t varResult;
EXCEPINFO excepinfo = {};
UINT nArgErr = (UINT)-1;
hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &varResult, &excepinfo, &nArgErr);
if (FAILED(hr)) {
printf("Failed to invoke method.");
SafeArrayDestroy(psa);
return -1;
}
// Print the result
printf("Result: %f\n", varResult.dblVal);
// Clean up
SafeArrayDestroy(psa);
return 0;
}
I am wrote a function which return all the direct reports of the active directory user but it return only the first value rather returning all the direct reports.
void getDirectReports(LPCWSTR pwszAccountName) {
HRESULT hr;
CoInitialize(NULL);
IDirectorySearch* pdSearch = NULL;
ADS_SEARCH_COLUMN col;
CComBSTR path = L"LDAP://WIN-F94H2MP3UJR.Test.local/CN=";
path += pwszAccountName;
path += L",CN=Users,DC=Test,DC=local";
hr = ADsOpenObject(path, L"Administrator", L"Password",
ADS_SECURE_AUTHENTICATION, // For secure authentication
IID_IDirectorySearch,
(void**)&pdSearch);
if (SUCCEEDED(hr)) {
LPWSTR directReports = convert(L"directReports");
LPWSTR pszAttr[] = { directReports};
ADS_SEARCH_HANDLE hSearch;
DWORD dwCount = 0;
DWORD nameSize = sizeof(pszAttr) / sizeof(LPWSTR);
LPWSTR path = convert(L"(&(objectClass=user))");
hr = pdSearch->ExecuteSearch(path, pszAttr, nameSize, &hSearch);
while (pdSearch->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS) {
hr = pdSearch->GetColumn(hSearch,directReports, &col);
if (SUCCEEDED(hr)) {
printf("%S\n", (BSTR)col.pADsValues->CaseIgnoreString);
}
}
}
}
I have 5 direct reports to the ad user and this function only prints the first value alone.
The IDirectorySearch::GetColumn method will give you an ADS_SEARCH_COLUMN structure in your col variable. That documentation tells us that the value in col.pADsValues is an array, with the array size given in the col.dwNumValues.
In C++, accessing an array without the index (like col.pADsValues) is equivalent to reading the first value in the array (col.pADsValues[0]).
You just need to loop through the array.
hr = pdSearch->GetColumn(hSearch,directReports, &col);
if (SUCCEEDED(hr)) {
for (DWORD x = 0; x < col.dwNumValues; x++) {
printf("%S\n", (BSTR)col.pADsValues[x].CaseIgnoreString);
}
}
I'm getting a memory leak when calling a .Net dll from C++ in the function below. I'm thinking it is with the SafeArray declaration but I don't know how else to release the memory besides SafeArrayDestroyDescriptor. What else am I missing?
VARIANT_BOOL SendPack(IDotNetDll* wb, WW_POLL pl)
{
HRESULT hr = 0;
VARIANT_BOOL bretval;
BYTE destination = pl.uDest;
BYTE raw[2];
raw[0] = pl.uAPI;
raw[1] = pl.uOpcode;
SAFEARRAY* bytes = NULL;
hr = SafeArrayAllocDescriptor(1, &bytes);
bytes->cbElements = sizeof(raw[0]);
bytes->rgsabound[0].cElements = sizeof(raw);
bytes->rgsabound[0].lLbound = 0;
bytes->pvData = raw;
bytes->fFeatures = FADF_AUTO | FADF_FIXEDSIZE;
wb->SendMessage(destination,(BYTE)4, bytes, VARIANT_FALSE,
200.0,&bretval);
SafeArrayDestroyDescriptor(bytes);
return bretval;
}
Edit:
I also tried this method using a variant
VARIANT_BOOL SendPacket(IDotNetDll* wb, WW_POLL pl)
{
HRESULT hr = 0;
VARIANT_BOOL bretval;
_variant_t var;
void * pArrayData = NULL;
var.vt = VT_ARRAY | VT_UI1;
SAFEARRAYBOUND rgsabound1[1];
rgsabound1[0].cElements = 5;
rgsabound1[0].lLbound = 0;
var.parray = SafeArrayCreate(VT_UI1, 1, rgsabound1);
BYTE destination = pl.uDest;
SafeArrayAccessData(var.parray, &pArrayData);
BYTE raw[5];
raw[0] = pl.uAPI;
raw[1] = pl.uOpcode;
raw[2] = pl.uPayLoad[0];
raw[3] = pl.uPayLoad[1];
raw[4] = pl.uPayLoad[2];
memcpy(pArrayData, raw, 5);
SafeArrayUnaccessData(var.parray);
//Send packet
wb->SendMessage(destination,
(BYTE)4,
var.parray,
VARIANT_FALSE,
200.0, &bretval);
var.Clear();
return bretval;
}
Here is the memory usage from VS 2015 profiler
I have a piece of HTML source like this:
<FONT color=#5a6571>Beverly Mitchell</FONT> <FONT color=#5a6571>Shawnee Smith</FONT> <FONT color=#5a6571>Glenn Plummer</FONT> <NOBR>more >></NOBR>
I tried to retrieve the "color" value, like this:
MSHTML::IHTMLDocument2Ptr htmDoc1 = NULL;
SAFEARRAY *psaStrings1 = SafeArrayCreateVector(VT_VARIANT, 0, 1);
CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**) &htmDoc1);
VARIANT *param1 = NULL;
HRESULT hr = SafeArrayAccessData(psaStrings1, (LPVOID*)¶m1);
param1->vt = VT_BSTR;
param1->bstrVal = SysAllocString(varSrc1.bstrVal);
hr = SafeArrayUnaccessData(psaStrings1);
hr = htmDoc1->write(psaStrings1);
MSHTML::IHTMLElementPtr pElemBody1 = NULL;
MSHTML::IHTMLDOMNodePtr pHTMLBodyDOMNode1 =NULL;
hr = htmDoc1->get_body(&pElemBody1);
if(SUCCEEDED(hr))
{
hr = pElemBody1->QueryInterface(IID_IHTMLDOMNode,(void**)&pHTMLBodyDOMNode1);
if(SUCCEEDED(hr))
{
ProcessDomNodeSmartWrapper(pHTMLBodyDOMNode1, ProcTgtTagStrVec);
}
}
long lLength = 0;
MSHTML::IHTMLElementCollectionPtr pElemColl1 = NULL;
MSHTML::IHTMLElementPtr pChElem1 = NULL;
MSHTML::IHTMLStylePtr pStyle1 = NULL;
IDispatchPtr ppvdisp1 = NULL;
hr = htmDoc1->get_all(&pElemColl1);
hr = pElemColl1->get_length(&lLength);
for(long i = 0; i < lLength; i++)
{
_variant_t name(i);
_variant_t index(i);
ppvdisp1 = pElemColl1->item(name, index);
if(ppvdisp1 && SUCCEEDED(hr))
{
hr = ppvdisp1->QueryInterface(IID_IHTMLElement, (void **)&pChElem1);
if(pChElem1 && SUCCEEDED(hr))
{
BSTR bstrTagName = NULL;
pChElem1->get_tagName(&bstrTagName);
hr = pChElem1->get_style(&pStyle1);
if(pStyle1 && SUCCEEDED(hr))
{
_variant_t varFtCol;
hr = pStyle1->get_color(&varFtCol);
if(hr = S_OK && varFtCol)
{
hmStyles1[wstring(varFtCol.bstrVal)] = L"FontColor";
}
}
if(bstrTagName)
SysFreeString(bstrTagName);
} // if pStyle && SUCCEEDED(hr)
}//if ppvdisp && SUCCEEDED(hr)
}//for
But I can never get the "color" value - varFtCol.bstrVal is a bad pointer when I debug the program. This is what varFtCol showed when I debug the program:
- varFtCol {???} _variant_t
- tagVARIANT BSTR = 0x00000000 tagVARIANT
vt 8 unsigned short
- BSTR 0x00000000 wchar_t *
CXX0030: Error: expression cannot be evaluated
#5a6571 is a hex color represents for RGB value of (90,101,113).
How can I get this color info?
You shouldn't be getting style on pChElem1 because the color is not part of style in your case. Color is part of Font element.
Instead you must call pChElem1->getAttribute("color" . . .)
This will return #5a6571
The following code is in MFC. But you can easily convert to regular Win32 if you are not using MFC.
COLORREF GetColorFromHexString( CString szColor )
{
TCHAR *szScan;
CString strTemp;
CString strColor = szColor;
long lRR = 0,lGG = 0,lBB = 0;
//first we will remove # characters which come from XML document
strColor.TrimLeft(_T('#'));
strColor.TrimRight(_T('#'));
//it should be of the form RRGGBB
if (strColor.GetLength() == 6) {
//get red color, from the hexadecimal string
strTemp = strColor.Left(2);
lRR = _tcstol(LPCTSTR(strTemp),&szScan,16);
//get green color
strTemp = strColor.Mid(2,2);
lGG = _tcstol(LPCTSTR(strTemp),&szScan,16);
//get blue color
strTemp = strColor.Right(2);
lBB = _tcstol(LPCTSTR(strTemp),&szScan,16);
}
return RGB(lRR,lGG,lBB);
}
According to the MSDN documentation, IHTMLStyle::get_color may return either a BSTR or an integer value in the variant. Have you tried assigning varFtCol into an integer value and examining that result?
const int colorValue = static_cast<int>(varFtCol);
As a recommendation, when working with _variant_t, it is usually best to use the built-in casting operators than to direct access the members of the union itself.
I am trying to call a mthod that takes 2 strings here you are the code
VARIANT vArgs[2];
VariantInit(&vArgs[0]);
VariantInit(&vArgs[1]);
//VariantInit(&vArgs[2]);
//vArgs[2].pdispVal = pDisptEntries;
vArgs[1].bstrVal = bstrSrc;
vArgs[0].bstrVal = bstrtrgt;
vArgs[0].vt = VT_BSTR;
vArgs[1].vt = VT_BSTR;
//vArgs[2].vt = VT_DISPATCH;
dpEntry.rgvarg = vArgs;
dpEntry.cArgs = 2;
dpEntry.cNamedArgs = 0;
//dpEntry.rgdispidNamedArgs = new DISPID[2];
//dpEntry.rgdispidNamedArgs[0] = 0;
//dpEntry.rgdispidNamedArgs[1] = 1;
UINT index = -1;
EXCEPINFO ex;
hr = pDisptEntries->Invoke(dispid_Add, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD,
&dpEntry, NULL, &ex, &index);
The error code unequivocally tells you that the function does not in fact take two arguments of type string. Getting the dispid wrong is possible too, it will call the wrong function. Watch out for the return value, not sure what happens when you pass NULL but the function returns a value.