Reputation: 155
As a learning exercise I've written a standard Windows program that registers and creates a window without explicitly including Windows.h
. All header symbols from Windows.h
have been extracted and placed into a custom header which I include into the source. The custom header has type definitions like CreateWindow
and WNDCLASSEX
.
The program is compiled with cl.exe
with a command line like so:
cl main.cpp /link opengl32.lib gdi32.lib kernel32.lib user32.lib
It is my understanding that these .lib files are import libs which do setup work for loading in function addresses from the associated DLLs upon process startup. The above command line will work perfectly fine if main.cpp
includes Windows.h
directly. However, when including the custom header cl.exe
fails to link all function calls made into the Windows API, despite having specified the associated import libs on the command line (unresolved external symbol errors).
Is there some kind of special-case magic cl.exe
performs specifically for Windows.h
? What steps are necessary to ensure proper linking in this scenario?
Here is the smallest example I could construct:
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
typedef __int64 LONG_PTR, *PLONG_PTR;
typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;
typedef LONG_PTR LRESULT;
typedef int INT;
typedef unsigned int UINT;
typedef char CHAR;
typedef unsigned short WORD;
#define CONST const
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HICON);
DECLARE_HANDLE(HCURSOR);
DECLARE_HANDLE(HBRUSH);
DECLARE_HANDLE(HMODULE);
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef CHAR *NPSTR, *LPSTR, *PSTR;
typedef CONST CHAR *LPCSTR, *PCSTR;
#define CALLBACK __stdcall
#define WINAPI __stdcall
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
#define DECLSPEC_IMPORT __declspec(dllimport)
#define WINUSERAPI DECLSPEC_IMPORT
#define WINBASEAPI DECLSPEC_IMPORT
#define CS_VREDRAW 0x0001
#define CS_HREDRAW 0x0002
#define CS_OWNDC 0x0020
#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#define IDC_ARROW MAKEINTRESOURCE(32512)
#define NULL 0
typedef WORD ATOM; //BUGBUG - might want to remove this from minwin
typedef struct tagWNDCLASSEXA {
UINT cbSize;
/* Win 3.x */
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
/* Win 4.0 */
HICON hIconSm;
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;
WINUSERAPI LRESULT CALLBACK DefWindowProcA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
WINBASEAPI HMODULE WINAPI GetModuleHandleA(LPCSTR lpModuleName);
WINUSERAPI HCURSOR WINAPI LoadCursorA(HINSTANCE hInstance, LPCSTR lpCursorName);
WINUSERAPI ATOM WINAPI RegisterClassExA(CONST WNDCLASSEXA *);
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
return DefWindowProcA( hWnd, message, wParam, lParam );
}
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, INT sw )
{
// register window class
WNDCLASSEXA wcex = { 0 };
wcex.cbSize = sizeof( WNDCLASSEXA );
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = (HINSTANCE)GetModuleHandleA( NULL );
wcex.hIcon = NULL;
wcex.hCursor = LoadCursorA( NULL, IDC_ARROW );
wcex.lpszClassName = "Title";
RegisterClassExA( &wcex );
return 0;
}
Compiled with:
@echo off
call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64
set CONSOLE= -subsystem:windows
set CFLAGS= -Zi -nologo
set CFLAGS= -D__WINDOWS__ -D_CRT_SECURE_NO_WARNINGS %CFLAGS%
set LFLAGS= -incremental:no -opt:ref
set LFLAGS= user32.lib kernel32.lib %LFLAGS%
cl %CFLAGS% main.cpp -Fegame.exe /link %LFLAGS% %CONSOLE%
Upvotes: 2
Views: 2858
Reputation: 490408
No, the tools don't do anything especially for windows.h.
I'd guess you've gotten your header wrong. It's hard to guess what the problem might be without seeing the header, but one possibility sticks out.
For most functions, Windows has both "ANSI" and "Wide" versions, so what you see as CreateWindow
is really two functions CreateWindowA
and CreateWindowW
. CreateWindow
exists only as a macro that maps to either the *A
or *W
name, depending upon whether UNICODE
is defined.
So, if you've provided a prototype/declaration for a function named CreateWindow
, it's not going to link--what exists in the library are CreateWindowA
and CreateWindowW
, not CreateWindow
.
Upvotes: 4