C++, Hand over map as _variant_t to COM Component

Hi,

I have done some development in various languages, but am relatively new to c++. I need to access a COM object and hand over a hashtable to that object. I have written a working code in VBScript and tried to "convert" this to C++, with limited success ;-)

The VBScript is:
1
2
3
4
5
6
Set System = CreateObject("MYCOM.APPLICATION")
Dim PropTable
Set PropTable = CreateObject("System.Collections.Hashtable")
Call PropTable.Add("PORT", "COM17")
Call PropTable.Add("LANGUAGE", "en")' "en" or "de"
RetVal = System.setProperties(PropTable) 


So I am connectin to a COM Component and hand over a hashtable. Works.

Conneting to the Com-Component with CoCreateInstance I have managed and it works in principle. But I am really not sure how to hand over a hashtable. The signature of the method is:
inline VARIANT_BOOL System::setProperties ( const _variant_t & propertymap )

I tried something which compiles but gives me the exception "Interface not supported":
1
2
3
4
5
6
map<_bstr_t,_bstr_t>mymap;

mymap["PORT"] = "COM4";
mymap["LANGUAGE"] = "en";

m_diagSystem->setProperties(&mymap);



I guess a map just isn't a _variant_t, but also can't find a way to convert the map...

If it wokrs from VBScript it must work from C++ I guess, can anyone help me?
You are using the wrong object as a map. A Hashtable COM object is not the same as a std::map object, even though they are functionally the same. You must use a COM object.

By the way, that signature cannot possibly be the one found in the type library. The type _variant_t is a C++ class that encapsulates a VARIANT for easy working, and therefore cannot be specified as a data type for a method in a COM interface.

So, assuming that this is some auto-generated function signature, what you need to do is use CoCreateInstance() to instantiate a System.Collections.Hashtable object and then assign it to the pDispatch member of a VARIANT. In the case of _variant_t, you do this like this:

1
2
3
4
5
6
//Use a pure pointer or a C++ helper class like CComPtr<>.
IHashtable *pTable = NULL; //Assuming the interface for the hashtable object is called IHashtable.
HRESULT hr = CoCreateInstance(..., (LPVOID*)&pTable);
CheckHR(hr);
_variant_t theHashTable = pTable;
m_diagSystem->SetProperties(theHashTable);


In the sample, CheckHR() would be a placeholder for code to check the hr value to ensure the function call succeeds. The above should work OK.
Thank you very much for your answer!
I didn't answer yet, because I was away for a week - I think I will now manage to do this, thanks to yxour description.
I tried today to do this and ran into an error again, can you maybe help me once more?

I instanciated the hashtable via com. since the hashtable has no interface of its own but implements several interfaces i tried using the fitting one - IDictionary (both hashtable and IDictioary i got from mscorlib.tlb).

OK, compiles, Cocreateinstance works, but I still can't hand over the table:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
try
	{	
		 CLSID clsid;
		 HRESULT hr = CLSIDFromProgID(L"System.Collections.Hashtable", &clsid);
		 
		 mscorlib::IDictionaryPtr mymap;
		 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL,  mscorlib::IID_IDictionary, (void **)&mymap);
		// mymap.CreateInstance( mscorlib::IID_IDictionary);

		 ::MessageBox(NULL,L"got the hashtable from COM", L"setProperties", MB_ICONINFORMATION);

//works so far

		 _variant_t key = "PORT";
		 _variant_t val = "COM19";
		 mymap->Clear();
		 mymap->Add(key, val);

//seems to work, at least no exception

		 _variant_t mapVariant = mymap;
		 m_diagSystem->setProperties(mapVariant);

//BOOOM, „Interface not supported“,

		 ::MessageBox(NULL,L"table handed over", L"setProperties", MB_ICONINFORMATION);
		 

	 }
	catch( _com_error& ex )
	{
		LPCTSTR errMsg = ex.ErrorMessage();
		::MessageBox(NULL,errMsg, L"setProperties", MB_ICONERROR);
	}



I get the exception "Interface not supported" at line setProperties()
But if hashtable implements IDictionary it should be ok, or not?

I use the class Hashtable (its CLSID) and the interface IDictionary (IID), think this should be ok ...
What am I missing?

Your posted code is not C++.
Well, most of it appears to be C++, but yes, the use of mscorlib::IID_IDictionary suggests that you are programming C++/CLI. C++/CLI looks and feels like C++ sometimes, but it is definitely NOT C++. If you are using C++/CLI, all bets are off here. We don't know C++/CLI for the most part and you should ask this in the MSDN forums.

But well, assuming the CLI part is neglible here, or non-existent and the "mscorlib" part is merely the namespace auto-generated by the #import directive, then let me ask this: What's the type expected by the parameter in setProperties()? Show us the IDL of this other COM object.
Hi,

Thanks!
Well, the COM dll I imported was written in .NET, that's why it wants a datatype from mscorlib I guess. So I imported mscorlib.tlb

setProperties expects a _variant_t, this I have in the generated .tli file:
1
2
3
4
5
6
inline VARIANT_BOOL DiagSystem::setProperties ( const _variant_t & propertymap ) {
    VARIANT_BOOL _result = 0;
    HRESULT _hr = raw_setProperties(propertymap, &_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}

Last edited on
Hi,

just some additional info:
i tried now to add this to my code:
1
2
_variant_t result =  mymap->GetItem("PORT");
 ::MessageBox(NULL,result.bstrVal, L"Result", MB_ICONINFORMATION);

Displays "COM19" in a msgbox.

So i am really quite sure that instanciation of the hashtable worked fine and the IDictionary interface is working for it. so the error seems to be in those 2 lines:
1
2
_variant_t mapVariant = mymap;
m_diagSystem->setProperties(mapVariant);


I guess the "Interface not supported" is the catch-all error here, because if i try to put juist a string or something stupid in the _varaint_t I still get the same error. Just when I tried to hand over mymap.GetAddress() it said "Invalid pointer"
Strange. I'm not the brightest when it comes to C++ classes, so maybe line #21 above is creating a variant of a different variant type than expected. After all, there are many constructors (http://msdn.microsoft.com/en-us/library/k74e1xsh(VS.80).aspx ) and assignment may be picking the wrong one.

I would try using an explicit IDispatch pointer to see if the error message goes away. I am of course assuming that setProperties() works OK with an IDispatch pointer. If not, try with an explicit IUnknown pointer.

To obtain an explicit IDispatch pointer, the orthodox way would be to call QueryInterface() on mymap, or an equivalent, like using ATL's CComQIPtr<IDispatch>.
I have the solution, I am not sure if this is the ideal way, but it works:

Instead of this 2 lines:
1
2
_variant_t mapVariant = mymap;
m_diagSystem->setProperties(mapVariant);


I have now:
1
2
3
IDispatch* test = mymap;
_variant_t mapVariant = test;
 m_diagSystem->setProperties(mapVariant);


So a conversion to IDispatch*. I first normally use it as IDictionaryPtr, then I just put it in an IDispatch* and it works. No QueryInterface was even needed, tried it also, makes no difference ...
Last edited on
Ah, see? Explicit pointer did the trick. I was right in thinking the wrong constructor was being used.

Yes, due to how vtables are constructed, I don't think you need to QueryInterface. But this may not work on all objects. I guess the trick works by coincidence and it may not be guaranteed to work on all COM objects from a variety of compilers. If I were you, I'd just QueryInterface() to be in the safe side. It is not even for the ref count, it just to avoid problems with compiler differences.

Also COM aggregation may be implementing a particular interface in an entirely different object, so yes, stick to QueryInterface().
Topic archived. No new replies allowed.