Passing a pointer to a struct into a dll

windows 10
visual studio 2019 community

I have an exe project with a large struct in it. I also have a dll project. I pass a pointer to the struct in the exe into the dll after loading the dll with LoadLibrary.

If I inspect the struct whilst debugging the exe it is as it should be. If I call a function in the dll I can see (in the debugger) that immediately upon calling the function the 2nd half of the struct is corrupted (the data changes). Immediately upon the dll function returning to the exe I can see (in the debugger) that the corrupted data returns to what it should be.

It's my understanding that the exe and dll share the same address space, so why is the data in the struct corrupted when inside the dll and why does it return to normal when returning to the dll? Can I mark the struct in some way to make it stay valid when inside the dll? All I can think of is to copy the struct into the dll instead of passing a pointer but I'd rather not because the data in the struct changes over time.

In exe:

struct FOO
{
...
};
FOO foo =
{
...
};

In dll:

FOO* dllFoo = NULL;

dllFoo is exported in a def file, it's address is obtained using GetProceAddress, and a pointer to foo is copied into it. All this must be ok because the first half of the struct is correct when inside the dll (it's complex data not all zeros, and it's correct up to a point).
Last edited on
Have the .exe and .dll been re-compiled using the same compiler with the same settings and using the same struct definition? Are the struct member variables at the same memory offset in the .dll as they are in the .exe? Access to the struct members is done via memory offsets when running code - not by name. So the offsets used in the .dll must match those used in the .exe. structs can be 'padded' with extra bytes for alignment with 4/8/ byte boundaries etc. This 'padding' alignment must be the same for the .exe and the .dll.

The 2 projects are in the same visual studio solution with the same settings throughout, and I've rebuilt the whole solution numerous times trying to figure it out, so it's not to do with alignment. I can see right there in the debugger that the values in the 2nd half are changing when entering the dll and returning to their correct values on returning to the exe. I know that it's not the debugger because the garbage values from within the dll caused the program to crash (that's how I noticed it to begin with). The 2nd half of the memory is being overwritten when entering the dll and then replaced when returning to the exe. The only thing that I can think is that the compiler is using that memory to store some state in (like it's pushing / popping on the stack) and then returning it to its correct values when returning to the exe, but the struct is a global variable in the exe and therefore its memory should not be used in this way. In the disassembly window I can see that the memory is being overwritten on the 'call' instruction and fixed on the 'ret' instruction - there are no hidden instructions doing something behind the scenes. It's nuts.
What's the format of the struct?
1
2
3
4
5
6
7
8
9
10
11
struct FOO
{
    BOOL a;
    BOOL b;
    int c;
    void (*d)(int);
    :
    (about 50 other function pointers)
    :
    int (*z)(float);
};
Does it still 'corrupt' when you look at the struct in a memory window?
https://docs.microsoft.com/en-us/visualstudio/debugger/memory-windows?view=vs-2019
No "interpretation", just "bytes".

> The 2 projects are in the same visual studio solution with the same settings throughout
And if the DLL project silently transforms those settings into something else, would you necessarily know?

I'm going with "it's an alignment issue".
Especially if just watching the memory shows no difference, which is my guess.

Or use the debugger expression evaluator to show you say sizeof(struct FOO) in both contexts.
If you get a different answer in the exe and in the DLL, then it's alignment.
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

 
class ClassA;


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?
Last edited on
When a class has been forward declared, the compiler doesn't know the size of the class until the class has been fully defined - which it hasn't been. Hence memory isn't properly allocated for the class. Hence the difference in the sizes. Hence the issue with class contents etc etc.

Yes I can see that. But a pointer is a pointer no? In TheStruct there is one function pointer, so the size of the struct should be either 4 or 8 depending on the size of a pointer. What does it matter if the compiler doesn't know the structure of what the pointer points to? Instead my example proves that the size of the struct with one pointer in it is either 4 or 16! Why? 16 doesn't even make any sense at all...
Instead my example proves that the size of the struct with one pointer in it is either 4 or 16! Why? 16 doesn't even make any sense at all...
Dunno if this helps, https://stackoverflow.com/questions/29607359/size-of-pointer-to-member-function-varies-like-crazy

This maybe worth reading too, https://devblogs.microsoft.com/oldnewthing/20040209-00/?p=40713
Last edited on
Essentially a "pointer to member function" is a data structure beyond a simple pointer.

A pointer-to-member function needs to support dynamic dispatch, which is complicated by multiple inheritance, virtual base classes, and so on. The class type might even be incomplete.
Yes, this appears to be the answer. The compiler is 'assuming the worse' and allocating 16 bytes for the pointer because it doesn't know what it's pointing at (16 bytes being the absolute maximum space it would use).

For anyone facing the original problem I had, you need to make sure that both the exe and the dll either have the incomplete or complete definitions (they both need to see the same thing). It's obvious once you know why it's happening, but not before.
Registered users can post here. Sign in or register to post.