caveats when exporting classes from a dll?
-
Hi, I am exporting classes from a dll, and I try to avoid recompiling the world, when the dll changes. I am feeling uncertain in that point and would greatly appreciate your advice. I reckoned the only problem would arise when classes with virtual functions would be used by the client. I set up a little experiment with 3 classes A, B and C, with 3 virtual functions each. C inherits from B and B from A. Found.. I can not change the order of virtual functions, nor can I insert one. The vftable entries seem to be permuted resp shifted by the number of insertions. I can insert additional code into a function, so that the function entries in the map file have shifted adresses. But still the client works without recompiling. I thought it would crash, why not does it? I thought this would result in wrong vftable entries. Are these not generated at dll compile time? These problems only seem occur when I use pointers to instances of these classes. I.e. as long as I do not pass any instance from a dll class via reference, or make a pointer to it, I will not have to recompile the client. True? Best regards Werner
-
Hi, I am exporting classes from a dll, and I try to avoid recompiling the world, when the dll changes. I am feeling uncertain in that point and would greatly appreciate your advice. I reckoned the only problem would arise when classes with virtual functions would be used by the client. I set up a little experiment with 3 classes A, B and C, with 3 virtual functions each. C inherits from B and B from A. Found.. I can not change the order of virtual functions, nor can I insert one. The vftable entries seem to be permuted resp shifted by the number of insertions. I can insert additional code into a function, so that the function entries in the map file have shifted adresses. But still the client works without recompiling. I thought it would crash, why not does it? I thought this would result in wrong vftable entries. Are these not generated at dll compile time? These problems only seem occur when I use pointers to instances of these classes. I.e. as long as I do not pass any instance from a dll class via reference, or make a pointer to it, I will not have to recompile the client. True? Best regards Werner
First, I'm no expert on this, so I may be wrong. As I understand it you have three questions: 1: Why does your vtable still works although the functions in the dll have changed base adresses? 2: When are vtables generated? 3: How are dlls linked to the main app? 1: (actually ties in with two and three, but here goes) The vtable is populated at compile time. The main app calls the virtual function through the vtable, but reads the vtabel at runtime, not compile time. That's why it will work as long as the entries in the vtable doesn't change position. For instance, it's important that the adress of CYourClass::Foo is always the second entry in the vtable, but it's not important (at compile time) what the actual value of the second entry is. 2: at compile time. 3: When the app is started the dlls that it depends on are loaded (in theory, at least), and the entries into the dll are read from the export table. This is dynamic linking, where the function calls are resolved at runtime. Even though your statically link to your dll Windows will still dynamically link the dll - all the LoadLibrary and GetProcAddress calls are just done behind the scenes. So, as long as the app can find the right entries in the table the dll will work. Why it doesn't work with function pointers is a little more unclear to me, but that will of course depend on how you construct and use the pointers. Cheers Steen. "To claim that computer games influence children is ridiculous. If Pacman had influenced children born in the 80'ies we would see a lot of youngsters running around in dark rooms eating pills while listening to monotonous music"
-
First, I'm no expert on this, so I may be wrong. As I understand it you have three questions: 1: Why does your vtable still works although the functions in the dll have changed base adresses? 2: When are vtables generated? 3: How are dlls linked to the main app? 1: (actually ties in with two and three, but here goes) The vtable is populated at compile time. The main app calls the virtual function through the vtable, but reads the vtabel at runtime, not compile time. That's why it will work as long as the entries in the vtable doesn't change position. For instance, it's important that the adress of CYourClass::Foo is always the second entry in the vtable, but it's not important (at compile time) what the actual value of the second entry is. 2: at compile time. 3: When the app is started the dlls that it depends on are loaded (in theory, at least), and the entries into the dll are read from the export table. This is dynamic linking, where the function calls are resolved at runtime. Even though your statically link to your dll Windows will still dynamically link the dll - all the LoadLibrary and GetProcAddress calls are just done behind the scenes. So, as long as the app can find the right entries in the table the dll will work. Why it doesn't work with function pointers is a little more unclear to me, but that will of course depend on how you construct and use the pointers. Cheers Steen. "To claim that computer games influence children is ridiculous. If Pacman had influenced children born in the 80'ies we would see a lot of youngsters running around in dark rooms eating pills while listening to monotonous music"
Thank you! The vtable is populated at compile time. The main app calls the virtual function through the vtable, but reads the vtabel at runtime, not compile time. > => when I insert a virtual function or change its position, the the client will read a wrong function address instead of the original one, if it does use the vtable. Why it doesn't work with function pointers is a little more unclear to me, but that will of course depend on how you construct and use the pointers. > you gave the answer. When I use a class from a dll this way MyDllClass theClass; theClass.aVirtualFunc(); // the vtable will not be used when calling this function (right?). It's clear, to which class aVirtualFunc refers, it can be referred to by it's export name or ordinal. But here MyDllBaseClass * ptheClass = new(MyDllClass); ptheClass->aVirtualFunc(); // the vtable will be used. Beware of changing the functions position in the dll without recompiling the client. The wrong function will be used (it happened also when I used a MyDllClass * instead of MyDllBaseClass *). or even here, I think - and less obvious - MyDllClass theClass; doSomethingWith(theClass); .. void doSomethingWith(MyDllClass & refToMyClass) {.. // because there is an implicit pointer operation. . Cheers Werner
-
Hi, I am exporting classes from a dll, and I try to avoid recompiling the world, when the dll changes. I am feeling uncertain in that point and would greatly appreciate your advice. I reckoned the only problem would arise when classes with virtual functions would be used by the client. I set up a little experiment with 3 classes A, B and C, with 3 virtual functions each. C inherits from B and B from A. Found.. I can not change the order of virtual functions, nor can I insert one. The vftable entries seem to be permuted resp shifted by the number of insertions. I can insert additional code into a function, so that the function entries in the map file have shifted adresses. But still the client works without recompiling. I thought it would crash, why not does it? I thought this would result in wrong vftable entries. Are these not generated at dll compile time? These problems only seem occur when I use pointers to instances of these classes. I.e. as long as I do not pass any instance from a dll class via reference, or make a pointer to it, I will not have to recompile the client. True? Best regards Werner
A dll has functions exported by ordinal, and (optionally) name. When you create a dll you have 2 ways of exporting data/functions: 1. use an export file Here you explicitly define the ordinal (and optionally hide the name) of the functions to be exported. Because you control the ordinal you control any changes that occur between releases. Usually when new functions are added you just assign them new (unused) ordinal values. Class methods are exported using their mangled names, which makes this a tedious way of exporting class' by hand, usually you will have another program generate this file. 2. use __declspec(dllexport) Here the compiler auto-generates the export table from all __declspec(dllexport) it finds. You have no control over the ordinals assigned to data/functions. I assume you are using __declspec(dllexport) statements. What you are talking about is observing an effect and inferring a reason. You can NOT assume your conclusions will hold for all cases - they won't. To get the result you want you need to use an export file. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_core_export_functions_from_a_dll_by_ordinal_rather_than_by_name.asp[^] ...cmk Save the whales - collect the whole set
-
Thank you! The vtable is populated at compile time. The main app calls the virtual function through the vtable, but reads the vtabel at runtime, not compile time. > => when I insert a virtual function or change its position, the the client will read a wrong function address instead of the original one, if it does use the vtable. Why it doesn't work with function pointers is a little more unclear to me, but that will of course depend on how you construct and use the pointers. > you gave the answer. When I use a class from a dll this way MyDllClass theClass; theClass.aVirtualFunc(); // the vtable will not be used when calling this function (right?). It's clear, to which class aVirtualFunc refers, it can be referred to by it's export name or ordinal. But here MyDllBaseClass * ptheClass = new(MyDllClass); ptheClass->aVirtualFunc(); // the vtable will be used. Beware of changing the functions position in the dll without recompiling the client. The wrong function will be used (it happened also when I used a MyDllClass * instead of MyDllBaseClass *). or even here, I think - and less obvious - MyDllClass theClass; doSomethingWith(theClass); .. void doSomethingWith(MyDllClass & refToMyClass) {.. // because there is an implicit pointer operation. . Cheers Werner
WernerP wrote:
MyDllClass theClass; theClass.aVirtualFunc(); // the vtable will not be used when calling this function (right?). It's clear, to which class aVirtualFunc refers, it can be referred to by it's export name or ordinal.
I think the vtable will be used regardless of whether you use . or ->, but it may be implementation-specific. In any regard, you shouldn't count on it. As cmk wrote, you have no control of the ordinals assigned to the exported functions when you use __declspec(dllexport). So the safest option is to recompile. Anyway, if you headers doesn't change it's only relinking which is pretty fast. If your headers change you need to recompile anyway. Cheers Steen. "To claim that computer games influence children is ridiculous. If Pacman had influenced children born in the 80'ies we would see a lot of youngsters running around in dark rooms eating pills while listening to monotonous music"
-
A dll has functions exported by ordinal, and (optionally) name. When you create a dll you have 2 ways of exporting data/functions: 1. use an export file Here you explicitly define the ordinal (and optionally hide the name) of the functions to be exported. Because you control the ordinal you control any changes that occur between releases. Usually when new functions are added you just assign them new (unused) ordinal values. Class methods are exported using their mangled names, which makes this a tedious way of exporting class' by hand, usually you will have another program generate this file. 2. use __declspec(dllexport) Here the compiler auto-generates the export table from all __declspec(dllexport) it finds. You have no control over the ordinals assigned to data/functions. I assume you are using __declspec(dllexport) statements. What you are talking about is observing an effect and inferring a reason. You can NOT assume your conclusions will hold for all cases - they won't. To get the result you want you need to use an export file. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_core_export_functions_from_a_dll_by_ordinal_rather_than_by_name.asp[^] ...cmk Save the whales - collect the whole set
Thanks. Option 1 is tedious indeed. I have been looking for ways how to generate a def file with mangled names, they talk about using (1) the VC++ /FAcs compiler option to get a listing (2) the VC++ generate map file option when linking (3) the DUMPBIN.EXE on the dll to get the mangled names. There is also a tool from http://www.objmedia.demon.co.uk/freeSoftware/def32.html[^] which creates a def file from the map file and also assigns ordinals to it. The result looks ok (but haven't yet tested compiling and linking). regards Werner
-
Thanks. Option 1 is tedious indeed. I have been looking for ways how to generate a def file with mangled names, they talk about using (1) the VC++ /FAcs compiler option to get a listing (2) the VC++ generate map file option when linking (3) the DUMPBIN.EXE on the dll to get the mangled names. There is also a tool from http://www.objmedia.demon.co.uk/freeSoftware/def32.html[^] which creates a def file from the map file and also assigns ordinals to it. The result looks ok (but haven't yet tested compiling and linking). regards Werner