Having been making native extensions for GameMaker for years, there is one thing that always bothered me: although the code is often minimal (some of my smaller extensions have less than 100 lines of code!), the DLLs still come out to 70..100KB OR have to depend on Microsoft Visual C++ Runtime Redistributable, which is non-preferable for games that should "just run".
Having finally figured it out, I decided to write a small post about the matter.
Configuration
If you have not made a project yet, you will need a "[C++] Dynamic Link Library (DLL)". This can also be done for "[C++] Empty Project" if you set it up as a DLL in the wizard.
You'll usually only want to do this for your Release configuration so that you can still debug your code as normal (and benefit from safety checks).
These are the things you'll need to change:
- Linker ➜ Input ➜ Ignore All Default Libraries: Yes
The important bit. You can later add needed libraries to Additional Dependencies. Linker ➜ Advanced ➜ No Entry Point: Yes
If you don't, you'll be seeingLNK2001 unresolved external symbol __DllMainCRTStartup@12
Other option is to add an int a_func() {} and specify that name in Entry Point.
- C/C++ ➜ Code Generation ➜ Runtime Library: Multi-Threaded
Removes dependency on MSVCR redist. If you are doing this for Debug configuration, you'll need Multi-Threaded Debug instead. C/C++ ➜ Code Generation ➜ Security Check: Disable
If enabled, generatesLNK2001 unresolved external symbol @__security_check_cookie@4
and other references to symbols that you no longer have.
- C/C++ ➜ General ➜ SDL checks: No
Incompatible with "Security Check" anyway.
Optional:
- Linker ➜ Debugging ➜ Generate Debug Info: No
Spares you of embedding a full path to your project folder for a PDB. - Linker ➜ Manifest File ➜ Generate Manifest: No
If your DLL does not change any particular manifest settings (read: you didn't change anything in "Manifest file" section), you may spare yourself some 400 bytes of data by disabling it entirely.
Caveats and fixes
The ones that I have ran into, anyway
_fltused
If you are using float or double types anywhere, you'll be seeing this error:
dllmain.obj : error LNK2001: unresolved external symbol __fltused
According to this antique post, this is a variable assignment that is emitted (even 15 years later!) by MSVC if any floating-point types are used.
The fix is to define it yourself somewhere:
extern "C" int _fltused = 0;
static initializers
If you initialize static variables inside functions with non-primitive values, like so:
double test() { static auto out = GetStdHandle(STD_OUTPUT_HANDLE); return out != 0; }
you'll be seeing
LNK2019 unresolved external symbol __tls_index referenced in function LNK2019 unresolved external symbol __Init_thread_header referenced in function LNK2019 unresolved external symbol __Init_thread_footer referenced in function LNK2019 unresolved external symbol __Init_thread_epoch referenced in function LNK2019 unresolved external symbol __tls_array referenced in function
There are a few ways to get around this: you could move the static variable out of the function:
static auto test_out = GetStdHandle(STD_OUTPUT_HANDLE); double test() { return test_out != 0; }
Or you could add a flag for it:
double test() { static HANDLE out{}; static auto ready = false; if (!ready) { ready = true; out = GetStdHandle(STD_OUTPUT_HANDLE); } return out != 0; }
printf
I have a separate post about this, but, in short, if you want a normal-looking printf function for logging, you can replicate it with WinAPI like so:
void my_printf(const char* pszFormat, ...) { char buf[1024]; va_list argList; va_start(argList, pszFormat); wvsprintfA(buf, pszFormat, argList); va_end(argList); DWORD done; WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &done, NULL); }
after adding user32.lib and kernel32.lib to Additional Dependencies.
And with that we have turned a 70KB DLL into a 2KB DLL!
Was having some trouble a little while ago with missing MSVC++ dependencies, hopefully (among other things) this will take care of those! Fun stuff.
Seriously, every forum or GM page I ever open you’re always there, Dragonite. You’re like Gary, always mocking me with your previous presence.