Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. Other Discussions
  3. Clever Code
  4. Ominous bug from DLL world [modified]

Ominous bug from DLL world [modified]

Scheduled Pinned Locked Moved Clever Code
c++jsonperformancehelpquestion
30 Posts 11 Posters 25 Views 1 Watching
  • 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.
  • G Graham Bradshaw

    steveb wrote:

    withing the DLL address space

    Is this terminology correct? In my definition, DLLs don't have address spaces, processes do. The DLL is loaded into the address space for the process.

    steveb wrote:

    Vtable addresses located in the DLL space because DLL is linked to static library and has its own copy of the class requested.

    This sounds more and more like C runtimes to me. Are you certain the DLL and executable are using *exactly* the same CRT? Specifically, which CRT(s) are they using?

    J Offline
    J Offline
    Joe Woodbury
    wrote on last edited by
    #20

    Graham Bradshaw wrote:

    Is this terminology correct? In my definition, DLLs don't have address spaces, processes do.

    DLLs use the address space of the loading process. However, the portion of memory which the DLL itself occupies can be called its address space. In this case, the vtable resolves to an address in the memory space occupied by the DLL, which is why the vtable is not located in the global heap, but in the private heap maintained by the DLL. When the DLL is unloaded, the vtable is nuked because it's invalid anyway. (Even if linking to a static library, you must use the DLL version of the CRT IF you intend to allocate memory in the DLL and deallocate it elsewhere. I personally think this is a terrible idea from a design standpoint. In the future, I may make the object use a fixed memory allocator or otherwise customize how it allocates, and by having the allocating module do the unallocating, it prevents problems.)

    Anyone who thinks he has a better idea of what's good for people than people do is a swine. - P.J. O'Rourke

    1 Reply Last reply
    0
    • S steveb

      Ok. C++ project that consist out of the 3 subprojects: Executable, DLL, and Static library. Dependencies as follows: Executable linked to Static library, DLL linked to static library. Static library contains set of C++ classes which are used in DLL and the EXE. DLL acts as some sort of Factory for the classes by allocating them with "new" and returning to executable. Executable uses them throughout app and deallocates them with the "delete". The DLL is loaded on the fly with LoadLibrary and then unloaded with FreeLibrary API (this is done to conserve memory). Classes in the static lib usually contain arbitrary set of virtual functions ( very usual stuff: Dtors, overrides etc). Here we go: 1. Executable loads DLL into memory with LoadLibrary. 2. Executable calls function in DLL say "GetClassA" which in turn looks like this for simplicity:

      CMyClass* GetClassA()
      {
      return new CMyClass;
      }

      3. Executable receives pointer to CMyClass. 4. Executable frees DLL with FreeLibrary. 5. Application at some point goes on to use CMyClass pointer and dies miserable death with Access violation. What has happened? Answer later.

      modified on Tuesday, July 22, 2008 11:19 PM

      S Offline
      S Offline
      steveb
      wrote on last edited by
      #21

      http://www.sbryndin.com/download/Crash.zip[^] Have fun!

      1 Reply Last reply
      0
      • G Graham Bradshaw

        The executable and DLL are using different C runtimes, which means you can't pass pointers (among other things) across the executable/DLL boundary. See http://msdn.microsoft.com/en-us/library/ms235460(VS.80).aspx[^] for a full explanation.

        T Offline
        T Offline
        Tim Smith
        wrote on last edited by
        #22

        You can pass pointer just fine, the problem is heap management.

        Tim Smith I'm going to patent thought. I have yet to see any prior art.

        1 Reply Last reply
        0
        • G Graham Bradshaw

          The executable and DLL are using different C runtimes, which means you can't pass pointers (among other things) across the executable/DLL boundary. See http://msdn.microsoft.com/en-us/library/ms235460(VS.80).aspx[^] for a full explanation.

          J Offline
          J Offline
          Jorgen Sigvardsson
          wrote on last edited by
          #23

          Graham Bradshaw wrote:

          you can't pass pointers (among other things) across the executable/DLL boundary.

          What? Sure you can. It's just that if you have different heaps between the modules, you must delete where you've newed, and free where you've malloced.

          -- Kein Mitleid Für Die Mehrheit

          G J 2 Replies Last reply
          0
          • J Jorgen Sigvardsson

            Graham Bradshaw wrote:

            you can't pass pointers (among other things) across the executable/DLL boundary.

            What? Sure you can. It's just that if you have different heaps between the modules, you must delete where you've newed, and free where you've malloced.

            -- Kein Mitleid Für Die Mehrheit

            G Offline
            G Offline
            Graham Bradshaw
            wrote on last edited by
            #24

            Jörgen Sigvardsson wrote:

            It's just that if you have different heaps between the modules, you must delete where you've newed, and free where you've malloced

            Yes, I know. And that was the point of the original post. The OP said: DLL acts as some sort of Factory for the classes by allocating them with "new" and returning to executable. Executable uses them throughout app and deallocates them with the "delete". Which is my point. The memory was allocated in the DLL and freed in the executable, which you can't do if the DLL and executable use different run-times. Which part of my response do you not agree with?

            J 1 Reply Last reply
            0
            • G Graham Bradshaw

              Jörgen Sigvardsson wrote:

              It's just that if you have different heaps between the modules, you must delete where you've newed, and free where you've malloced

              Yes, I know. And that was the point of the original post. The OP said: DLL acts as some sort of Factory for the classes by allocating them with "new" and returning to executable. Executable uses them throughout app and deallocates them with the "delete". Which is my point. The memory was allocated in the DLL and freed in the executable, which you can't do if the DLL and executable use different run-times. Which part of my response do you not agree with?

              J Offline
              J Offline
              Jorgen Sigvardsson
              wrote on last edited by
              #25

              Graham Bradshaw wrote:

              Which part of my response do you not agree with?

              This part: which means you can't pass pointers (among other things) across the executable/DLL boundary.

              G 1 Reply Last reply
              0
              • J Jorgen Sigvardsson

                Graham Bradshaw wrote:

                Which part of my response do you not agree with?

                This part: which means you can't pass pointers (among other things) across the executable/DLL boundary.

                G Offline
                G Offline
                Graham Bradshaw
                wrote on last edited by
                #26

                And there is, of course, an implied "in the case which you are discussing" in there. You allocate in the DLL, pass the pointer across, delete in the executable, you're in trouble. Technically, yes, its posssible. "You can't do it" doesn't mean it's physically impossible. It means you won't get the results you expect.

                S 1 Reply Last reply
                0
                • G Graham Bradshaw

                  And there is, of course, an implied "in the case which you are discussing" in there. You allocate in the DLL, pass the pointer across, delete in the executable, you're in trouble. Technically, yes, its posssible. "You can't do it" doesn't mean it's physically impossible. It means you won't get the results you expect.

                  S Offline
                  S Offline
                  steveb
                  wrote on last edited by
                  #27

                  If you do not use virtual functions inside the classes which are created inside the dll (which is later unloaded on the fly) then everything works fine. Please note that this bug manifetst itself only when the factory DLL that created the class is destroyed while the class still lives. If the factory DLL has the span of the executable, then everything works fine. I think that is why ATL explicitly declares all classes with ATL_NO_VTABLE (I could be wrong).

                  1 Reply Last reply
                  0
                  • S steveb

                    Ok. C++ project that consist out of the 3 subprojects: Executable, DLL, and Static library. Dependencies as follows: Executable linked to Static library, DLL linked to static library. Static library contains set of C++ classes which are used in DLL and the EXE. DLL acts as some sort of Factory for the classes by allocating them with "new" and returning to executable. Executable uses them throughout app and deallocates them with the "delete". The DLL is loaded on the fly with LoadLibrary and then unloaded with FreeLibrary API (this is done to conserve memory). Classes in the static lib usually contain arbitrary set of virtual functions ( very usual stuff: Dtors, overrides etc). Here we go: 1. Executable loads DLL into memory with LoadLibrary. 2. Executable calls function in DLL say "GetClassA" which in turn looks like this for simplicity:

                    CMyClass* GetClassA()
                    {
                    return new CMyClass;
                    }

                    3. Executable receives pointer to CMyClass. 4. Executable frees DLL with FreeLibrary. 5. Application at some point goes on to use CMyClass pointer and dies miserable death with Access violation. What has happened? Answer later.

                    modified on Tuesday, July 22, 2008 11:19 PM

                    Z Offline
                    Z Offline
                    Zeef
                    wrote on last edited by
                    #28

                    The allocation of the CMyClass is performed by the the DLL yes? Well then the class instance is allocated in the DLLs heap space. Once the DLL is unloaded then that heap space is no longer available. Another senario that falls under this problem is the following (which is similar by not the same as the static lib will use the heap space of the calling exe or dll): -Exe links against DLL A and DLL B. -Exe calls function in DLL A which in turn constructs instance of class C defined in DLL B on the heap and returns it to Exe. -DLL A is unloaded but DLL B is not The same effect will be caused in this case as the new operator (that assigns the memory) allocated it in the address space of DLL A. This can be overcome by adding a static create function to class C, which just calls "return new C()" in it's implementation. It is also possible to do this via overloading the new operator in the class but in a much more convoluted way. So going back to your issue, it basically depends how you want to fix it... - Keep DLL in existance - or turn your static lib (which I'm assuming is where classes are defined) into a DLL and add static create functions [Edit - I must admit that I didn't see the other posts concerning the vtable links due to the static library. This is probably also an issue. I'm pretty certain that the workarounds above will also work]

                    1 Reply Last reply
                    0
                    • J Jorgen Sigvardsson

                      Graham Bradshaw wrote:

                      you can't pass pointers (among other things) across the executable/DLL boundary.

                      What? Sure you can. It's just that if you have different heaps between the modules, you must delete where you've newed, and free where you've malloced.

                      -- Kein Mitleid Für Die Mehrheit

                      J Offline
                      J Offline
                      Jerry Jeremiah
                      wrote on last edited by
                      #29

                      Ok, Maybe you can tell me what is wrong with this. When I run this, it dies with memory corruption in the find statement in the Execute function. I pass the pointer to keep the map from being copied (since it might get huge in a real project) To me there is no obvious reason why. // The DLL #include #include using namespace std; typedef map<string,string> param_t; __declspec(dllexport) bool Execute(param_t *params){ params->find("xyz"); return true; } // The EXE #include #include using namespace std; typedef map<string,string> param_t; __declspec(dllimport) bool Execute(param_t *params); int main() { param_t params; params["xyz"]="abc"; params.find("xyz"); // test it here before we call the other function return Execute(¶ms); } I was using Visual C++ 6.0. Compiling it with any other compiler seems to work fine (including newer versions of Visual C++)

                      J 1 Reply Last reply
                      0
                      • J Jerry Jeremiah

                        Ok, Maybe you can tell me what is wrong with this. When I run this, it dies with memory corruption in the find statement in the Execute function. I pass the pointer to keep the map from being copied (since it might get huge in a real project) To me there is no obvious reason why. // The DLL #include #include using namespace std; typedef map<string,string> param_t; __declspec(dllexport) bool Execute(param_t *params){ params->find("xyz"); return true; } // The EXE #include #include using namespace std; typedef map<string,string> param_t; __declspec(dllimport) bool Execute(param_t *params); int main() { param_t params; params["xyz"]="abc"; params.find("xyz"); // test it here before we call the other function return Execute(¶ms); } I was using Visual C++ 6.0. Compiling it with any other compiler seems to work fine (including newer versions of Visual C++)

                        J Offline
                        J Offline
                        Jorgen Sigvardsson
                        wrote on last edited by
                        #30

                        I am equally perplexed. On the other hand, VC6's implementation of STL isn't the hottest either. Have you tried STL Port?[^]

                        -- Kein Mitleid Für Die Mehrheit

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups