loading dll issue

so i was using a plugin system from this article: cplusplus.com/articles/48TbqMoL in my program using sdl2. i created a dll plugin to test the program and when i run the program using the dll the program's console starts printing a bunch of random strings and then crashes. i can't find any errors in the code and when i debugged the dll was loaded and all of the functions were too but when i call a function that returns a string it crashes. so all i can thing of that is causing this issue is a compiling error.

here is the code:

main.cpp:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#include <SDL.h>
#include <lua.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <Windows.h>
#include <vector>
#include <memory>

#include "conio.h"
#include "game.h"
#include "LTimer.h"
#include "modbase.hpp"

using namespace std;

game* g_game = new game;

LTimer capTimer;

const int FPS = 60;
int SCREEN_TICK_PER_FRAME = 1000 / FPS;

// Load the objects from the plugin folder.
//
// Takes as a parameter a reference to a list of modules,
// which will be emptied and then refilled with handles to
// the modules loaded. These should be freed with the
// FreeLibrary() after use.
//
// Returns a list of Base*, contained in a smart pointer
// to ease memory deallocation and help prevent memory
// leaks.
std::vector<std::unique_ptr<ModBase>> getPlugins(std::vector<HINSTANCE>& modules) {
	// A temporary structure to return.
	std::vector<std::unique_ptr<ModBase>> ret;
	// empty the modules list passed
	modules.clear();

	// Find the files contained in the 'plugins' folder
	WIN32_FIND_DATA fileData;
	HANDLE fileHandle = FindFirstFile(R"(mods\*.dll)", &fileData);

	if (fileHandle == (void*)ERROR_INVALID_HANDLE ||
		fileHandle == (void*)ERROR_FILE_NOT_FOUND) {
		// If we couldn't find any plugins, quit gracefully,
		// returning an empty structure.
		return std::vector<std::unique_ptr<ModBase>>();
	}

	// Loop over every plugin in the folder and store in our
	// temporary return list
	do {
		// Define the function types for what we are retrieving
		typedef std::unique_ptr<ModBase>(__cdecl *ObjProc)(void);
		typedef std::string(__cdecl *NameProc)(void);
		typedef void(__cdecl *InitProc)(void);

		// Load the library
		HINSTANCE mod = LoadLibrary((R"(mods\)" + std::string(fileData.cFileName)).c_str());

		if (!mod) {
			// Couldn't load the library, cleaning module list and quitting.
			for (HINSTANCE hInst : modules)
				FreeLibrary(hInst);
			throw std::runtime_error("Library " + std::string(fileData.cFileName) + " wasn't loaded successfully!");
		}

		// Get the function and the class exported by the DLL.
		// If you aren't using the MinGW compiler, you may need to adjust
		// this to cope with name mangling (I haven't gone into this here,
		// look it up if you want).
		ObjProc objFunc = (ObjProc)GetProcAddress(mod, "getObj");
		NameProc nameFunc = (NameProc)GetProcAddress(mod, "getName");
		InitProc initFunc = (InitProc)GetProcAddress(mod, "initDLL");

		if (!objFunc || !nameFunc || !initFunc)
			throw std::runtime_error("Invalid Plugin DLL: both 'getObj' , 'getName' and 'init' must be defined.");

		// push the objects and modules into our vectors
		ret.push_back(objFunc());
		modules.push_back(mod);

		clog << nameFunc() << endl;
		initFunc();

	} while (FindNextFile(fileHandle, &fileData));

	clog << endl;

	// Close the file when we are done
	FindClose(fileHandle);
	return ret;
}

//return a random string from a list of strings
string getRandStr()
{
	int r = rand() % 9 + 1;
	string returnStr;

	switch (r)
	{
	case 1:
		returnStr = "Spacey things!";
		break;
	case 2:
		returnStr = "Like the Oregon Trail!";
		break;
	case 3:
		returnStr = "3.1415926% Death!";
		break;
	case 4:
		returnStr = "Herry has dysentery..";
		break;
	case 5:
		returnStr = "Explosions!";
		break;
	case 6:
		returnStr = "I\'m sorry, Dave. I\'m afraid I can\'t do that.";
		break;
	case 7:
		returnStr = "It\'s a TRAP!";
		break;
	case 8:
		returnStr = "Mineing For Gold!";
		break;
	case 9:
		returnStr = "Get In The Ship!";
		break;
	case 10:
		returnStr = "Space For Dummies!";
		break;
	default:
		returnStr = "IMPOSSABREW";
		break;
	}
	return returnStr;
}

int main(int argc, char *argv[])
{
	std::vector<HINSTANCE> modules;

	// Enter a block, to prevent the Base objects being
	// deallocated after the modules are freed (which will
	// cause your program to crash)
	{

		//random seed
		srand(time(0));
		string name = getRandStr();
		//init game
		if (!g_game->g_init(name.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOW_SHOWN))
		{
			//best error ever
			cout << "well shit..." << endl;
			_getch();
			return 0;
		}
		else
		{
			//load media
			if (!g_game->g_loadMedia())
			{
				//throw this
				cout << "well shit..." << endl;
				_getch();
				return 0;
			}
			else
			{
				std::vector<std::unique_ptr<ModBase>> objs;
				// Load the plugins using our function
				try {
					objs = getPlugins(modules);
				}
				catch (const std::exception& e) {
					std::cerr << "Exception caught: " << e.what() << std::endl;
					_getch();
					return 1;
				}

				//successful load!
				cout << "-------------------------\nSuccessfully loaded!" << endl;

				while (!g_game->g_getStop())
				{
					capTimer.start();

					g_game->g_handleEvents();
					g_game->g_update();

					g_game->g_preRender();
					g_game->g_render();

					for (auto& x : objs) 
					{
						x->update();
						x->render();
					}

					g_game->g_aftRender();

					int frameTicks = capTimer.getTicks();
					if (frameTicks < SCREEN_TICK_PER_FRAME)
					{
						SDL_Delay(SCREEN_TICK_PER_FRAME - frameTicks);
					}
				}
			}
		}

		g_game->getFlieO();

		//close program
		for (HINSTANCE hInst : modules)
			FreeLibrary(hInst);

		delete g_game;
		g_game = NULL;
		return 0;
	}
}


modbase.hpp:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#ifndef __MODBASE_HPP_INCLUDED__
#define __MODBASE_HPP_INCLUDED__

#include <memory>
#include <string>

// Test to see if we are building a DLL.
// If we are, specify that we are exporting
// to the DLL, otherwise don't worry (we
// will manually import the functions).
#ifdef BUILD_DLL
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI
#endif // BUILD_DLL

// This is the base class for the class
// retrieved from the DLL. This is used simply
// so that I can show how various types should
// be retrieved from a DLL. This class is to
// show how derived classes can be taken from
// a DLL.
class ModBase {
public:
	virtual ~ModBase() = default;

	// Pure virtual function to render dll
	virtual void render(void) = 0;

	// Pure virtual function to update dll
	virtual void update(void) = 0;
};


// DLL export funcs

// Get an instance of the derived class
// contained in the DLL.
DLLAPI std::unique_ptr<ModBase> getObj(void);

// Get the name of the plugin. This can
// be used in various associated messages.
DLLAPI std::string getName(void);

//init the dll
DLLAPI void initDLL(void);

#endif // __MODBASE_HPP_INCLUDED__ 


mtestplugin.cpp:
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
#include "stdafx.h"
#include <iostream>
#include <cmath>
#include <windows.h>
#include <string>
#include "modbase.hpp"

class mtestplugin : public ModBase {
public:
	void render(void)
	{
	
	}

	void update(void)
	{
	
	}
};

std::string getName(void) {
	return std::string("a");
}

std::unique_ptr<ModBase> getObj(void) {
	return std::unique_ptr<ModBase>(new mtestplugin);
}

void initDLL(void)
{
	std::cout << "hia" << std::endl;
}


mtestplugin.def
1
2
3
4
5
LIBRARY mtestplugin
EXPORTS
  getObj
  getName
  initDLL


i have tried to fix this for weeks but nothing is working
The problem is your getObj and getName functions. The issue with using DLLs is that they have to be built by the same compiler and with the exact same settings. If you build the application and DLL in different mods (e.g. Debug vs Release) they will have different definitions of the standard types like std::string and such. This means that while your DLL's getName function returns one kind of std::string, your application interprets it as a different kind of std::string with a different memory layout.

You must use types that will always remain the same between DLL and application regardless of compiler and/or build settings. Otherwise you will have to painstakingly track down which settings are different and make them the same.
Last edited on
It's best to avoid the C++ STL altogether when making a DLL interface. If you want use it, by all means use it internally, but avoid it being used when trying to create an interface.

Good:
1
2
3
4
5
6
7
8
class ToExport
{
    public:
        void SetString(const char* newStr);
        const char* GetString() const;
    private:
        const char* str;
};


Not Good:
1
2
3
4
5
6
7
8
9
10
#include <string>

class ToExport
{
    public:
        void SetString (const std::string &newStr);
        std::string GetString() const;
    private:
        std::string str;
};
Last edited on
Another consideration is structure member padding, which can vary.
i hate how the settings have to be exactly the same but ill just put it in the documentation for set up. thanks!
Well it's something that really can't be helped due to the lack of a binary standard for C++. That's why it's preferred to use C for DLL interfacing.
Topic archived. No new replies allowed.