0

I have written a c++ MFC DLL that brings up an SDI Application which is a very legacy OLE Server. (I have no choice about using this OLE Server so I have to make it work.)

I am accessing this c++ DLL from C#.

I have everything "working". I can call methods of the DLL, I have implemented C# delegates correctly, etc.

I can call methods of the OLE Server directly in C++ and export these so that my C# application call call them too. This is my "Hello World" for accessing the OLE Server functionality in its entirety from C#.

So far so good.

The next step is to make this C++ DLL as much of a "pass-through" as possible so that C# developers can write business logic around this OLE server. If changes or updates happen from the maker of the OLE Server, we do not want to have to update C++ code, we want to respond to the changes in C#.

So even though I can implement methods in C++ using the imported class from the OLEServer.tlb file and pass these through to C#, we do not want to ultimately do this. I am trying to call methods through the IDispatch interface instead and I am running into difficulties that I can't quite understand.

I am using Visual Studio 2010 for both unmanaged C++ and C#.

VARIANT LaserCat::LaserCatCommand(LPCTSTR* p_pMethodNameAndParamsInReverseOrder, UINT p_Count)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState());
  VARIANT result;
  VARIANT* pResult = NULL;
  VariantInit(&result);
  HRESULT hr = NULL;
  DISPID dispid;
  const IID IID_ITriad = {0x60EE772D,0xE076,0x4F58,{0xA8,0xB4,0x2F,0x7A,0x29,0xBB,0x02,0x50}};
  COleException* pError = NULL;
  BOOL HasDispatch = theApp.pTriadView->pTriadItem->Catalog.CreateDispatch(IID_ITriad, pError);
  LPDISPATCH iDisp = theApp.pTriadView->pTriadItem->Catalog.m_lpDispatch;
  LPOLESTR Names[1] = {(LPOLESTR)L"GetInterfaceVersion"};
  hr = iDisp->GetIDsOfNames(IID_NULL, Names, 1, LOCALE_SYSTEM_DEFAULT, &dispid);

  if (hr != S_OK) return result;

  DISPPARAMS* pParams = new DISPPARAMS();
  short maj = 0;
  short min = 0;
  short* nMajor = &maj;
  short* nMinor = &min;
  VARIANTARG Args[2];
  VariantInit(&Args[0]);
  VariantInit(&Args[1]);
  Args[0].piVal = nMinor;
  Args[0].vt = VT_BYREF;
  Args[1].piVal = nMajor;
  Args[1].vt = VT_BYREF;
  pParams->rgvarg = Args;
  pParams->cNamedArgs = 0;
  pParams->cArgs = 2;
  pParams->rgdispidNamedArgs = NULL;
  EXCEPINFO* pExcept = NULL;
  UINT* pArgErrorIndex = NULL;
  LPCTSTR Error = NULL;

  hr = iDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, pParams, pResult, pExcept, pArgErrorIndex);
  if (pExcept != NULL || pArgErrorIndex != NULL || hr != S_OK)
  {
    Error = _T("Error");
    return result;
  }
  result = *pResult;
  return result;
}

The following line from above gives me a "Bad variable type" error:

  hr = iDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, pParams, pResult, pExcept, pArgErrorIndex);

BTW, I am sure that the code can be vastly improved, it has been years since I have written in C++ and this is just a "first kick" at getting this working so no real error handling etc.

I have toyed around with "Args[]" type and value with variations of "Type Mismatch" errors and "Null Reference Pointers"

The function that is imported by the .tlb file looks like this:

  short GetInterfaceVersion(short * nMajor, short * nMinor)
  {
    short result;
    static BYTE parms[] = VTS_PI2 VTS_PI2 ;
    InvokeHelper(0x178, DISPATCH_METHOD, VT_I2, (void*)&result, parms, nMajor, nMinor);
    return result;
  }

Oh, although I am passing in the "pMethodNameAndParamsInReverseOrder" as a parameter, I am just hard-coding it to get this one simple method working. Once I have it working with this and a few other methods, I am planning on making this generic to handle any methods implemented by the COM interface via IDispatch.

Any help would be appreciated mostly in getting this working but I would also appreciate any pointers on improving the code, I have mouch to learn in C++

Thank-You!

BTW, If this helps clarify things, theApp.pTriadView->pTriadItem->Catalog is the COM class I am implementing

EDIT:

Thanks to @HansPassant (see first comment) I see what I was missing. Unfortunately I have hit a downstream result of fixing that. The VARIANT "pResult" is coming back empty. I will continue to hunt that down now but any thoughts would be welcome :)

user3739214
  • 81
  • 1
  • 6
  • Args[0].vt = VT_BYREF | VT_I2; What you are doing is very, very pointless. Use the wrapper or use C#. – Hans Passant Jun 30 '14 at 17:23
  • @HansPassant I think I understand why you say that. My company wants to be able to essentially pass a method name and some arguments from c# and IF it is implemented by the OLE server, have it call the function, interpret the results enough to send them back as meaningful types to C# and have all business logic on the .NET side. – user3739214 Jun 30 '14 at 19:14
  • @HansPassant If methods are added or modified by the makers of the OLE server, they do not want to have to respond to them in C++, they simply want to change or add to what is being passed from C#. We are only taking this approach because this particular vendor won't move away from an OLE server and the ONE event we need to hook into is simply not exposed by the COM object in a way that .NET can respond to. We need the COleClientItem and COleDispatchDriver. Given these constraints, is there anything I can do to access via IDispatch.GetIDsOfNames and IDispatch.Invoke? – user3739214 Jun 30 '14 at 19:15
  • @HansPassant OH! I misunderstood your comment! Args[].vt = VT_BYREF | VT_I2 worked! I did not realize this was your "answer". I agree with you but have little choice. Now I have hit another issue with the result of the call, I will edit my question to reflect it. – user3739214 Jun 30 '14 at 20:21

0 Answers0