Thread: Losing reference between dlls

  1. #1
    In the Land of Diddly-Doo g4j31a5's Avatar
    Join Date
    Jul 2006
    Posts
    476

    Losing reference between dlls

    Hi guys. I've got a weird situation that I don't know how to handle. You see, I'm using a few dinamically linked libraries and I don't know why but the memory reference for a variable in another dll was lost. And it caused an unhandled exception. To illustrate my problem, I'll try to write a short version of it.

    Consider I have this class in my main program (MyApp.exe):
    Code:
    MyDllClass1 gMyDll1;
    
    class MyApp
    {
    
    
       public:
    ...
          void create();
    
    }
    
    //Method definition
    void MyApp::create()
    {
    ...
          gMyDll1 = new MyDll();
          gMyDll1->create(); //exception here. it calls the methods that is defined inside a dll called MyDll.dll
    ...
    }
    When I debugged it and traced it inside the MyApp::create(), I found that mMyDll1 was initialized correctly. But when I traced into MyDll::create(), I found out that the this pointer of gMyDll1 object was null (thus the exception). How can this possible? I'm quite sure that the mMyDll1 wasn't deleted.

    Can anybody help me here? Thanks a lot in advance.
    Last edited by g4j31a5; 05-17-2010 at 07:47 AM.
    ERROR: Brain not found. Please insert a new brain!

    “Do nothing which is of no use.” - Miyamoto Musashi.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You see, I'm using a few dinamically linked libraries and I don't know why but the memory reference for a variable in another dll was lost.
    The pointer to an object in another DLL should be valid. The only time you will run into issues is with static variables. You can also run into problems by attempting to export instances of templates and when using dynamic memory. In short if you allocate memory in a DLL that same DLL should de-allocate the memory. Each DLL has it's own CRT so if you allocate in one and destroy in another, the CRT of the DLL that did not allocate has no record or knowledge of the allocation in it's CRT. This will corrupt the heap in the DLL where you are performing the cleanup of dynamic memory.

    This particular problem sounds like it may be related to static instances in another DLL.
    Last edited by VirtualAce; 05-17-2010 at 07:58 PM.

  3. #3
    In the Land of Diddly-Doo g4j31a5's Avatar
    Join Date
    Jul 2006
    Posts
    476
    Quote Originally Posted by Bubba View Post
    The pointer to an object in another DLL should be valid. The only time you will run into issues is with static variables. You can also run into problems by attempting to export instances of templates and when using dynamic memory. In short if you allocate memory in a DLL that same DLL should de-allocate the memory. Each DLL has it's own CRT so if you allocate in one and destroy in another, the CRT of the DLL that did not allocate has no record or knowledge of the allocation in it's CRT. This will corrupt the heap in the DLL where you are performing the cleanup of dynamic memory.

    This particular problem sounds like it may be related to static instances in another DLL.
    OIC. BTW, the first DLL was actually calling another DLL and use a singleton in the second DLL to instantiate a member of its object. And we all know that a Singleton is actually a glorified static.

    So it's like this:

    MyApp.exe:

    Code:
    MyDllClass1 gMyDll1;
    
    class MyApp
    {
    
    
       public:
    ...
          void create();
    
    }
    
    //Method definition
    void MyApp::create()
    {
    ...
          gMyDll1 = new MyDllClass1();
          gMyDll1->create(); //exception here. it calls the methods that is defined inside a dll called MyDll.dll
    ...
    }
    MyDLL1.dll:

    Code:
    class DllExport MyDllClass1 
    {
    
       private:
          ObjectManager *mObjectManager;
    
       public:
    ...
          MyDllClass1 (MyDllClass1 *dllClass);
          void create();
    
    }
    
    //Method definition
    void MyDllClass1 ::MyDllClass1 ():
          mObjectManager(new ObjectManager (this))
    {
    
    }
    
    void MyDllClass1 ::create()
    {
          if (mCreated)
          {
                return;
          }
    
          mObjectManager->create(); //exception here.
    ...
    }
    
    ////
    // ObjectManager class
    class DllExport ObjectManager
    {
    
       private:
          Object *mObject;
    
       public:
    ...
          ObjectManager();
          void create();
    
    }
    
    //Method definition
    void ObjectManager::ObjectManager():
          mObject(null)
    {
    
    }
    
    void MyDllClass1 ::create()
    {
          if (mCreated)
          {
                return;
          }
    
          mObject = MyDll2NameSpace::ObjectManager::getSingleton().createManual("Object_1"); //exception here.
    ...
    }
    MyDLL2.dll:


    Code:
    class DllExport ObjectManager:public ResourceManager, public Singleton<ObjectManager>
    {
    
       private:
          ObjectManager *mObjectManager;
    
       public:
    ...
          void createManual(const String& name);
          void createImpl(const String& name);
    
    }
    
    //Method definition
    ObjectPtr ObjectManager::createManual( const String& name)
    {
         return create(name, true);
    }
    
    ObjectPtr ObjectManager::createImpl( const String& name)
    {
         return new Object(name); //exception here
    }
    
    
    //ResourceManager
    class DllExport ResourceManager
    {
    
       private:
          ObjectManager *mObjectManager;
    
       public:
    ...
          void create(const String& name, bool manual = false);
    
    }
    
    //Method definition
    ObjectPtr ObjectManager::create( const String& name, bool manual)
    {
    	// Call creation implementation
    	ResourcePtr ret = ResourcePtr(
                createImpl(name)); //exception here
    
            if (manual)
            {
               ...
             }
        
             return ret;
    }
    So is there some way to solve this? I see that the second library uses a templated singleton. I don't want to change the library because it will be hectic because I should change at least 75% of the code.
    Last edited by g4j31a5; 05-18-2010 at 09:25 PM.
    ERROR: Brain not found. Please insert a new brain!

    “Do nothing which is of no use.” - Miyamoto Musashi.

  4. #4
    Registered User
    Join Date
    May 2010
    Posts
    27
    Where is MyDll?

    If it is your dll, then you must load the dll, if you have not loaded it statically, you'll need to use LoadLibrary() to load it dynamically, and then use GetProcAddress() to get the address of the method you want to use.

    Now anyone would better answer if you give complete details. Statically link dll or dynamically link dll. Further more, what is MyDll()???

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    OIC. BTW, the first DLL was actually calling another DLL and use a singleton in the second DLL to instantiate a member of its object.
    This sounds suspect.

  6. #6
    In the Land of Diddly-Doo g4j31a5's Avatar
    Join Date
    Jul 2006
    Posts
    476
    Quote Originally Posted by AdnanShaheen View Post
    Where is MyDll?

    If it is your dll, then you must load the dll, if you have not loaded it statically, you'll need to use LoadLibrary() to load it dynamically, and then use GetProcAddress() to get the address of the method you want to use.

    Now anyone would better answer if you give complete details. Statically link dll or dynamically link dll. Further more, what is MyDll()???
    My bad. If you see my declaration, it was actually a typo. It was not MyDll but MyDllClass1.

    Umm I don't quite understand what you mean. You do realize that dll is a short term for Dynamic-link library, right? How can it be statically linked? Furthermore, won't a static- link compile the whole main program inside the executable, not in a different dll? Also if I haven't included the dll, there will be lots of link errors LNK2019, right?

    Quote Originally Posted by Bubba View Post
    This sounds suspect.
    Yeah, I suspect so. However it was a part of the dll. So how do I solve this problem? If you see my code, you'll see that the "MyDll2NameSpace::ObjectManager" in the second dll is a child of "ResourceManager" and "Singleton<ObjectManager>", right? And it is used for initializing lots of objects and manage them. It has a method to create objects and returned the created object's pointer (ObjectPtr or Object *). While in the first dll, the "MyDllClass1" is trying to create an object using this method and store the return value inside using another "ObjectManager" inside a different namespace (MyDll2NameSpace). Sorry for the confusing names. I was confused at first until I realized that although the name are the same (ObjectManager), but they are on different namespaces and dlls.
    Last edited by g4j31a5; 05-18-2010 at 09:57 PM.
    ERROR: Brain not found. Please insert a new brain!

    “Do nothing which is of no use.” - Miyamoto Musashi.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    class DllExport ObjectManager:public ResourceManager, public Singleton<ObjectManager>
    I suspect this is the problem right here. Look at what that statement is doing. It is attempting to export a templated class or at least a class that derives from a templated class. This is just not going to work across DLLs.

    The solution is to re-implement the singleton pattern in the DLL. Yes it is nice to have a singleton template that does all of the work for you, however, in the context of a DLL it will not work if you intend on exporting the class. When you export a class you export all members of that class. You might be able to get away with exporting specific functions in the class instead of the entire class. I have used that many times to get around the issue. There is another issue as well. The parameters to functions in DLL interfaces also cannot use templates. So no std::string or any other templates. Let's look at the problem:

    Code:
    class DLLInterface
    {
        public:
           virtual ~DLLInterface() { }
           virtual void Foo(const std::string &someString) = 0;
    };
    Since templates are really just a fancy way to do inline expansion where exactly does the inline expansion take place for std::string? Does it happen on the DLL side or does it happen on the client side of the DLL? Also let's say the DLL is using a different version of the STL than the client or user of the DLL. You would be passing in a reference to a string from one version of the STL and expecting a future version or possibly older version to read that string. This would force the implementors of the STL libraries for a compiler to maintain backwards compat. for all compiler versions which is just not possible. In essence the above code will crash at some point if you are using different versions of the STL.

    Now look at your code. You are deriving from a template class. Where is the expansion taking place? When you attempt to use this instance from inside another DLL the template will also be expanded in that code. Now you have two expansions and the question becomes which one is used? Microsoft has pretty much figured this out and this type of code works fine if you are using the same exact version of their compiler for the DLL and the client of the DLL. It does not work fine in any other instance.

    Now you also are talking about statics here. The static instance you create in one DLL does not exist in another DLL or clients of the DLL b/c DLLs have their own heap and CRTs. Now you can return a pointer to the instance but only from the DLL that created the instance. You are just returning a pointer to a memory address on the heap in the DLL. You can also do this with any dynamic memory with one catch. Since the client of the DLL and other DLLs do not have any record of the allocation in another DLL if you attempt to clean up memory in a DLL that did not allocate the memory (and thus its CRT has no record of it) you will corrupt the heap of the DLL attempting to do the cleanup.

    Be very careful with your use of statics, dynamic memory, and templates when it comes to DLLs. General rule is you cannot export any template instances or any templated classes across the boundary. Another general rule to follow is that if you are going to return pointers to objects that are on the heap in a DLL you should code the object in such a way that cleanup of the object only happens in the DLL that allocated the memory for it. If you do not then any user can attempt to clean up this memory thinking they are just being a good C++ programmer but will actually de-stabilize the system to a point that will ultimately end in a crash.

    I suspect removing the templated class from the inheritance chain and implementing the singleton pattern in each DLL that needs to be a singleton will work just fine. Note that singletons also do not work when it comes to interfaces. You cannot declare a pure virtual static function and since interfaces do not have data or impl you also will not have a static instance pointer. Also templates do not work well with interfaces since you cannot have a pure virtual template function. In essence there is no way to implement the singleton pattern or force the singleton pattern in an interface. However the impl of the interface can implement the pattern. This is similar to the PIMPL idiom which also might work for you. Essentially what PIMPL does is hide all the impl by using a wrapper class that holds a pointer to the actual impl. This allows you to handle templates and other nasties that don't work with DLLs in the impl where they do work but the external interface does not expose these.

    Say you had a class that used a vector but did not want to expose the vector. You could either only expose the functions in the class instead of the entire class or you could write a pimpl that interfaced with the impl that used the vector:

    VectorImpl
    Code:
    class VectorImpl
    {
       public:
          void AddObject(Object *pObject) { m_objects.push_back(pObject); }
    
      private:
         std::vector<Object *> m_objects;
    };
    DLLInterface
    Code:
    class DLLEXPORT DLLInterface
    {
        public:
          DLLInterface() { m_pVectorImpl = new VectorImpl(); }
          void AddObject(Object *pObject) { m_pVectorImpl->AddObject(pObject); }
    
       private:
         VectorImpl *m_pVectorImpl;
    };
    Of course you could not have any impl in the DLL headers like I have shown but you get the idea. Also note that pointers are being passed in which implies the memory they point to was allocated outside of the DLL. This means that VectorImpl cannot cleanup the memory for these pointers b/c VectorImpl is in a DLL and that DLL's CRT has no record of the allocations that occurred outside of the DLL.

    DLLInterface would most likely derive from a pure virtual interface and supposedly then some object would return that interface to the clients of the DLL. It does not have to do this and the example I provided should work but in more complex systems interfaces are definitely the way to go. They decouple interface from impl, allow for new impls without changing client code, and if used dynamically can be used as patches to existing code. If you find a bug in the DLL you just send a new DLL and as long as the external interface does not change...no changes need to be made to client code. Very nice indeed.

    Keep working on it and keep posting your issues. Together I'm sure we can arrive at a satisfactory solution. I've been through DLL hell quite a lot and I will gladly help someone divert prior to entering that hell. I do apologize if my response is full of things you already know but b/c I don't know who you are or what your experience is I have to assume that my suggestions might help you in some way. If you already knew about them then just ignore my tutorial.
    Last edited by VirtualAce; 05-18-2010 at 10:34 PM.

  8. #8
    In the Land of Diddly-Doo g4j31a5's Avatar
    Join Date
    Jul 2006
    Posts
    476
    Thanks Bubba. Wow. That's a lot to digest. I'll try to read and understand it first before implementing it. Thanks.

    Quote Originally Posted by Bubba View Post
    I suspect this is the problem right here. Look at what that statement is doing. It is attempting to export a templated class or at least a class that derives from a templated class. This is just not going to work across DLLs.

    The solution is to re-implement the singleton pattern in the DLL. Yes it is nice to have a singleton template that does all of the work for you, however, in the context of a DLL it will not work if you intend on exporting the class. When you export a class you export all members of that class. You might be able to get away with exporting specific functions in the class instead of the entire class. I have used that many times to get around the issue. There is another issue as well. The parameters to functions in DLL interfaces also cannot use templates. So no std::string or any other templates. Let's look at the problem:

    Code:
    class DLLInterface
    {
        public:
           virtual ~DLLInterface() { }
           virtual void Foo(const std::string &someString) = 0;
    };
    Since templates are really just a fancy way to do inline expansion where exactly does the inline expansion take place for std::string? Does it happen on the DLL side or does it happen on the client side of the DLL? Also let's say the DLL is using a different version of the STL than the client or user of the DLL. You would be passing in a reference to a string from one version of the STL and expecting a future version or possibly older version to read that string. This would force the implementors of the STL libraries for a compiler to maintain backwards compat. for all compiler versions which is just not possible. In essence the above code will crash at some point if you are using different versions of the STL.

    Now look at your code. You are deriving from a template class. Where is the expansion taking place? When you attempt to use this instance from inside another DLL the template will also be expanded in that code. Now you have two expansions and the question becomes which one is used? Microsoft has pretty much figured this out and this type of code works fine if you are using the same exact version of their compiler for the DLL and the client of the DLL. It does not work fine in any other instance.

    Now you also are talking about statics here. The static instance you create in one DLL does not exist in another DLL or clients of the DLL b/c DLLs have their own heap and CRTs. Now you can return a pointer to the instance but only from the DLL that created the instance. You are just returning a pointer to a memory address on the heap in the DLL. You can also do this with any dynamic memory with one catch. Since the client of the DLL and other DLLs do not have any record of the allocation in another DLL if you attempt to clean up memory in a DLL that did not allocate the memory (and thus its CRT has no record of it) you will corrupt the heap of the DLL attempting to do the cleanup.

    Be very careful with your use of statics, dynamic memory, and templates when it comes to DLLs. General rule is you cannot export any template instances or any templated classes across the boundary. Another general rule to follow is that if you are going to return pointers to objects that are on the heap in a DLL you should code the object in such a way that cleanup of the object only happens in the DLL that allocated the memory for it. If you do not then any user can attempt to clean up this memory thinking they are just being a good C++ programmer but will actually de-stabilize the system to a point that will ultimately end in a crash.

    I suspect removing the templated class from the inheritance chain and implementing the singleton pattern in each DLL that needs to be a singleton will work just fine. Note that singletons also do not work when it comes to interfaces. You cannot declare a pure virtual static function and since interfaces do not have data or impl you also will not have a static instance pointer. Also templates do not work well with interfaces since you cannot have a pure virtual template function. In essence there is no way to implement the singleton pattern or force the singleton pattern in an interface. However the impl of the interface can implement the pattern. This is similar to the PIMPL idiom which also might work for you. Essentially what PIMPL does is hide all the impl by using a wrapper class that holds a pointer to the actual impl. This allows you to handle templates and other nasties that don't work with DLLs in the impl where they do work but the external interface does not expose these.

    Say you had a class that used a vector but did not want to expose the vector. You could either only expose the functions in the class instead of the entire class or you could write a pimpl that interfaced with the impl that used the vector:

    VectorImpl
    Code:
    class VectorImpl
    {
       public:
          void AddObject(Object *pObject) { m_objects.push_back(pObject); }
    
      private:
         std::vector<Object *> m_objects;
    };
    DLLInterface
    Code:
    class DLLEXPORT DLLInterface
    {
        public:
          DLLInterface() { m_pVectorImpl = new VectorImpl(); }
          void AddObject(Object *pObject) { m_pVectorImpl->AddObject(pObject); }
    
       private:
         VectorImpl *m_pVectorImpl;
    };
    Of course you could not have any impl in the DLL headers like I have shown but you get the idea. Also note that pointers are being passed in which implies the memory they point to was allocated outside of the DLL. This means that VectorImpl cannot cleanup the memory for these pointers b/c VectorImpl is in a DLL and that DLL's CRT has no record of the allocations that occurred outside of the DLL.

    DLLInterface would most likely derive from a pure virtual interface and supposedly then some object would return that interface to the clients of the DLL. It does not have to do this and the example I provided should work but in more complex systems interfaces are definitely the way to go. They decouple interface from impl, allow for new impls without changing client code, and if used dynamically can be used as patches to existing code. If you find a bug in the DLL you just send a new DLL and as long as the external interface does not change...no changes need to be made to client code. Very nice indeed.

    Keep working on it and keep posting your issues. Together I'm sure we can arrive at a satisfactory solution. I've been through DLL hell quite a lot and I will gladly help someone divert prior to entering that hell. I do apologize if my response is full of things you already know but b/c I don't know who you are or what your experience is I have to assume that my suggestions might help you in some way. If you already knew about them then just ignore my tutorial.
    ERROR: Brain not found. Please insert a new brain!

    “Do nothing which is of no use.” - Miyamoto Musashi.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. Undefined Reference Compiling Error
    By AlakaAlaki in forum C++ Programming
    Replies: 1
    Last Post: 06-27-2008, 11:45 AM
  3. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  4. C OpenGL Compiler Error?
    By Matt3000 in forum C Programming
    Replies: 12
    Last Post: 07-07-2006, 04:42 PM
  5. c++ linking problem for x11
    By kron in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2004, 10:18 AM