Just started Windows api....

I just started to learn how to code in windows api, I learnt about handles windows and instances ect... I managed to code something I THINK should display a window however I keep getting linker errors

Here is the code :
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
#include <windows.h>

LRESULT CALLBACK WindowProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );

int WINAPI windmain(HINSTANCE hInstance,HINSTANCE hprevInstance,LPSTR szCmdline, int icmdshow)
{

WNDCLASS wc;
    wc.cbClsExtra = 0;  
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND+1;
    wc.hCursor = LoadCursor(hInstance,IDC_CROSS);
    wc.hIcon = 0;
    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpszClassName = TEXT("MyWnd");
    wc.lpszMenuName = 0;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    
    RegisterClass(&wc);
    
    
    HWND myhandle = CreateWindow(
    TEXT("MyWnd"),TEXT("MyWindow"),WS_OVERLAPPEDWINDOW
    ,540,412,200,200
    ,NULL,NULL
    ,hInstance,NULL);
    
    ShowWindow(myhandle,SW_SHOW);
    UpdateWindow(myhandle);
    
    LPMSG wndmsg;
     GetMessage(wndmsg,myhandle,0,0);
}

and here are the error messages :
In function `int windmain(HINSTANCE__*, HINSTANCE__*, CHAR*, int)':
  [Linker error] undefined reference to `_Z7WndProcP6HWND__jjl@16' 
  [Linker error] undefined reference to `WinMain@16'  
  ld returned 1 exit status 


Is there something I need to put in my code to fix this ?
I thought as soon as I clicked on the window the GetMessage() function would have stopped waiting and ended the program.

I have tried to look for some solution to no avail, I wonder what the problem is :|
Use a default win32 application template to get started.
Hi,
undefined reference
means that symbol (of function/variable) is declared at compilation time, but linker can't found body for it.

[Linker error] undefined reference to `_Z7WndProcP6HWND__jjl@16'


You declared function WindowProc at line 3 and used it at line 18, but there is no code ('body') for it anywhere. You need to write body for this function:

1
2
3
4
LRESULT CALLBACK WindowProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
   ... some code here to process messages ...
}


or use default function shipped with system instead : DefWindowProc. (Body for this function already lives in User32.dll, so we don't write own one in this case).

[Linker error] undefined reference to `WinMain@16' 


This is the same problem, linker searches for WinMain function, but there is no body for it anywhere. WinMain is a default entry point for Windows application. To fix problem you can:

1. Rename windmain function to WinMain.
or
2. Change entry point to windmain in Your linker.

Last edited on
This is somewhat unrelated but....

1
2
    LPMSG wndmsg;
     GetMessage(wndmsg,myhandle,0,0);


This will explode. "LPMSG" is just a pointer to a message... MSG*. The pointer doesn't actually point to anything.

So you're calling GetMessage with a bad pointer, which will be very bad.

Do this instead:

1
2
    MSG wndmsg;
    GetMessage(&wndmsg,myhandle,0,0);
ok thanks a bunch!

EDIT:
I made the changes and added some more code ,so when I click on the window it shows that the program has gotten the message by making a message box, however the window just closes a split second after it is created, why does this happen ?

Updated code:
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
#include <windows.h>

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM);

MSG wndmsg;
HWND myhandle;

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hprevInstance,LPSTR szCmdline, int icmdshow)
{

WNDCLASS wc;
    wc.cbClsExtra = 0;  
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND+1;
    wc.hCursor = LoadCursor(hInstance,IDC_CROSS);
    wc.hIcon = 0;
    wc.style = CS_HREDRAW|CS_VREDRAW;
    wc.lpszClassName = TEXT("MyWnd");
    wc.lpszMenuName = 0;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = (WNDPROC)WndProc;
    
    RegisterClass(&wc);
    
    
    myhandle = CreateWindow(
    TEXT("MyWnd"),TEXT("MyWindow"),WS_OVERLAPPEDWINDOW
    ,540,412,200,200
    ,NULL,NULL
    ,hInstance,NULL);
    
    ShowWindow(myhandle,SW_SHOW);
    UpdateWindow(myhandle);
    
    bool msgloop;
    
    while((msgloop = PeekMessage(&wndmsg, myhandle, 0, 0,PM_REMOVE)) != 0)
    {
     if(msgloop == -1)
     {
                DestroyWindow(myhandle);
     }else{
     TranslateMessage(&wndmsg);
     DispatchMessage(&wndmsg);
     };
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
        while(PeekMessage(&wndmsg ,myhandle ,0 ,0 , PM_REMOVE) )
                 {
                       switch(wndmsg.message)
                       {                  
                              case WM_CLOSE: 
                              if (IDOK == MessageBox(myhandle,"Do you Want to Exit","Exit",MB_OKCANCEL))
                              DestroyWindow(myhandle);
                              break;
                              case WM_LBUTTONDOWN:
                              case WM_RBUTTONDOWN:
                              case WM_MBUTTONDOWN:
                            MessageBox(myhandle,"You pressed the mouse button.","Button Press",MB_OK);
                            break;
                            default:
                       return DefWindowProc(hwnd,message,wparam,lparam);
                       }
                 }
                 return 0;
}
Last edited on
1
2
if ( !managedapp )
  exit(mainret);


That's what triggers under whatever file I stepped into in Visual Studio. I couldn't tell you why because I too just picked up WINAPI, but try modeling your WinMain after this and see if your problem persists. At least you can narrow down whether the problem stems from your WndProc or not. This code DEFINITELY works although keep in mind I have the window styles managed to restrict resizing except for minimization.

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
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{

	// Register the window class
	const LPCSTR CLASS_NAME[] = {"Main Window"};

	WNDCLASS wc = { };

	wc.lpfnWndProc	 = WindowProc;
	wc.hInstance	 = hInstance;
	wc.lpszClassName = *CLASS_NAME;

	RegisterClass(&wc);

	// Create the window
	hwnd = CreateWindowEx(
		0,				// Optional window style
		*CLASS_NAME,			// Window class
		"I Guess This Is A Window?",	// Window text
		WS_OVERLAPPED | WS_SYSMENU |
		WS_MINIMIZEBOX,			// Window styles

		// Size and Position of the window
		CW_USEDEFAULT, CW_USEDEFAULT, 335, 148,

		NULL,				// Parent window
		NULL,				// Menu
		hInstance,			// Instance handle
		NULL				// Additional application data
		);

	// Makes sure the windows are created; else ends the program
	if (hwnd == NULL)
	{
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);

	// Message loop
	MSG msg = { };
	while (GetMessage(&msg, NULL, 0, 0) != 0)
	{
		if (!IsDialogMessage(hwnd, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return 0;
}
@Night of Nights;

Your code for WinMain is fairly correct. I do the end part differently though, like this (From line 32 of yours):

1
2
3
4
5
6
7
8
9
10
ShowWindow(myhandle, icmdshow);
UpdateWindow(myhandle);

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

return (int)msg.wParam;


The reason that your window is closing straight away is due to a problem with your WndProc. Try something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
    switch(message)
    {                  
    case WM_CLOSE: 
        if (IDOK == MessageBox(myhandle,"Do you Want to Exit","Exit",MB_OKCANCEL))
        DestroyWindow(myhandle);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
        MessageBox(myhandle,"You pressed the mouse button.","Button Press",MB_OK);
        break;
    }
    return DefWindowProc(hwnd, message, wparam, lparam);
} 
@ Everyone: You guys are all using the wrong character type.

See this: http://www.cplusplus.com/forum/windows/106683/
If you made it this far and you are still having issues with the window, I'm going to guess that the default setting is on and the program expects to be encountering TCHAR type character strings instead of regular char, or maybe wchar_t depending on what it's set to. If none of that just made sense then I really suggest you read Petzold's Programing Windows API from chapter 1, or look up how to set your program to use ANSI instead of unicode... I think I phrased that right. Personally I tend to just use wchar_t format on everything.

I do have some info you might like though;

NT3 and TDSGoldenSun are both right in that their codes put together will work fine. The default template provided by Visual C++ is one of the most convoluted/confusing ways of going about setting up a window, so I would say that NT3 and GoldenSun have given the best starter's setup.
I'm going to try to go a bit further and explain what was wrong with your previous WndProc() having a loop around it's inner working like that, because when you're just starting out it might seem weird that winMain() doesn't seem to call the WndProc function, so how does it get into your program and keep running, right?

Look at the loop in TDSGoldenSun's WinMain() that goes:
1
2
3
4
5
6
while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
//by the way, don't use peekmessage(), it is taking a "peek" at the next message on the que after the one that should be getting attention, 
//this might be advantageous when you deal in multiple windows, but not to be used to run the main program itself
}


GetMessage() is getting the first message the operating system has in it's que for your window, TranslateMessage() is changing the information in the message so that it can be read by C++ Win API and sent off to the right window (because you can have multiple windows open at once, called child windows, your loop needs to know which window to send it to).
Finally DispatchMessage() is calling your WndProc that was pointed to in the WNDCLASS and it is supplying it with the message information. (so that last function is when the WndProc() gets called - also take note how I phrased that, your WNDCLASS has a pointer to the WndProc function, not important now but may become so in the future)

In essence the WinMain() is doing nothing but initial set-up of the main window and then running that loop to catch messages from the OS and send it to your WndProc() until the window is closed with a PostQuitMessage(0) from the WndProc. The brains of the operation is not the WinMain() but actually in your WndProc, so having that internal while(PeekMessage(etc) actually prevents your window from getting the next message like it should.

This also means that since the WndProc is a function being called by the loop in WinMain(), it loses scope after each loop (meaning all regular variables lose their values between each message retrieved). If you want to keep information from the last time that the function looped (like if WM_KEYDOWN was called and you need to keep the char that was pressed) then you need to declare your variable as static.

In the code below we are taking into account the chance that the user might resize the window screen, so we need to get the new rectangle that represents the new size.
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
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
         //declare variables that you use a lot in this scope
    static RECT crect; //declare the crect rectangle to be static, we want to keep changes made from loop to loop
        //these are given new values each time they are needed so they don't need to be static
    HDC hdc;
    PAINTSTRUCT ps;

    switch(message)
    {     
    case WM_CREATE:
         //if there is any info that you need to set right when the window opens, set it in here.
         //WM_CREATE will be called once and only once when the window is being created.
         //actually it gives you a heads up that the window is about to be created
    break;
    case WM_SIZE://the screen is getting resized by the user
         GetClientRect(hwnd, &crect);//load crect with the new size of the screen  
         InvalidateRect(hwnd, &crect, 1);//the screen is a new size so we want to redraw it.    
    break;
     case WM_PAINT: //A message called whenever the screen needs redrawing
          hdc = BeginPaint(hwnd, &ps);
          //Do any drawing functions between Beginpaint and EndPaint
          Rectangle(hdc, 10, 10, crect.right-10, crect.bottom-10); // draw a simple rectangle onscreen
          EndPaint(hwnd, &ps);
     break;
    case WM_CLOSE: //The user has clicked on the red X to close the screen
                    //If there is any last minute thing you want to happen before closing, do it here
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//sends a message to the OS to end the loop in WinMain()
        break;
    case WM_LBUTTONDOWN:
        break;
    }
    return DefWindowProc(hwnd, message, wparam, lparam);
} 


I'm hoping we didn't throw too much at you, My best suggestion is to get Petzold's Programing Windows fifth edition, whether you are lost or not right now, it is the holy-grail of beginning to moderate level Windows API. I am sorry for everything I threw at you, and I didn't even get into the use of LPARAM and WPARAM, I just wanted to give you a wide range of things that you can now look up on http://msdn.microsoft.com/en-us/library/windows/desktop/ff818516%28v=vs.85%29.aspx

Last edited on
Topic archived. No new replies allowed.