2

Note:

  • Attempting to invoke a method of an interface, of which the return type is _variant_t

Code:

_variant_t resultsDataString;

_bstr_t simObjectNames;

simObjectNames = SysAllocString (L"TEST example 3");

resultsDataString = pis8->GetSimObject (simObjectNames);

inline function illustrated below, contained in .tli FILE:

inline _variant_t IS8Simulation::GetSimObject ( _bstr_t Name ) {
    VARIANT _result;
    VariantInit(&_result);
     HRESULT _hr = get_SimObject(Name, &_result);
   if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _variant_t(_result, false);
}

Note:

  • resultsDataString is of struct tagVARIANT:
    • VARTYPE vt is 9 (unsigned short)
    • IDispatch IDispatch interface pointer

Question

  • How can I then convert or extract the value?
  • Possible using VariantChangeType?
  • Another way?

Edit:

Note:

  • Looking to transform the following, Visual Basic code to Visual C++

  • MFC or ATL, if need be

  • Ideally, pure C++

Visual basic equivalent:

Public example1, example2 As SIMUL8.S8SimObject
Dim numberOfexamples As Variant
Dim resultString As Variant

Set example1 = MySimul8.SimObject("Example 1")
Set example2 = MySimul8.SimObject("Example 2")

numberOfexamples = example1.CountContents + example2.CountContents

resultString = CStr(numberOfexamples) & "*"
MPelletier
  • 16,256
  • 15
  • 86
  • 137
Aaron
  • 2,823
  • 9
  • 44
  • 57

4 Answers4

2

Take a look at the MSDN docs for _variant_t

There is a ChangeType method as well as Extractors.

Edit: Once you have an IDispatch pointer, you need to use QueryInterface to get a specific object type with members and methods, or use IDispatch::Invoke. You can look at the MSDN Docs for IDispatch. Either way, you need to know about the object type that is being returned by the IS8Simulation::GetSimObject call.

Edit #2: Based on your VB code update, you want to use the same kind of code for C++, i.e. use the S8SimObject type in its C++ form (look in the generated .tlh file for _COM_SMARTPTR_TYPEDEF). Then you can directly do the same thing with CountContents. Otherwise, you would need to look up the CountContents DISPID with IDispatch::GetIDsOfNames and then call invoke.

crashmstr
  • 28,043
  • 9
  • 61
  • 79
  • Once the IDispatch interface pointer is extracted, is it possible to extract a value: some string, int etc...? – Aaron Mar 04 '09 at 12:36
  • pDispatch->Invoke (DISPID_VALUE, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET | DISPATCH_METHOD, &dispParamsNoArgs, &varResolved, NULL, NULL); - varResolved is "Empty"??? – Aaron Mar 04 '09 at 13:20
  • Did Invoke return S_OK, or an error value? And make sure your parameters are all correct. The first parameter looks fishy. Use IDispatch::GetIDsOfNames to get the IDs based on string names of the properties. – crashmstr Mar 04 '09 at 13:51
  • hresult return "Member not found" – Aaron Mar 04 '09 at 13:58
  • @crashmstr: What parameter values would you recommend? regards – Aaron Mar 04 '09 at 14:01
  • I just updated my answer. You need to do a lookup of the ID (first parameter) using IDispatch::GetIDsOfNames – crashmstr Mar 04 '09 at 14:02
  • Hopefully there is a "dual" interface here - using IDispatch::Invoke directly is really, really ugly (as you've noticed). – Aardvark Mar 04 '09 at 14:23
  • No kidding. Thankfully, I never had to do anything directly with Invoke. But either way, MS made COM client development a whole lot cleaner and easier with VB than with C++. – crashmstr Mar 04 '09 at 14:25
  • IS8SimObject *pS8SimObject; hresult = pIClassFactory->CreateInstance( NULL, __uuidof(IS8SimObject), (LPVOID *)&pS8SimObject); – Aaron Mar 04 '09 at 15:21
  • struct __declspec(uuid("f049c9d1-9ca6-4a70-bcdf-13e66fae659d")) /* dual interface */ IS8Simulation; struct /* coclass */ S8SimObject;.........? – Aaron Mar 04 '09 at 15:36
  • @crashmstr Yeah VB6 was a super simple COM client. .NET COM support completely went off the rails IMOHO. I always liked using MIDL generated headers with ATL smart points. (I can't decide about #import... it's good and bad) – Aardvark Mar 04 '09 at 16:21
  • Also you may want to check that IDispatchPtr((IDispatch*)example2Var) will correctly reference count. You get +1 with the cast operator, not sure about the ctor of the IDispatchPtr? – Aardvark Mar 04 '09 at 18:08
2

It appears you are using C++ as a COM client by relying on the VC++ compiler's built-in COM support. To make coding the client "easier" you've used #import to generate C++ wrapper classes that attempt to hide all the COM details from you - or at least make the COM details simpler. So you're not using the COM SDK directly, but are using a client-side framework (I think of it like a light-weight COM-only framework akin to ATL or MFC).

Your example code, however, seems to be mixing the direct low-level COM SDK (VARIANTs, BSTR, SysAllocString) with the #import COM framework (_variant_t, _bstr_t, XXXXPtr). COM from C++ is complicated at first - so in a perfect world I would suggest getting to know the basics of COM before going too far forward.

However, if you just want something to work I would guess this is #import-style-of-COM-clients version of the VB code you provided:

_variant_t example1Var;
_variant_t example1Var;
SIMUL8::S8SimObjectQIPtr example1; // I'm guessing at this type-name from the VB code
SIMUL8::S8SimObjectQIPtr example2;

example1Var = pis8->GetSimObject(_bstr_t(L"Example 1"));
example2Var = pis8->GetSimObject(_bstr_t(L"Example 2"));
if (example1Var.vt == VT_DISPATCH && example2Var.vt == VT_DISPATCH)
{
   // **UPDATE** to try to spoon feed the QI ptr...
   example1 = IDispatchPtr((IDispatch*)example1Var);
   example2 = IDispatchPtr((IDispatch*)example2Var);
   // Does this screw-up reference counting?

   int numberOfexamples = example1->CountContents + example2->CountContents;

}

UPDATE:
Documentation on #import This makes using COM from C++ much easier, but is yet one other thing to learn...

bluish
  • 26,356
  • 27
  • 122
  • 180
Aardvark
  • 8,474
  • 7
  • 46
  • 64
  • ::S8SimObject *example1, *example2; resultsDataString = pis8->GetSimObject (simObjectNames); if (resultsDataString.vt == VT_DISPATCH) { example1 = (IDispatch *) resultsDataString; total_results example1->CountContents; } – Aaron Mar 04 '09 at 16:07
  • Please excuse my lack of knowledge...: error C2440: '=' : cannot convert from 'IDispatch *' to 'S8SimObject *' – Aaron Mar 04 '09 at 16:12
  • I think you'd want to use the #import smart pointers vs. a raw interface pointer. The smart pointers have "Ptr" appended to the end. #import also creates fancier versions of the smart pointer that will query interface for you on the = operator. ("QIPtr") – Aardvark Mar 04 '09 at 16:15
  • Does a variable called S8SimObjectQIPtr compile? – Aardvark Mar 04 '09 at 16:16
  • You might need to do something like: example1Var.QueryInterface(__uuidof(IS8SimObject), &example1); – crashmstr Mar 04 '09 at 16:20
  • I was under the impression that the "QI" smart pointer would work. Maybe the equal operator requires a smartpointer r-value. I'll edit my answer with another idea. – Aardvark Mar 04 '09 at 16:22
  • I try to keep the "real" com stuff from leaking thru the #import abstraction if possible... this is why I'm avoiding QueryInterface. – Aardvark Mar 04 '09 at 16:25
  • @Aardvark: S8SimObjectQIPtr does not compile. Regarding import: #import "S8.tlb" no_namespace – Aaron Mar 04 '09 at 16:33
  • error C2039: 'QueryInterface' : is not a member of '_variant_t' – Aaron Mar 04 '09 at 16:34
  • try ((IDispatch*)example1Var)->QueryInterface – crashmstr Mar 04 '09 at 16:42
  • Works now: _COM_SMARTPTR_TYPEDEF(IS8SimObject, __uuidof(IS8SimObject)); IS8SimObjectPtr example1; – Aaron Mar 04 '09 at 16:46
  • @Aardvark && @crashmstr: Thanks for all your help guys :)!!!!!!!! - ummm where can I learn more about this stuff??? – Aaron Mar 04 '09 at 16:48
  • "Inside Distributed COM" from Microsoft Press and "Essential COM" from Don Box were the two books I learned from. Both are over 10 years old, but not much has changed as far as I can tell. – crashmstr Mar 04 '09 at 17:07
  • Inside COM - Rogerson (Microsoft Press) is great for the basics. The books crashmstr mentions are good as well. I'll add a link to the answer for what "#import" brings to the table... – Aardvark Mar 04 '09 at 18:02
1

You don't have to convert the _variant_t. It contains an IDispatch interface pointer, so you can get it out like this:

if (v.vt == VT_DISPATCH)
{
    IDispatchPtr pDispatch = v.pdispVal;
    // Do something with pDispatch.
    // e.g. assign it to a strongly-typed interface pointer.
}
Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380
0

There is a concept of "value" for an object (DISPID_VALUE).

Here is a codeproject.com article on resolving VARIANTs that might help.

chrish
  • 2,352
  • 1
  • 17
  • 32