C# COM object not being released

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • techie

    C# COM object not being released

    I have a VC++ client that creates a COM object that is implemented in C#.
    The problem is the COM object doesn't get released, so I get a memory leak.

    I have no problems creating the COM object and getting a pointer to the
    required interface (IATImportFilte r) in the client:

    COleDispatchDri ver* resultInterface = new COleDispatchDri ver;
    if ( resultInterface == NULL ) return FALSE;
    COleException oOleException;
    BOOL success = resultInterface->CreateDispatch ( progID, &oOleExcepti on );

    IUnknown *p;
    if ( resultInterface->m_lpDispatch->QueryInterface ( *pRequiredInter face,
    (void**)&p ) != S_OK )

    where prodID ("SI.ValidatorI mport") is the name of COM object.



    My C# COM object class is declared like this:

    [ClassInterface( ClassInterfaceT ype.None)]
    [ProgId("SI.Vali datorImport")]
    public class ValidatorImport : IATImportFilter
    {

    The object is being released in the client like this:

    ((IUnknown*) pIF)->Release();
    pIF = NULL;

    The call to Release() on the interface does not actually destroy the object.
    It doesn't call the destructor of my COM object. I tried putting a
    breakpoint in there and even added a MessageBox() call but the program just
    doesn't go in there.

    Not sure why this happening. Since every interface supports IUnkown,
    Release() should release the COM object. The COM object is only created
    once.

    I'm new to C# so I would appreciate some help.


  • Mattias Sjögren

    #2
    Re: C# COM object not being released

    >The call to Release() on the interface does not actually destroy the object.[color=blue]
    >It doesn't call the destructor of my COM object.[/color]

    Since it's a managed object it will eventually be cleaned up by the
    garbage collector.



    Mattias

    --
    Mattias Sjögren [MVP] mattias @ mvps.org
    http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
    Please reply only to the newsgroup.

    Comment

    • Steve Walker

      #3
      Re: C# COM object not being released

      In message <jrKdnds2mpw3Bi nfRVn-3w@biscit.net>, techie
      <techie@nospam. biz> writes[color=blue]
      >The call to Release() on the interface does not actually destroy the object.
      >It doesn't call the destructor of my COM object. I tried putting a
      >breakpoint in there and even added a MessageBox() call but the program just
      >doesn't go in there.
      >
      >Not sure why this happening. Since every interface supports IUnkown,
      >Release() should release the COM object. The COM object is only created
      >once.[/color]

      As I understand it, when you call Release(), you're calling it on a
      COM-callable wrapper, not on the object itself. The COM callable wrapper
      reference counts as you would expect a COM object to. When all
      references are released, it releases its own reference to the object you
      wrote in C#, which is then available for garbage collection. This will
      happen at some point, but not necessarily when you expect it to. So, you
      don't get deterministic finalisation of your C# object. You *may* get DF
      of the wrapper, the documentation I've seen isn't clear on whether it is
      destroyed immediately you are done with it It's pinned on a non GC'd
      heap, again AIUI, so presumably it is destroyed immediately in the
      normal COM fashion.

      You probably don't have a memory leak, you more than likely have an
      unreferenced object sat waiting to be GC'd.

      --
      Steve Walker

      Comment

      • techie

        #4
        Re: C# COM object not being released

        "Steve Walker" <steve@otolith. demon.co.uk> wrote in message
        news:pIclE0SeWL tCFwvW@otolith. demon.co.uk...[color=blue]
        > You probably don't have a memory leak, you more than likely have an
        > unreferenced object sat waiting to be GC'd.[/color]

        Is there any I can force GC? Maybe if my class also implements IDisposable
        I can call dispose from my client?


        Comment

        • techie

          #5
          Re: C# COM object not being released

          "Mattias Sjögren" <mattias.dont.w ant.spam@mvps.o rg> wrote in message
          news:ufwVq7FdFH A.580@TK2MSFTNG P15.phx.gbl...[color=blue][color=green]
          > >The call to Release() on the interface does not actually destroy the[/color][/color]
          object.[color=blue][color=green]
          > >It doesn't call the destructor of my COM object.[/color]
          >
          > Since it's a managed object it will eventually be cleaned up by the
          > garbage collector.[/color]

          The call to Release() on my interface pointer returns 0 - the reference
          count on the interface - which indicates the COM object should have been
          released or at least queued to be released by the GC. When VC++ debugger
          halts it reports a lot of memory leaks which worries me.

          Can I assume the COM object is released by the GC eventually? It must
          happen some time after my VC++ client closes. Is there any way of forcing
          the GC to delete my object immediately from my client?


          Comment

          • Mattias Sjögren

            #6
            Re: C# COM object not being released

            [color=blue]
            >The call to Release() on my interface pointer returns 0 - the reference
            >count on the interface - which indicates the COM object should have been
            >released or at least queued to be released by the GC. When VC++ debugger
            >halts it reports a lot of memory leaks which worries me.[/color]

            The COM callable wrapper will be deleted, but the managed object is
            just eligible for GC.

            [color=blue]
            >Can I assume the COM object is released by the GC eventually?[/color]

            Yes

            [color=blue]
            >It must happen some time after my VC++ client closes.[/color]

            It will happen the next time GC runs, or at process shutdown if the
            executable calls CorExitProcess.

            [color=blue]
            >Is there any way of forcing
            >the GC to delete my object immediately from my client?[/color]

            Yes, with the GC class, but that doesn't mean it's a good idea to do
            so.


            And implementing IDisposable as you suggested in the other thread will
            not affect GC, only let have deterministic cleanup of unmanaged
            resources.



            Mattias

            --
            Mattias Sjögren [MVP] mattias @ mvps.org
            http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
            Please reply only to the newsgroup.

            Comment

            • techie

              #7
              Re: C# COM object not being released

              My VC++ 6 client doesn't make any GC.Collect() call. Its legacy code and I
              don't want to change it.

              I put a breakpoint in the destructor of my C# COM object class. I then ran
              the C# project with the Start Application set to my VC++ client. At no
              point did the program ever break into my breakpoint which implies the object
              never got deleted. Why is that? Doesn't the GC run every now and then?
              Does it only run when the memory usage of the machine reaches a high level?

              In this particular solution, my C# COM object will be created via a C++ COM
              object. A C# web service will create the C++ COM object. After I'm
              finished with the COM object I can call GC.Collect() which will force
              garbage collection. I'm just concerned my web server will run out of
              memory. This is an important project and there are going to be a large
              number of users.


              Comment

              • Willy Denoyette [MVP]

                #8
                Re: C# COM object not being released

                Object "Finalizers " (also called destructor) are executed by/on the
                finalizer thread, at some point in time, after the GC has run for the second
                time after the object became eligible for collecting.
                Note however that when your object has been instantiated in a STA thread,
                this thread needs to pump the window message queue, failing to do so will
                block the finalizer thread.
                So first thing you should do is; check the apartment you are running in, if
                it's a STA change it to MTA, and make sure your object is thread safe.

                Willy.



                "techie" <techie@nospam. biz> wrote in message
                news:u7udnXnrV9 XwFijfRVn-rA@biscit.net.. .[color=blue]
                > My VC++ 6 client doesn't make any GC.Collect() call. Its legacy code and
                > I
                > don't want to change it.
                >
                > I put a breakpoint in the destructor of my C# COM object class. I then
                > ran
                > the C# project with the Start Application set to my VC++ client. At no
                > point did the program ever break into my breakpoint which implies the
                > object
                > never got deleted. Why is that? Doesn't the GC run every now and then?
                > Does it only run when the memory usage of the machine reaches a high
                > level?
                >
                > In this particular solution, my C# COM object will be created via a C++
                > COM
                > object. A C# web service will create the C++ COM object. After I'm
                > finished with the COM object I can call GC.Collect() which will force
                > garbage collection. I'm just concerned my web server will run out of
                > memory. This is an important project and there are going to be a large
                > number of users.
                >
                >[/color]


                Comment

                • techie

                  #9
                  Re: C# COM object not being released

                  My VC++ 6 client is a normal .exe application. No MTA here. Could this be
                  reason for my memory leak?

                  However, my C++ COM object, which can be created from a ASP.NET web page, is
                  MTA. This COM object in turn will create my new C# object. I will test if
                  the destructor is called in this case.

                  "Willy Denoyette [MVP]" <willy.denoyett e@telenet.be> wrote in message
                  news:uOPqoQQdFH A.2556@TK2MSFTN GP10.phx.gbl...[color=blue]
                  > Object "Finalizers " (also called destructor) are executed by/on the
                  > finalizer thread, at some point in time, after the GC has run for the[/color]
                  second[color=blue]
                  > time after the object became eligible for collecting.
                  > Note however that when your object has been instantiated in a STA thread,
                  > this thread needs to pump the window message queue, failing to do so will
                  > block the finalizer thread.
                  > So first thing you should do is; check the apartment you are running in,[/color]
                  if[color=blue]
                  > it's a STA change it to MTA, and make sure your object is thread safe.
                  >
                  > Willy.[/color]


                  Comment

                  • Willy Denoyette [MVP]

                    #10
                    Re: C# COM object not being released

                    Even 'normal' applications must initialize the thread to enter an apartment
                    before they can create COM instances and use their interfcaces. Question
                    is - what do you mean with a normal exe? Does it initialize the thread by
                    calling CoInitialize(Ex ) to enter an STA or an MTA? And more importantly,
                    does it pump messages (all windows programs pump the message queue as part
                    of their UI message loop, console style applications don't pump however).

                    I'm not sure what you mean with your C++ object is MTA, COM objects can have
                    a 'threadingmodel ' apartment, both and free, if no threadingmode is
                    specified the object lives in an OLE supplied host apartment.
                    'apartment' requires a STA to live in, 'both' can live in a STA as well as a
                    MTA while 'free' requires a MTA.
                    MTA initialized threads don't need to pump messages.

                    If your C++ COM is marked 'apartment' in the registry it must run in a
                    asp.net application with <%@ page aspcompat=true %> attribute set.

                    Willy.




                    "techie" <techie@nospam. biz> wrote in message
                    news:qKKdnXBhx-vcTyjfRVn-2w@biscit.net.. .[color=blue]
                    > My VC++ 6 client is a normal .exe application. No MTA here. Could this
                    > be
                    > reason for my memory leak?
                    >
                    > However, my C++ COM object, which can be created from a ASP.NET web page,
                    > is
                    > MTA. This COM object in turn will create my new C# object. I will test
                    > if
                    > the destructor is called in this case.
                    >
                    > "Willy Denoyette [MVP]" <willy.denoyett e@telenet.be> wrote in message
                    > news:uOPqoQQdFH A.2556@TK2MSFTN GP10.phx.gbl...[color=green]
                    >> Object "Finalizers " (also called destructor) are executed by/on the
                    >> finalizer thread, at some point in time, after the GC has run for the[/color]
                    > second[color=green]
                    >> time after the object became eligible for collecting.
                    >> Note however that when your object has been instantiated in a STA thread,
                    >> this thread needs to pump the window message queue, failing to do so will
                    >> block the finalizer thread.
                    >> So first thing you should do is; check the apartment you are running in,[/color]
                    > if[color=green]
                    >> it's a STA change it to MTA, and make sure your object is thread safe.
                    >>
                    >> Willy.[/color]
                    >
                    >[/color]


                    Comment

                    • Steve Walker

                      #11
                      Re: C# COM object not being released

                      In message <GZ6dnTbkdtUTbS nfRVn-2A@biscit.net>, techie
                      <techie@nospam. biz> writes[color=blue]
                      >"Steve Walker" <steve@otolith. demon.co.uk> wrote in message
                      >news:pIclE0SeW LtCFwvW@otolith .demon.co.uk...[color=green]
                      >> You probably don't have a memory leak, you more than likely have an
                      >> unreferenced object sat waiting to be GC'd.[/color]
                      >
                      >Is there any I can force GC?[/color]

                      You can, but you probably shouldn't. The algorithm for garbage
                      collection itself is well documented. I can't find similar documentation
                      for the *scheduling* of garbage collection, but I get the impression
                      from its behaviour that it works on the principle that when there is
                      plenty of memory still free, CPU cycles should not be wasted on
                      recovering what has been used.

                      --
                      Steve Walker

                      Comment

                      • techie

                        #12
                        Re: C# COM object not being released

                        I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
                        COINIT_MULTITHR EADED) but it made no difference.

                        Coming from a C++ background I am used to all allocated memory being
                        released when an application closes down. If my main application is in C# I
                        can call Dispose() on the object but I can't in this case.


                        Comment

                        • Willy Denoyette [MVP]

                          #13
                          Re: C# COM object not being released

                          It's getting confusing, it would suggest you explain in detail your exact
                          scenario.
                          I see you called AfxOleInit() initially, that means the application is a MFC
                          application right? But what kind of application is it, a windows application
                          or something else, or simply put - does it have a UI?
                          If it has a UI you MUST not change the threads apartment to MTA, you must
                          keep the call to AfxOleInit.

                          Second, what kind of COM object are you creating from this C++ application;
                          1- a native C++ COM object, or
                          3- a native COM object creating a .NET COM object, or
                          2- a .NET COM object .

                          If it's 1 or 2, and the native COM object's 'threadingmodel ' is set to
                          'Apartment', the object will be created on a STA thread no matter how you
                          initialize the (main) thread. And that thread must pump the message queue,
                          that's why I asked if it's a windows type client (having a UI). In case 2,
                          both objects will be created in the same apartment - so it's the native COM
                          objects who determines the apartment type.
                          In case 3, the 'COM' object will live in the creators apartment irrespective
                          it's type, but I can't stress it enough, - STA threads must pump messages,
                          and UI threads do have a message pump, non UI threads don't!

                          Note that in .NET the memory will also be released when the application
                          closes down.
                          I'm also not clear why you have a destructor defined for your "COM" class,
                          what is it used for?
                          A general rule is to avoid finalizers, unless your object is disposable
                          (implements IDisposable) and the destructor is only used as a safety net.

                          Willy.

                          "techie" <techie@nospam. biz> wrote in message
                          news:arSdndUtVb TgiSrfRVn-tA@biscit.net.. .[color=blue]
                          >I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
                          > COINIT_MULTITHR EADED) but it made no difference.
                          >
                          > Coming from a C++ background I am used to all allocated memory being
                          > released when an application closes down. If my main application is in C#
                          > I
                          > can call Dispose() on the object but I can't in this case.
                          >
                          >[/color]


                          Comment

                          • techie

                            #14
                            Re: C# COM object not being released

                            The main application is an MFC Windows application with a UI. The main MFC
                            application creates a COM object. The COM object is a C# .NET COM object.
                            Therefore, my COM object is a STA thread? How do I get my COM object to
                            pump the message queue?


                            "Willy Denoyette [MVP]" <willy.denoyett e@telenet.be> wrote in message
                            news:%23hG78Udd FHA.796@TK2MSFT NGP09.phx.gbl.. .[color=blue]
                            > It's getting confusing, it would suggest you explain in detail your exact
                            > scenario.
                            > I see you called AfxOleInit() initially, that means the application is a[/color]
                            MFC[color=blue]
                            > application right? But what kind of application is it, a windows[/color]
                            application[color=blue]
                            > or something else, or simply put - does it have a UI?
                            > If it has a UI you MUST not change the threads apartment to MTA, you must
                            > keep the call to AfxOleInit.
                            >
                            > Second, what kind of COM object are you creating from this C++[/color]
                            application;[color=blue]
                            > 1- a native C++ COM object, or
                            > 3- a native COM object creating a .NET COM object, or
                            > 2- a .NET COM object .
                            >
                            > If it's 1 or 2, and the native COM object's 'threadingmodel ' is set to
                            > 'Apartment', the object will be created on a STA thread no matter how you
                            > initialize the (main) thread. And that thread must pump the message queue,
                            > that's why I asked if it's a windows type client (having a UI). In case 2,
                            > both objects will be created in the same apartment - so it's the native[/color]
                            COM[color=blue]
                            > objects who determines the apartment type.
                            > In case 3, the 'COM' object will live in the creators apartment[/color]
                            irrespective[color=blue]
                            > it's type, but I can't stress it enough, - STA threads must pump messages,
                            > and UI threads do have a message pump, non UI threads don't!
                            >
                            > Note that in .NET the memory will also be released when the application
                            > closes down.
                            > I'm also not clear why you have a destructor defined for your "COM" class,
                            > what is it used for?
                            > A general rule is to avoid finalizers, unless your object is disposable
                            > (implements IDisposable) and the destructor is only used as a safety net.
                            >
                            > Willy.
                            >
                            > "techie" <techie@nospam. biz> wrote in message
                            > news:arSdndUtVb TgiSrfRVn-tA@biscit.net.. .[color=green]
                            > >I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
                            > > COINIT_MULTITHR EADED) but it made no difference.
                            > >
                            > > Coming from a C++ background I am used to all allocated memory being
                            > > released when an application closes down. If my main application is in[/color][/color]
                            C#[color=blue][color=green]
                            > > I
                            > > can call Dispose() on the object but I can't in this case.
                            > >
                            > >[/color]
                            >
                            >[/color]


                            Comment

                            • J.Marsch

                              #15
                              Re: C# COM object not being released

                              It sounds to me as if a quick primer on how the garbage collector works
                              might be useful to you. Here's a pretty good article:
                              Find official documentation, practical know-how, and expert guidance for builders working and troubleshooting in Microsoft products.


                              In general:
                              Avoid calling GC.Collect() in production code -- trying to manually control
                              the GC will usually do more harm than good. (GC.Collect() can be useful in
                              testing scenarios, though).

                              Avoid using a Finalizer (destructor in C#) -- only use them if you need to
                              release some kind of unmanaged resource, like a handle.

                              IDisposable probably isn't what you think it is. You use it to provide a
                              deterministic way to call the finalizer (but you probably don't want to use
                              a finalizer).

                              Here's some additional reading:


                              A whole catalog of links:



                              "techie" <techie@nospam. biz> wrote in message
                              news:GZ6dnTbkdt UTbSnfRVn-2A@biscit.net.. .[color=blue]
                              > "Steve Walker" <steve@otolith. demon.co.uk> wrote in message
                              > news:pIclE0SeWL tCFwvW@otolith. demon.co.uk...[color=green]
                              >> You probably don't have a memory leak, you more than likely have an
                              >> unreferenced object sat waiting to be GC'd.[/color]
                              >
                              > Is there any I can force GC? Maybe if my class also implements
                              > IDisposable
                              > I can call dispose from my client?
                              >
                              >[/color]


                              Comment

                              Working...