1

I have the following COM types: Project, ContainerItem and Node. Project has a collection property with an Append function that accepts ContainerItems.

In C#, using type libraries I can send a Node object to the Append function and the library works as expected:

var prj = new Project();
var node = new Node();
prj.collection.Append(node);

In C++ I tried a direct pointer cast expecting this is what C# was doing, but it ends up in an error:

ProjectPtr prj;
prj.CreateInstance(__uuidof(Project));
NodePtr node;
node.CreateInstance(__uuidof(Node));
prj->collection->Append((ContainerItem**)&node.GetInterfacePtr());

Is there a specific way to these type of COM pointer casts in C++? What am I missing?

3
  • 2
    C-style casts are not valid on COM interfaces, you must use QueryInterface(). C# does it automatically but you have to do it yourself in C++. Commented Jul 25, 2016 at 14:28
  • It seems very unlikely that Append takes ContainerItem** as a parameter. That would make no sense. What's the declaration of Append? What's the relationship, if any, between Node and ContainerItem? Commented Jul 25, 2016 at 23:04
  • @IgorTandetnik The definition is Container::Append(ContainerItem**, long*), the second parameter can be null by the way. I don't know the relationship between Node and ContainerItem as it is not represented in the .tli file the compiler creates from the COM DLL. Commented Jul 26, 2016 at 7:34

2 Answers 2

1

COM casting is done with the QueryInterface() method. The object is queried for its support of the interface (based on the GUID), and if the interface is supported, the internal reference counter is incremented (see AddRef()) and a pointer to the interface is returned. MSDN has more detail on the inner workings.

C++ does not directly support the code generation for the "COM cast" as C# does, but its implementation is simple enough.

struct bad_com_cast : std::runtime_error {
    bad_com_cast() : std::runtime_error("COM interface not supported") {}
};

template <class To, class From>
To* qi_cast(From* iunknown)
{
    HRESULT hr  = S_OK;
    To* ptr = NULL;
    if (iunknown) {
        hr = iunknown->QueryInterface(__uuidof(To), (void**)(&ptr));
        if (hr != S_OK) {
            throw bad_com_cast(); // or return NULL
        }
    }
    return ptr;
}

Using the above "cast", the sample can be implemented as follows;

ContainerItem* ci = qi_cast<ContainerItem>(node);
prj->collection->Append(&ci);

If the ATL library is being used, you can use ATL::CComQIPtr<> directly to obtain the equivalent semantics;

auto ci = CComQIPtr<ContainerItem>(node);
if (ci) {
    // ...
}
Sign up to request clarification or add additional context in comments.

Comments

0

Just like @HansPassant commented, I had to use the QueryInterface function:

ContainerItem* ci = nullptr;
node.QueryInterface(__uuidof(ContainerItem), ci);
prj->collection->Append(&ci);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.