Handmade Hero»Forums»Code
54 posts
Compiling with Windows.h
Edited by Randy Gaul on
I'm trying to compile without including Windows.h from this idea by mmozieko. Making miniwin.h went quite smoothly (but took forever). Once I hit compile I get linker errors for every single function I used to use, specifically functions in gdi32.lib, user32.lib and kernel32.lib.

I thought the job of an important library was to cover the hassle of manually loading function pointers via GetProcAddress. In my command line I am specifying:

1
cl ... /link user32.lib kernel32.lib opengl32.lib gdi32.lib

Does anyone know why including Windows.h affects the import libs success? Is there any way to mimic this inside of one's own custom miniwin.h header? If not, is the only solution to manually load all functions through GetProcAddress, and if so is there a way to avoid this sort of nastiness?
511 posts
Compiling with Windows.h
There are most likely lib statements that signal to the linker that a certain lib is needed.
Mārtiņš Možeiko
2559 posts / 2 projects
Compiling with Windows.h
Edited by Mārtiņš Možeiko on
Including windows.h is only for compiler. When compiler compiles code that calls functions (yours or Windows API ones, doesn't matter), then it simply puts function name in object file. Basically "this code calls function named X".

When linker creates executable (or dll) from object files, then it needs to know where are all the functions. If function X is in other object file, then linker puts call to its address. But when linker doesn't know where is X (like VirtualAlloc in kernel32.dll) then you need to specify import library. Those *.lib files are for linker to know which functions are in which dll files. Because it needs to generate code that inform OS than your exe/dll file will need function X in Y.dll file.

Using import libraries is perfectly fine and there really is no good reason to not to use them.
54 posts
Compiling with Windows.h
Edited by Randy Gaul on
mmozeiko
Including windows.h is only for compiler. When compiler compiles code that calls functions (yours or Windows API ones, doesn't matter), then it simply puts function name in object file. Basically "this code calls function named X".

When linker creates executable (or dll) from object files, then it needs to know where are all the functions. If function X is in other object file, then linker puts call to its address. But when linker doesn't know where is X (like VirtualAlloc in kernel32.dll) then you need to specify import library. Those *.lib files are for linker to know which functions are in which dll files. Because it needs to generate code that inform OS than your exe/dll file will need function X in Y.dll file.

Using import libraries is perfectly fine and there really is no good reason to not to use them.


Thanks for the reply. I'm trying to explain that I want to use the import libs, however only specifying them on the command line like I've shown is not enough. I have copy + pasted out every symbol from Windows.h into a custom header, and have specified all required import libs on the command line (shown in original post), and still cannot properly link. Including Windows.h seems to signal to the linker some information somehow:

ratchetfreak
There are most likely lib statements that signal to the linker that a certain lib is needed.


So I'm asking, what do I need to do in order to compile + link without including Windows.h? I've searched inside of various Windows headers for "pragma comment", but cannot seem to find anything special. Perhaps MSVC is doing some special-case magic when compiling Windows.h. So, what additional steps should I be taking to properly use import libs for libraries like opengl32.lib, user32.lib, gdi32.lib, and kernel32.lib?
54 posts
Compiling with Windows.h
Edited by Randy Gaul on
Figured it out! I had just forgot to wrap the entire file in an
1
extern "C"
declaration. Thank you for reading the thread, both of you :)
Mārtiņš Možeiko
2559 posts / 2 projects
Compiling with Windows.h
Edited by Mārtiņš Možeiko on
Next time show exact error message from linker ("error LNK2019: unresolved external symbol: _blahblah"). Then we would instantly know what is the exact problem :)
11 posts
Friendly old programmer from the days of the BBC and Amiga
Compiling with Windows.h
I rather like this idea, the last time I checked the pre-processor output the windows guff made me feel a little ill.

I'd like to do this and removing the C runtime, I just had a quick bash at it but tripped up fairly quickly.

Here's my MiniWin.h
 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
// Bloatless version of windows.h, just including the things from windows.h that I need

extern "C"

struct HINSTANCE__
{
   int unused;
};
typedef struct HINSTANCE__* HINSTANCE;

typedef char* LPSTR;

#define WINAPI  __stdcall

int WINAPI
WinMain (
         HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPSTR     lpCmdLine,
         int       nShowCmd
        );


typedef const wchar_t* LPCWSTR;

typedef HINSTANCE HMODULE;

HMODULE WINAPI GetModuleHandle(LPCWSTR lpModuleName);

void __stdcall ExitProcess(unsigned int uExitCode);


Then when I try and build a simple (do nothing) program I get the following build errors:

1
2
Main_win32.obj : error LNK2019: unresolved external symbol "struct HINSTANCE__ * __cdecl GetModuleHandle(wchar_t const *)" (?GetModuleHandle@@YAPEAUHINSTANCE__@@PEB_W@Z) referenced in function "void __cdecl WinMainCRTStartup(void)" (?WinMainCRTStartup@@YAXXZ)
Main_win32.obj : error LNK2019: unresolved external symbol "void __cdecl ExitProcess(unsigned int)" (?ExitProcess@@YAXI@Z) referenced in function "void __cdecl WinMainCRTStartup(void)" (?WinMainCRTStartup@@YAXXZ)


I'm guessing this is because the code signatures of the functions in my header don't match those in kernel32.lib
I suspect the problem is that I cut some corners grabbing code from windows.h as going into that is like going down the rabbit hole!

Any ideas? (is there a way of checking what the code signatures are in the lib?)
ta,
Passive
Dominik Madarász
12 posts / 1 project
I'm Dominik, a developer always loving to explore new ways of programming. Since my [b]handicap[/b] made me more-or-less impossible to do certain jobs, I've started with programming, as that's the only thing keeping me on [b]life[/b]. I always like to explore nature, going out with friends and reading some articles or books on the Internet. In my spare time I progress with [b]hobby[/b] and [b]community[/b] projects. My development [b]interests[/b] are mostly around desktop applications, [b]high-performance interactive[/b] apps especially. Taking advices from [b]Casey Muratori's Handmade Hero[/b] series, I aim for [b]optimization and code quality (KISS)[/b] altogether. I love listening to [b]Iron Maiden[/b] and playing [b]The Ultimate Doom[/b], they blend together very well.
Compiling with Windows.h
Edited by Dominik Madarász on Reason: typo
You need to wrap the code inside of extern "C" scope:

 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
extern "C"
{
	struct HINSTANCE__
	{
		int unused;
	};
	typedef struct HINSTANCE__* HINSTANCE;

	typedef char* LPSTR;

#define WINAPI  __stdcall

	int WINAPI
		WinMain(
			HINSTANCE hInstance,
			HINSTANCE hPrevInstance,
			LPSTR     lpCmdLine,
			int       nShowCmd
		);


	typedef const wchar_t* LPCWSTR;

	typedef HINSTANCE HMODULE;

	HMODULE WINAPI GetModuleHandle(LPCWSTR lpModuleName);

	void __stdcall ExitProcess(unsigned int uExitCode);
}


Or you could just write extern "C" inline way:

1
2
extern "C" HMODULE WINAPI GetModuleHandle(LPCWSTR lpModuleName);
extern "C" void __stdcall ExitProcess(unsigned int uExitCode);


Your extern "C" written in code would apply only to your HINSTANCE__ struct.
11 posts
Friendly old programmer from the days of the BBC and Amiga
Compiling with Windows.h
Oh my goodness, how embarrassing!
That was it, also I had to define GetModuleHandle as GetModuleHandleA

1
2
  HMODULE WINAPI GetModuleHandleA(LPCWSTR lpModuleName);
  #define GetModuleHandle GetModuleHandleA


Then its built! Resulting in a 2k exe with a preprocessor output clear of windows guff, yay :)
(It also compiles in 1 second rather than 3 now, joy).

Thanks.
Mārtiņš Možeiko
2559 posts / 2 projects
Compiling with Windows.h
Edited by Mārtiņš Možeiko on
That's wrong. It should be GetModuleHandleW, because you are using LPCWSTR argument. That is wide string, so you need to use W suffix. Otherwise you'll get an crash or wrong result if you'll pass non-NULL string.
11 posts
Friendly old programmer from the days of the BBC and Amiga
Compiling with Windows.h
oh nice catch, thanks mmozeiko
54 posts
Compiling with Windows.h
Edited by Randy Gaul on
When I did this I didn't bother replicating the macros, and instead hard-coded with A suffixes. So I used GetModuleHandleA directly. You can see Casey do this in his videos. I believe it's a nice idea when you start writing your own miniwin.h header, since getting rid of the macro makes it very hard to make mistakes (which can otherwise result in very difficult to track crashes).

Example:

1
2
3
WINUSERAPI BOOL WINAPI PeekMessageA(LPMSG lpMsg, HWND hWnd, INT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg);
WINUSERAPI BOOL WINAPI TranslateMessage(CONST MSG *lpMsg);
WINUSERAPI LRESULT WINAPI DispatchMessageA(CONST MSG *lpMsg);
11 posts
Friendly old programmer from the days of the BBC and Amiga
Compiling with Windows.h
I like that even more, good thinking.
Mārtiņš Možeiko
2559 posts / 2 projects
Compiling with Windows.h
That's how Casey is using real windows.h - he directly calls W or A functions. No need for macros.