First, assume a software application executable that works with a large number of DLLs specific to that application. Each of those application-specific DLLs are the embodiment of many, many classes, most of which are based on a Letter/Envelope idiom, and thus inherit from the same two Letter and Envelope classes. The Letter/Envelope classes are compiled/linked into a library we'll name 'CommonLib.dll.'
Now, a change is made to the Letter class -- in this case, we'll say a data member was added, thus changing the size of the object when the the class is instantiated. A new CommonLib.dll is compiled and linked, containing the change. (The important point, I think, is that the definition of the object was changed, and thus its size has changed ... we didn't just modify the implementation code.)
Question ------------
Must one re-build the ENTIRE application (since all the other DLLs rely on the base classes in CommonLib.dll) and test it as a whole? Or should it be possible to, instead, switch out just the single CommonLib.dll in which the fundamental base classes actually reside, to test the effect of the change on all the classes/libs that link to and rely upon it?
Test Observations -----------------------
In truth, I know from testing that IF ONE CHANGES THE HEADER FILE / OBJECT DEFINITION, then rebuilding just the single CommonLib.dll and swapping it into place FAILS. If I take the lazy route, rebuild just the one library (CommonLib.dll), and swap out only that particular DLL to test, the run-time behavior gives every appearance of memory corruption ... in the debugger, the values shown for the new data member are far from what they should be, and change at odd, somewhat unpredictable points in the code execution.
Personal Speculation (Tell Me I'm Wrong / Right, and 'Splain it to Me, Please) -------------------------------------------------------------------------------------------
Indeed, I had MORE-than-halfway expected this -- I anticipated that changing the object would cause a mis-match between the various elements of the program, since the other DLLs had been compiled/linked with the old form of the class, and the CommonLib.dll was compiled/linked with the new form. It seems clear that addresses to things in the redefined object no longer go where they ought to, due to the changed class definition. But I would like to understand much more fully and EXACTLY what's going on at run-time, and just where the memory references go awry due to the class definition change.
Since in the past I've been able to change *just the implementation code* in the .cpp file, swap out the same DLL, and test the new behavior without any trouble, it appears to me that it is the change to the header file (i.e., the change in the object definition / size of the object / or layout of the object in memory) that creates the incompatibility.
More puzzlingly, and with much less certainty, it has also seemed from prior new-code-testing situations that the problems arise if one (a) adds a new data member, (b) adds a virtual method, but NOT if one adds a new non-virtual method or, for instance, modifies an existing enum in the class definition. Is this impression correct?
I searched all last night through various Google routes for any discussion of this, and after perhaps a hundred sites found a whole THREE off-the-cuff, not-very-informative mentions: (1) "DLLs created with different versions of the same header file lead to one form of the infamous condition known as 'DLL Hell.' The header file against which an application is compiled must match the version of the header file with which all the runtime DLLs were built." (2) "An application must get a library version that is the same, or newer, than the header file used when building the application." (3) "If the implementation in a template header file differs from the version of the header file with which the DLL was compiled and linked, memory corruption will result at run-time."
I want more. Is someone willing to 'splain to me the specifics of what's going wrong when one does this? I want to understand in much more detail, rather than just take it on faith.
>>Must one re-build the ENTIRE application (since all the other DLLs rely on the >>base classes in CommonLib.dll) and test it as a whole?
If there's any change to the interface to the classes affected by the change (i.e. the class' declaration in the header file), the answer is "yes". If not, this should not be necessary, yet I'd always recommend for a full test. You never know what side effects a change might have and certainly do not want the end users to uncover these.
I suspected as much (and thanks for the confirmation on that), but I was truly hoping for much more detail of what exactly goes awry when the in-memory class implemented within one DLL goes looking for something that's within the implementation of the changed base class object, in the other DLL. What is the old, outdated resource, specifically, to which the first DLL is referring in order to locate the data in the second object/DLL, and where why/where does the hand-off go wrong? Any words of detailed technical wisdom, there? Or am I being too vague in my question to make sense of it? (It's bad when you don't know enough to properly phrase the dang question ...)
And of course you're quite right about doing a full test prior to rolling it out ... but doing a full rebuild /re-isntall in this case is a HUGELY painful undertaking, and it would've been oh-so-nice to be able to test the thing in isolation, as an initial unit testing step. Maybe I'll implement the new data member as a static global in the file, initially, before moving it to its proper place, just to get away from modifying the object definition ... hmmmmmmmmmm.
If you change the interface, you have to recompile. Any build environment will detect the dependecy and trigger the update. Anr it's hard to give more details without having seen a single line of code ;o)
I hear ya. Can't post the actual code, for various reasons. Let me go off for a bit and try to pare it down to a small-scale example. I think this is one of those things that should be fundamentally obvious to me (and it is, at a conceptual level), but I've found myself in the position of having to explain this to a Lead who says he can't imagine why just rebuilding the one DLL wouldn't work. <sigh> So, I've gotta get into the nitty gritty of just WHY this is an obviously bad idea, and just where it goes wrong, and learn something about DLL interaction, in the process, for myself.
Gimme a little time, here, to come up with some example code ...
class __declspec(dllexport) foo {
public:
char* GetBuffer() { return m_arr;}
protected:
int m_nVar;
char m_arr[100];
int m_nOther;
double m_dMore;
};
// app
foo bar[16];
char* p = bar[2].GetBuffer(); // what is 'p' gonna point at if the buffer size is changed?