Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to destroy COM object created by QAxObject?

How to destroy COM object created by QAxObject?

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 3 Posters 1.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Max Gabler
    wrote on last edited by Max Gabler
    #1

    Hello,

    I'm struggling for quiet some time now but couldn't figure out how instances of an COM object which were created via QAxObject::setControl() could be destroyed.
    The documentation of the QAxObject destructor states: "Releases the COM object and destroys the QAxObject, cleaning up all allocated resources.".

    So, without creating any copies etc. of this object, its lifetime should end when the QAxObject instance is destroyed. BUT: As I've found out the COM object survives the QAxObjects desctructor and lives on until the calling app is shutdown completely.

    I've extracted the following simplified example to demonstrate said behaviour:

    The main.ccp loads the COM object, calls a method and than destroys it:

    // main.cpp
    #include <QAxObject>
    #include <QCoreApplication>
    #include <QDebug>
    
    #include <combaseapi.h>
    
    int main(int argc, char *argv[])
    {
        qInfo("main: Starting test...");
    
        QCoreApplication a(argc, argv);
    
        if (SUCCEEDED(CoInitializeEx(0, COINIT_MULTITHREADED))) {
    
            qInfo("main: CoInitializeEx() succeeded");
    
            QAxObject *axObject = new QAxObject();
    
            axObject->setControl("MyComObject.ComObject");
    
            axObject->dynamicCall("SayHello");
    
            delete axObject;
    
            qInfo("main: QAxObject deleted");
    
            CoUninitialize();
    
        } else {
            qFatal("main: CoInitializeEx() failed");
        }
    
        qInfo("main: Done...");
    
        return a.exec();
    }
    

    The following code shows a very simple example of the COM objects implementation in C#:

    using System;
    using System.Runtime.InteropServices;
    
    namespace MyComObject
    {
        [Guid("8157be30-e99a-4b14-b88d-bc8db831047b")]
        public interface IComObject
        {
            [DispId(1)]
            void SayHello();
        }
    
        [Guid("6c080298-7b10-494b-84af-38b6db12a7d4"),
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        public interface ComObjectEvents
        {
        }
    
        [Guid("63d3c24d-2a60-4338-b312-63a771c580bd"),
        ClassInterface(ClassInterfaceType.None),
        ComSourceInterfaces(typeof(ComObjectEvents))]
        public class ComObject : IComObject
        {
            public ComObject()
            {
                Console.WriteLine("ComObject: Object created");
            }
    
            ~ComObject()
            {
                Console.WriteLine("ComObject: Object destroyed");
            }
    
            public void SayHello()
            {
                Console.WriteLine("ComObject: Hello!");
            }
        }
    }
    

    Running the main program produces the following output:

    main: Starting test...
    main: CoInitializeEx() succeeded
    ComObject: Object created
    ComObject: Hello!
    main: QAxObject deleted
    main: Done...
    

    What I'm missing here is the output from the COM objects destructor, which tells me that the object isn't destroyed at all.
    Adding "QTimer::singleShot(0, &a, SLOT(quit()));" just before "a.exec()" the program actually terminates and produces "ComObject: Object destroyed" as the very last line of the output.

    My question now is: How is an instance of a COM object which was created by QAxObject::setControl() destroyed properly?
    Did I miss something here?

    JonBJ 1 Reply Last reply
    0
    • M Max Gabler

      Hello,

      I'm struggling for quiet some time now but couldn't figure out how instances of an COM object which were created via QAxObject::setControl() could be destroyed.
      The documentation of the QAxObject destructor states: "Releases the COM object and destroys the QAxObject, cleaning up all allocated resources.".

      So, without creating any copies etc. of this object, its lifetime should end when the QAxObject instance is destroyed. BUT: As I've found out the COM object survives the QAxObjects desctructor and lives on until the calling app is shutdown completely.

      I've extracted the following simplified example to demonstrate said behaviour:

      The main.ccp loads the COM object, calls a method and than destroys it:

      // main.cpp
      #include <QAxObject>
      #include <QCoreApplication>
      #include <QDebug>
      
      #include <combaseapi.h>
      
      int main(int argc, char *argv[])
      {
          qInfo("main: Starting test...");
      
          QCoreApplication a(argc, argv);
      
          if (SUCCEEDED(CoInitializeEx(0, COINIT_MULTITHREADED))) {
      
              qInfo("main: CoInitializeEx() succeeded");
      
              QAxObject *axObject = new QAxObject();
      
              axObject->setControl("MyComObject.ComObject");
      
              axObject->dynamicCall("SayHello");
      
              delete axObject;
      
              qInfo("main: QAxObject deleted");
      
              CoUninitialize();
      
          } else {
              qFatal("main: CoInitializeEx() failed");
          }
      
          qInfo("main: Done...");
      
          return a.exec();
      }
      

      The following code shows a very simple example of the COM objects implementation in C#:

      using System;
      using System.Runtime.InteropServices;
      
      namespace MyComObject
      {
          [Guid("8157be30-e99a-4b14-b88d-bc8db831047b")]
          public interface IComObject
          {
              [DispId(1)]
              void SayHello();
          }
      
          [Guid("6c080298-7b10-494b-84af-38b6db12a7d4"),
          InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
          public interface ComObjectEvents
          {
          }
      
          [Guid("63d3c24d-2a60-4338-b312-63a771c580bd"),
          ClassInterface(ClassInterfaceType.None),
          ComSourceInterfaces(typeof(ComObjectEvents))]
          public class ComObject : IComObject
          {
              public ComObject()
              {
                  Console.WriteLine("ComObject: Object created");
              }
      
              ~ComObject()
              {
                  Console.WriteLine("ComObject: Object destroyed");
              }
      
              public void SayHello()
              {
                  Console.WriteLine("ComObject: Hello!");
              }
          }
      }
      

      Running the main program produces the following output:

      main: Starting test...
      main: CoInitializeEx() succeeded
      ComObject: Object created
      ComObject: Hello!
      main: QAxObject deleted
      main: Done...
      

      What I'm missing here is the output from the COM objects destructor, which tells me that the object isn't destroyed at all.
      Adding "QTimer::singleShot(0, &a, SLOT(quit()));" just before "a.exec()" the program actually terminates and produces "ComObject: Object destroyed" as the very last line of the output.

      My question now is: How is an instance of a COM object which was created by QAxObject::setControl() destroyed properly?
      Did I miss something here?

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @Max-Gabler
      QAxObject::QAxObject(QObject *parent = nullptr)
      Creates an empty COM object and propagates parent to the QObject constructor. To initialize the object, call setControl.

      QAxObject::~QAxObject()
      Releases the COM object and destroys the QAxObject, cleaning up all allocated resources.

      Maybe not your finding, just saying the docs imply destructor does the job for you?

      Also void QAxBase::clear() says "Disconnects and destroys the COM object.", don't suppose that's any better for you?

      1 Reply Last reply
      0
      • M Offline
        M Offline
        Max Gabler
        wrote on last edited by
        #3

        "the docs imply destructor does the job for you" <-- That is exactly, what I've expected. But it turned out that this wasn't the case.
        I already tried calling QAXBase::clear() before deletion without any change as the destructor calls it anyways...

        JonBJ 1 Reply Last reply
        0
        • M Max Gabler

          "the docs imply destructor does the job for you" <-- That is exactly, what I've expected. But it turned out that this wasn't the case.
          I already tried calling QAXBase::clear() before deletion without any change as the destructor calls it anyways...

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #4

          @Max-Gabler
          Could be wrong, but I don't expect you to get a better answer here. Can only suggest: look at source code and maybe raise as "bug" over at Qt bug forum in the hope that somebody there can answer better?

          1 Reply Last reply
          0
          • hskoglundH Offline
            hskoglundH Offline
            hskoglund
            wrote on last edited by
            #5

            Hi, just a guess, but doesn't this occur because C# does not delete objects deterministically, i.e. you have to wait for the garbage collector to kick in (or in your case for the program to terminate).

            One workaround is to call the Dispose method in C# explicitly so that the COM object is destroyed directly, add something like this to your C# code:

            .... 
            // after SayHello()l
            [DispId(0x10000002)]
            void Dispose();
            }
            
            ...
            // after SayHello() imp.
            public void Dispose()
            {
                 Console.WriteLine("ComObject: disposed");   
            }
            

            and in your C++ code add a dispose call after SayHello:

            ...
            axObject->dynamicCall("SayHello");
            axObject->dynamicCall("Dispose");
            ...
            

            Note I have written maybe 20 lines of C# code during the last 20 years, so I'm not a C# expert :-)

            JonBJ 1 Reply Last reply
            0
            • hskoglundH hskoglund

              Hi, just a guess, but doesn't this occur because C# does not delete objects deterministically, i.e. you have to wait for the garbage collector to kick in (or in your case for the program to terminate).

              One workaround is to call the Dispose method in C# explicitly so that the COM object is destroyed directly, add something like this to your C# code:

              .... 
              // after SayHello()l
              [DispId(0x10000002)]
              void Dispose();
              }
              
              ...
              // after SayHello() imp.
              public void Dispose()
              {
                   Console.WriteLine("ComObject: disposed");   
              }
              

              and in your C++ code add a dispose call after SayHello:

              ...
              axObject->dynamicCall("SayHello");
              axObject->dynamicCall("Dispose");
              ...
              

              Note I have written maybe 20 lines of C# code during the last 20 years, so I'm not a C# expert :-)

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #6

              @hskoglund
              Hi. Isn't the point of @Max-Gabler's question that he has something which works in C# but he is trying to make it as good as that in C++? That is what I understand it to be.

              and in your C++ code add a dispose call after SayHello:

              Ohhh. So it's a mixed application with C++ calling C# ?

              hskoglundH 1 Reply Last reply
              0
              • JonBJ JonB

                @hskoglund
                Hi. Isn't the point of @Max-Gabler's question that he has something which works in C# but he is trying to make it as good as that in C++? That is what I understand it to be.

                and in your C++ code add a dispose call after SayHello:

                Ohhh. So it's a mixed application with C++ calling C# ?

                hskoglundH Offline
                hskoglundH Offline
                hskoglund
                wrote on last edited by
                #7

                @JonB said in How to destroy COM object created by QAxObject?:

                Ohhh. So it's a mixed application with C++ calling C# ?

                Yeah, COM is supposed to be language agnostic so it should work, but I'm guessing C#'s nondeterministic object lifetime is the culprit..

                1 Reply Last reply
                1
                • M Offline
                  M Offline
                  Max Gabler
                  wrote on last edited by Max Gabler
                  #8

                  @JonB : Yes, it's a mixed app as it relays onto some 3rd party libs that are only available in C#. Not nice, but without alternative for now...

                  @hskoglund : I've been convinced that you were on to something until I learned, that Dispose-Pattern is very useful to free unmanaged resources immediately instead of wating for the garbage collector. But there is no way in C# for an object to destroy itself and free the used memory leaving nothing but a dangling pointer behind...

                  I dug a little deeper into the topic and it seems, that this isn't Qt related. I tried the same, using only standard C++ and the ComBaseApi (combaseapi.h), and got pretty much to the same result. The COM objects reference count actually drops to zero as it is supposed.
                  Here I learned tha when creating a COM object a Runtime Callable Wrapper (RCW) is created to own the object and hold a strong reference to it which could possibly prevent tha GC from doing it's job. I guess I will have a closer look on that...

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved