CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Memory leaks when mixing managed & native code

    In my current assignment there is an application where they have mixed managed (I have never worked with managed C++ before) and native C++ code. I tried to introduce the usage of smart pointers and as soon as I added a static one... BAM! The code didn't even make it to main before crashing. After some surfing I found out that the reason for that is that in managed C++ the crt startup code is bypassed.

    The solution given on the net was to add __DllMainCRTStartup@12 in linker Force Symbol References. Even though it feels a bit strange to call Dll... it seems to do the trick of getting all statics properly initialized. What I've failed to find though is how to force a call to the cleanup on exit. Has anybody here some insight?

    If anyone is curious I've attached a minimalistic 2010 Express project that demonstrate the issue. Comment/uncomment the define in NativeLib.cpp, run and then just Close the form to see the leak report (or not depending on the define).

    Edit: I forgot to mention that the project settings are copied from the full one so it might be something that look peculiar for the posted one.
    Attached Files Attached Files
    Last edited by S_M_A; February 11th, 2013 at 04:41 PM.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  2. #2
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: Memory leaks when mixing managed & native code

    The crt exit code is _DllMainCRTExit

  3. #3
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Memory leaks when mixing managed & native code

    Thanks, that's a good guess but it's not present in any lib or crt source (searced both "Program Files" and "Program Files (x86)". Also, if it was present I doubt that forcing a call to it would do any good. I've never used the Force Symbol References but obviously it makes the routine to get called in the start-up so if DllMainCRTExit existed it has to be added somewhere else.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  4. #4
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Memory leaks when mixing managed & native code

    First thing I did was creating a native client for your lib for reference and setting a breakpoint to see from where the shared_ptr<T> destructor gets called upon app shutdown. The direct caller to it was this:

    Code:
    >	NativeClient.exe!`dynamic atexit destructor for 'Any::var''()  + 0x28 Bytes	C++
    That gave me the idea to try registering a function to reset the smart pointer using atexit() or _onexit(). None of these worked, however; registration seemingly succeded, but the registered function never got called. _onexit_m() worked, but it's undefined in the native lib code and a managed function taking a managed function pointer argument in the Windows Forms app. So the whole thing becomes somewhat convoluted:

    Code:
    // NativeLib.cpp
    
    // ...
    
    /* static */ void Any::ResetVar()
    {
      var.reset();
    }
    In a Form1 implementation file I added to the project:

    Code:
    // Form1.cpp
    
    #include "stdafx.h"
    
    #include "Form1.h"
    
    using namespace FormsApp;
    
    /* static */ int Form1::ResetVar()
    {
      Any::ResetVar();
      return 0;
    }
    And finally in the Form1 c'tor:

    Code:
    // Form1.h
    
    // ...
    
            Form1(void)
            {
                InitializeComponent();
                any = new Any();
                _onexit_m(ResetVar);
            }
    I'm not really happy with this solution since it's terrible complicated for such a simple thing, but maybe we can at least use it as a starting point.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  5. #5
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Memory leaks when mixing managed & native code

    Thanks! I agree however that the solution isn't exactly a dream. It should be possible to use in my case but it also defeats the purpose of using a smart pointer.

    There are most likely a lot of legacy C++ libs that doesn't behave as expected in combination with managed C++. Or at least flood the output window with leak info on exit effectively hiding an newly introduced one. I can't believe that MS actually did forget about this.

    The search goes on...
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  6. #6
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Memory leaks when mixing managed & native code

    Your ideas woke me up enough to try to debug things instead of just searching the net.

    If a breakpoint is set at DllMainCRTStartup it turns out that it's actually called both on start-up and on exit, with DLL_PROCESS_ATTACH as well as DLL_PROCESS_DETACH. Since the source states
    * On DLL_PROCESS_ATTACH, the C++ constructors for the DLL will be called.
    *
    * On DLL_PROCESS_DETACH, the C++ destructors and _onexit/atexit routines
    * will be called.
    I found it a bit strange that MS really had missed the issue. So, either this really is a bug, the leak detection shows false leaks or forcing a call to DllMainCRTStartup for a lib is dead wrong.

    Now I felt just had to try finding a non-dll startup routine so I tried _mainCRTStartup instead and voilá! the destructor was now called on exit!

    As far as I can tell this is the solution but if I experience some other side-effect I will update this thread.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  7. #7
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Memory leaks when mixing managed & native code

    Nice to see you got it working! Yet I did some more experiments regarding this subject (even after reding post #6).

    My goal was to conceal all the nasty details from my last proposal from the client code and also minimize the native library code changes required. For this purpose I wrote a managed wrapper around your native library, which is quite common practice in such a scenario. The wrapper is a managed (or rather mixed-mode) DLL project which staticaly links in your native library code. More precisely, the wrapper does not link against your .lib file; instead it incorporates the very same (no copies to simplify maintainance) source files of your original library in its project. Preprocessor conditionals take care of the few differences between pure native an mixed-mode compilation. The main thing they do by now is just putting the native library code into its own namespace instead of the global one in the managed wrapper. It's simplest to define BEHIND_MANAGED_WRAPPER project-wide in the ManagedWrapper project settings.

    While doing stepwise refinement of the project, I realized to my surprise that registering an onexit function to clean up the smart pointer isn't even necessary anymore. I'm not entirely sure about the mechanisms responsible for that, but I think it simply is the fact that the native library code now is compiled and linked as part of a mixed-mode project. Not even the forced symbol reference you mentioned in the initial post is required anymore.

    Here are the relevant (parts of the) source files, and I've also attached the updated solution to the post:

    Code:
    // NativeLib.h
    
    #pragma once
    
    #include "stdafx.h"
    
    #include <memory>
    
    #ifdef BEHIND_MANAGED_WRAPPER
    #pragma managed(push, off)
    
    namespace NativeLib
    {
    #endif
    
    class Leak
    {
    };
    
    class Any
    {
      static std::shared_ptr<Leak> var;
    
    public:
        Any();
        ~Any();
    };
    
    #ifdef BEHIND_MANAGED_WRAPPER
    }  // namespace ManagedLib
    
    #pragma managed(pop)
    #endif
    Code:
    // NativeLib.cpp
    
    #include "stdafx.h"
    
    #include "NativeLib.h"
    
    #ifdef BEHIND_MANAGED_WRAPPER
    #pragma unmanaged  // Or explicitly disable CLR support for this module in project settings
    
    using namespace NativeLib;
    #endif
    
    #ifdef _DEBUG
        #define new DEBUG_CLIENTBLOCK
    #endif
    
    // #define NO_LEAK // Comment to omit automatic destruction of statics
    
    std::shared_ptr<Leak> Any::var;
    
    Any::Any()
    {
        var.reset( new Leak );
    }
    
    Any::~Any()
    {
    #ifdef NO_LEAK
        var.reset();
    #endif
    }
    Code:
    // ManagedWrapper.h
    
    #pragma once
    
    #include "../NativeLib/NativeLib/NativeLib.h"
    
    namespace ManagedWrapper
    {
    
    public ref class Any
    {
    public:
      Any() : m_pNativeAny(new NativeLib::Any)
      {}
    
      ~Any()
      {
        delete m_pNativeAny;
      }
    
    private:
      NativeLib::Any *m_pNativeAny;
    };
    
    }
    And finally:

    Code:
    // Form1.h
    
    // ...
    
        public ref class Form1 : public System::Windows::Forms::Form
        {
            ManagedWrapper::Any ^any;
    
        public:
            Form1(void)
            {
                InitializeComponent();
                any = gcnew ManagedWrapper::Any();
            }
    
            // ...
    
        };
    No more low-level fiddling necessary at all!
    Attached Files Attached Files
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  8. #8
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Memory leaks when mixing managed & native code

    That's great input. Even though I've never done anything in managed C++ before my gut feeling is that it's at least one of the proper ways of doing it if one have all the source.

    I'm not supposed to work with the application i question but I can't fully stay away from getting my nose wet so I made quick test of my solution today. It didn't fully solve the issue but that might be due to structural issues. Mildly speaking it's a bit of a mess mixing managed and native stuff here and there, a lot of globals and so on. It's also built in a way as if there were 3rd party libs involved so that also might have something to do with it.

    That unmanaged pragma looks like something I have to test. It's not used at all so it might be why start-up/exit isn't properly executed.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  9. #9
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Memory leaks when mixing managed & native code

    So you're not allowed or at least not supposed to modify the native lib's source code? All the changes I made in the native lib source code in my last proposal are basically optional: The extra pragma in the .cpp file can be replaced by a project setting and those in the .h file are to allow to seamlessly include it in source files that can be compiled to both native and managed code. Putting the native class into its own namespace is meant to prevent name conflicts. The class may well remain in the global namespace, but then it would be better to always namespace-qualify the name of the wrapper class in any code section where both classes are in scope - or simply give that class a different name. (Or maybe an anonymous namespace could be helpful here? I have never used them and am somewhat uncertain about what they actually can do.)

    The key to the solution here seems to be to just compile the native library source code as part of a mixed-mode project. (It may even be sufficient to simply link the existing native .lib file into a mixed-mode project but I didn't test that yet.)

    IMO using a managed wrapper to interface between a managed host app and native library code is almost always good practice, even if the wrapper practically consists of only pass-through functions. It conceals the native nature of the underlying code from the .NET part. Quite some times the wrappers may also contain some concrete interfacing logic. A simple but IMO rather illustrative example of that can be found in http://forums.codeguru.com/showthrea...-gt-c-CLI-gt-c.

    Quote Originally Posted by S_M_A View Post
    [...] Mildly speaking it's a bit of a mess mixing managed and native stuff here and there, a lot of globals and so on. It's also built in a way as if there were 3rd party libs involved so that also might have something to do with it.
    Yeah. That's why I (and others) keep discouraging people who carelessly mix native and managed code without a need to do so. They probably find that convenient, but that superficial convenience comes at a high price. Somehow they're lucky when they hit bizarre problems with that right from the start. Imagine such unnesessary (and perhaps poorly written) interop code finds its way into a larger piece of code that needs to be kept maintained for a non-trivial timespan: could become a real nightmare...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  10. #10
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Memory leaks when mixing managed & native code

    I'm allowed to change the code but I'm not contracted for that so I can't spend a lot of hours on it or do something that turns their world upside down. A few hours is no problem so if I find some minor changes that fixes the issue it's ok. Finding out why things go wrong will be way to many hours so that part I'll have to do at home. As said before, I've never done anything in managed C++ before so in the process I will learn something new so spending some unpaid hours on it is ok for me.

    You gave me some ideas to work with (not to mention the enthusiasm which I badly needed to get something going) so for now I plan to step by step extend the test project closer and closer to the way the original project looks like. Most likely it breaks at some point and hopefully that give me some new ideas.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  11. #11
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Memory leaks when mixing managed & native code

    Ok. Keep me updated on how it's proceeding if you like.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured