Ok, I've worked it out.
First, thanks to salem c for suggesting I do sizeof(struct FOO) in the exe and dll to see what it says, because yes it was different. Why? Forward declaration leads to different sizes... I'll explain what I did to prove it (because I still don't understand WHY this is happening). As far as I know a pointer is a pointer and they're all the same length...
You don't have to do this, but you can do it yourself to prove it.
Create a blank solution in visual studio.
Add a "windows desktop application" project.
Add a dll project.
Add an empty file called TheStruct.h to the desktop application project and put this in it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
#ifdef _USRDLL
// DLL is being compiled.
class ClassA
{
public:
BOOL Fn(int i);
};
#else
// exe is being compiled.
class ClassA;
#endif
#pragma pack(show)
#pragma pack(push)
// It doesn't matter what you set this to, the result is the same.
#pragma pack(8)
#pragma pack(show)
struct TheStruct
{
BOOL (ClassA::*fnA)(int i);
};
#pragma pack(pop)
#pragma pack(show)
#ifndef _USRDLL
// exe is being compiled.
class ClassA
{
public:
BOOL Fn(int i);
};
#endif
| |
Add the following to the dll project:
1 2 3 4 5 6 7
|
#include "../WindowsDesktopApplication/TheStruct.h"
extern "C" __declspec(dllexport) void DllFn(TheStruct* i)
{
size_t theStructSizeInDLL = sizeof(TheStruct); // <- 0x04
__debugbreak();
}
| |
Add the following to the desktop application (at the global level):
1 2 3
|
#include "TheStruct.h"
BOOL ClassA::Fn(int i) {return TRUE;}
| |
Add the following to the desktop application (inside WinMain):
1 2 3 4 5 6 7 8
|
TheStruct theStruct = {&ClassA::Fn};
__debugbreak();
HMODULE dllHandle = LoadLibraryA("DLL.dll");
void (*dllFn)(TheStruct*) = (void (*)(TheStruct*))GetProcAddress(dllHandle, "DllFn");
dllFn(&theStruct);
size_t theStructSizeInExe = sizeof(TheStruct); // <- 0x10
__debugbreak();
| |
See the "// <- 0x.." comments in the code above... inside the dll TheStruct is 4 bytes in size, but in the exe it's 16 bytes in size. If you look at TheStruct.h you can see that the difference is that, at the point that
1 2 3 4
|
struct TheStruct
{
BOOL (ClassA::*fnA)(int i);
};
| |
is being compiled the dll sees ClassA as
1 2 3 4 5
|
class ClassA
{
public:
BOOL Fn(int i);
};
| |
but the exe see ClassA as
If you remove that difference, so that they both see it as one or the other, then the dll and exe will agree upon the size of TheStruct.
Why? You tell me...
I should point out that this is a highly simplified version of my original problem. In reality TheStruct has a lot of function pointers and other variables in it and I actually pass a pointer to TheStruct into the DLL so that the DLL can call the functions through the function pointers. But the above is the root cause of my problem.
The question is: why is a function pointer to a class member function whose class has been defined a different size to a function pointer to a class member function whose class has simply been forward declared?