How to make a keyboard input button?

Hello. Does anyone here know how to make a small little window with a button
that gets keyboard input?

like in html:
 
<button>Keyboard</button>


Something maybe like:
[Enter your name] <--- button(confirms input) [ ] <--- text box for
input

Thanks in advance.

Romans 10:9!
Last edited on
Can anyone tell me how to make a small window with a button on it in c++?
Hi again :)

You have a few options. There are cross-platform GUI libraries that you can look up.
To name a few, there are: wxWidgets, GTK+, Qt, and others.
You can also use platform-specific libraries, such as Win32 (Window controls), or MFC on Windows.

But I'll you right now: Creating simple GUI apps is a lot easier in other languages like C# [WinForms/WPF] than it is in C++. (There are ways to combine C# to do front-end work, and use C++ to use back-end work.)

I've used wxWidgets for a few hobby programs. I like it. Here is an example they have, but I would read their other tutorials for beginners and check out their forum:
https://wiki.wxwidgets.org/Writing_Your_First_Application-Adding_A_Button
https://www.wxwidgets.org/

Here is an example using just Windows controls (it will display "Hello World" in a pop-up when you click on the button.)
With g++, you can compile with:
g++ -Wall main.cpp -lgdi32 -o main
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
#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h> 
 
// https://docs.microsoft.com/en-us/cpp/mfc/tn020-id-naming-and-numbering-conventions?redirectedfrom=MSDN&view=vs-2019
const int Id_MyButton = 9;

// Function prototypes. 
 
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int); 
BOOL InitApplication(HINSTANCE); 
BOOL InitInstance(HINSTANCE, int); 
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); 

// Application entry point. 
 
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG msg; 
 
    if (!InitApplication(hinstance))
        return FALSE; 
 
    if (!InitInstance(hinstance, nCmdShow))
        return FALSE; 
 
    BOOL ret;
    while( (ret = GetMessage( &msg, NULL, 0, 0 )) != 0)
    { 
        if (ret == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    } 
    return msg.wParam; 
        UNREFERENCED_PARAMETER(lpCmdLine); 
} 
 
BOOL InitApplication(HINSTANCE hinstance) 
{ 
    WNDCLASSEX wcx; 
 
    // Fill in the window class structure with parameters 
    // that describe the main window. 
 
    wcx.cbSize = sizeof(wcx);          // size of structure 
    wcx.style = CS_HREDRAW | 
        CS_VREDRAW;                    // redraw if size changes 
    wcx.lpfnWndProc = WindowProc;     // points to window procedure 
    wcx.cbClsExtra = 0;                // no extra class memory 
    wcx.cbWndExtra = 0;                // no extra window memory 
    wcx.hInstance = hinstance;         // handle to instance 
    wcx.hIcon = LoadIcon(NULL, 
        IDI_APPLICATION);              // predefined app. icon 
    wcx.hCursor = LoadCursor(NULL, 
        IDC_ARROW);                    // predefined arrow 
	
	wcx.hbrBackground = static_cast<HBRUSH>(
	    GetStockObject(WHITE_BRUSH));  // white background brush 
    wcx.lpszMenuName =  L"MainMenu";    // name of menu resource 
    wcx.lpszClassName = L"MainWClass";  // name of window class 
    wcx.hIconSm = static_cast<HICON>(
	    LoadImage(hinstance, // small class icon 
            MAKEINTRESOURCE(5),
            IMAGE_ICON, 
            GetSystemMetrics(SM_CXSMICON), 
            GetSystemMetrics(SM_CYSMICON), 
            LR_DEFAULTCOLOR));
 
    // Register the window class. 
 
    return RegisterClassEx(&wcx); 
} 
 
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow) 
{ 
    HWND hwnd; 
 
    // Create the main window. 
    hwnd = CreateWindow( 
        L"MainWClass",       // name of window class 
        L"Sample",           // title-bar string 
        WS_OVERLAPPEDWINDOW, // top-level window 
        CW_USEDEFAULT,       // default horizontal position 
        CW_USEDEFAULT,       // default vertical position 
        100 + 35,            // width 
        100 + 60,            // height 
        (HWND) NULL,         // no owner window 
        (HMENU) NULL,        // use class menu 
        hinstance,           // handle to application instance 
        (LPVOID) NULL);      // no window-creation data 
 
    if (!hwnd) 
        return FALSE; 
	

    // https://docs.microsoft.com/en-us/windows/win32/controls/create-a-button
    //HWND hwndButton =
	CreateWindow( 
        L"BUTTON",  // Predefined class; Unicode assumed 
        L"OK",      // Button text 
        WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
        10,         // x position 
        10,         // y position 
        100,        // Button width
        100,        // Button height
        hwnd,       // Parent window
        (HMENU)Id_MyButton,  // Unique identifier for the button
        (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), 
        NULL);      // Pointer not needed.
 
    // Show the window and send a WM_PAINT message to the window 
    // procedure. 
 
    ShowWindow(hwnd, nCmdShow); 
    UpdateWindow(hwnd); 
    return TRUE; 
}

// https://docs.microsoft.com/en-us/windows/win32/learnwin32/writing-the-window-procedure
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CLOSE:
        {
            PostQuitMessage(0);
        }
		
	case WM_COMMAND: //Command from Child windows and menus are under this message
            switch(wParam) //the ID is is wParam
            {
                case Id_MyButton: //check for our button ID
                {
                    MessageBox(hwnd, L"Hello, world!", L"My Title", MB_OK);
                    break;
                }
            }
    }
	
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


TextBoxes are another type of control.
https://docs.microsoft.com/en-us/windows/win32/uxguide/ctrl-text-boxes
https://docs.microsoft.com/en-us/windows/win32/controls/use-a-single-line--edit-control

I would suggest looking into wxWidgets.
Last edited on

Does anyone here know how to make a small little window with a button
that gets keyboard input?


Sure, everybody here knows how to do that, and now you do :) ....

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
// cl Form2.cpp /O1 /Os /link Kernel32.lib User32.lib 
#include <windows.h>
#include <tchar.h>
#define  IDC_BUTTON 1500
#define  IDC_EDIT   1505


LRESULT fnWndProc_OnCreate(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 HINSTANCE hIns = NULL;
  
 hIns=((LPCREATESTRUCT)lParam)->hInstance;
 CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,50,40,200,25,hwnd,(HMENU)IDC_EDIT,hIns,0);
 CreateWindowEx(0,_T("button"),_T("Retrieve Text"),WS_CHILD|WS_VISIBLE,95,90,120,30,hwnd,(HMENU)IDC_BUTTON,hIns,0);

 return 0; 
}


LRESULT fnWndProc_OnCommand(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 TCHAR szBuffer[256];
 
 if(LOWORD(wParam)==IDC_BUTTON && HIWORD(wParam)==BN_CLICKED)
 {
    GetWindowText(GetDlgItem(hwnd,IDC_EDIT),szBuffer,256);	 
    MessageBox(hwnd,szBuffer,_T("Button Click"),MB_OK);
 }
 
 return 0;	
}


LRESULT fnWndProc_OnDestroy(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 PostQuitMessage(0);    
 return 0;   	
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
     return fnWndProc_OnCreate(hwnd, msg, wParam, lParam);
   case WM_COMMAND:
     return fnWndProc_OnCommand(hwnd, msg, wParam, lParam);
   case WM_DESTROY:
     return fnWndProc_OnDestroy(hwnd, msg, wParam, lParam);
 } 

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[] =_T("Form2"); 
 HWND hWnd = NULL;                       
 WNDCLASSEX wc;                   
 MSG messages;                    
 
 memset(&wc,0,sizeof(wc));                     
 wc.lpszClassName = szClassName;               
 wc.lpfnWndProc   = fnWndProc;                 
 wc.cbSize        = sizeof(WNDCLASSEX);        
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;   
 wc.hInstance     = hInstance;                 
 RegisterClassEx(&wc);                         
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,200,HWND_DESKTOP,0,hInstance,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))   
 {                                       
    TranslateMessage(&messages);         
    DispatchMessage(&messages);          
 }                                       
                                         
 return messages.wParam;
}
Last edited on
Um... Sorry but I'm fairly new to c++, finished reading one c++ book, and I have a question:


Is every line in freddie1's code absolutely necessary? I have no idea what anything does
besides the switches and other c++ keywords... (Except a few I never learned about).

Please add some notes or something, or a easier code??? I don't know.
If you're new to programming and C++, GUI programming can be overwhelming. Creating a GUI window and adding a few controls to it does require a lot of boilerplate code and ancient-looking incantations to have the minimum setup required (if we're just using Win32, that is).

That's kind of why I stressed using a friendlier GUI library, such as WinForms [C#] or wxWidgets. You can get more stuff done in less lines of code, because the harder stuff is usually hidden from you. Downloading/building a library is still a rather complicated task, but there are tutorials you can search for. (If you went with WinForms and C#, everything would mostly just work out of the box with Visual Studio.)

In a GUI application on Windows, you start in WinMain. In here, you have to set up what's called a "window class" (WNDCLASSEX), and you then use that window class to create the main window of your application (CreateWindowEx).

A GUI application doesn't immediately end like a console application; it loops around a message loop (lines 73-77). Each time an event happens, such as a mouse movement or resizing the window, an event is sent to WndProc function (fnWndProc in freddie's code).

For example, clicking on a button triggers a WM_COMMAND message, and inside the handler fnWndProc_OnCommand, we check if the event is currently acting on the button that exists (unique ID of this button is IDC_BUTTON 1500).

If you look at my code, I have several comments and links to Microsoft documentation pages you can look up. (But I'm not saying it's easy.)

________________________________________________

If you have any interest in making games for fun, I would check out a library called SFML; it is a multimedia library that has plenty of tutorials and is fairly easy for beginners to get into, although it's not specifically a GUI library.
https://www.sfml-dev.org/
Last edited on
I saw a graphics application made in visual basic with buttons that wrote to a file, etc.

Is there a way to include that and c++?

Probably a dumb question.


As to what I am trying to make, I just wanna test stuff such as Win32 graphics.
Hello Alexander. In the past I've written and posted a lot of Win32 tutorials, and here is one you might examine - as well as Ganado's posted links....

http://www.jose.it-berater.org/smfforum/index.php?topic=3389.0

Win32 coding started with Windows back around 1986. Charles Petzold is the renouned author who basically taught the world how it was done. In his various books he often stated that it would take an experienced, professional C coder six months to begin to become proficient in Win32 coding using the Windows Api directly, as I and Ganado have done in our examples. I'm sorry you found my code difficult. It doesn't seem that way to me, but I've been doing this stuff like forever. I started coding keypunching cards for a Fortran mainframe in the 1970s. That 1st example I posted that was so short was from an old tutorial I wrote sometime or other. I left out the tutorial part in what I posted. I thought maybe you were more advanced in all this and could get by without it. I think it'll fit in the 8192 byte limit here, so I'll try to post the whole little mini-tutorial. But actually, if one were tasked to explain the totality of Win32 'a la Petzold' style coding in five sentences or less, Ganado did a pretty good job of it!
Form1

To create an API (Application Programming Interface) based Windows program using Microsoft's
Software Development Kit (SDK), one must first fill out several of the more critical members
of a WNDCLASS or WNDCLASSEX struct ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   struct WNDCLASSEX
   {
     UINT    cbSize;
     UINT    style;
     WNDPROC lpfnWndProc;
     int     cbClsExtra;
     int     cbWndExtra;
     HANDLE  hInstance;
     HICON   hIcon;
     HCURSOR hCursor;
     HBRUSH  hbrBackground;
     LPCTSTR lpszMenuName;
     LPCTSTR lpszClassName;
     HICON   hIconSm;
   };


...and then pass this information to Windows by calling RegisterClass() or
RegisterClassEx()...

https://msdn.microsoft.com/en-us/library/windows/desktop/ms633587%28v=vs.85%29.aspx

The three most important members of WNDCLASSEX that must be filled out properly and without
which an application window can't be created are ...

1
2
3
   WNDCLASSEX::lpszClassName   // The null terminated textural name of the class, here "Form1"
   WNDCLASSEX::fnWndProc       // The address of the Window Procedure
   WNDCLASSEX::cbSize          // The size of the WNDCLASSEX object 


While the program can compile and run without filling out the WNDCLASSEX::hInstance member,
I'd highly recommend that be filled out, because it could put you in a situation of
undefined behavior. Anyway, its provided for you as the 1st parameter of WinMain(), so
there's no reason not to use it.

After filling out these members of a WNDCLASSEX struct and calling RegisterClassEx() on that
object, you can make a call to CreateWindow() or CreateWindowEx() to instantiate an
instance of the Registered Class. Note that the 1st parameter of CreateWindow() and the 2nd
parameter of CreateWindowEx() is a pointer to the textural null terminated C String to which
WNDCLASSEX::lpszClassName points - here "Form1". The call to CreateWindow() or
CreateWindowEx() represents a C based constructor call for an object.

After calling CreateWindow() the remainder of the code in WinMain() must fall into a message
loop - sometimes called a 'message pump,' which retrieves messages Windows the Operating
System places in the program's 'message queue.' This is a memory structure Windows creates
for all programs where it places messages informing the program of user interactions with
the program such as keyboard or mouse input, as well as system wide notifcations. The
GetMessage() function retrieves the next message from the queue, and the DispatchMessage()
function within the message pump causes Windows to dispatch (call) the Registered Window
Procedure associated with the Window Handle of the message.

The final piece of the puzzle is the Window Procedure itself, which is likely the most
important concept in Windows Programming. Note that the WNDCLASSEX::lpfnWndProc member is
typed as WNDPROC. What that is is a function pointer like so...

typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

In x86 LRESULT is a typedef for a LONG and hence is 32 bits, while in x64 LRESULT is a 64
bit entity. The CALLBACK symbol is a typedef of __stdcall. So if the following looks
better to you, you could think of it as this, at least in x86 ...

long (__stdcall* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

In other words, the WNDCLASSEX::lpfnWndProc entity requires a pointer to a function that
returns a long, uses standard call stack protocol, and has HWND, UINT, WPARAM, and LPARAM
parameters. Don't let these confuse you. Their usage will become clear, and I'll cover them
in Form2 and Form3. So just accept them for now, awaiting further clarification. Note that
our fnWndProc() function just below fullfills our specifications for a Window Procedure, and
the address of that Window Procedure is assigned to WNDCLASSEX::fnWndProc in WinMain() as
follows ...

wc.lpfnWndProc = fnWndProc;

Note that our fnWndProc() just below only does two things. First, it tests the msg
parameter of type unsigned int to see if it equals WM_DESTROY. You can find WM_DESTROY
defined in Windows.h as follows ...

#define WM_DESTROY 0x0002

So its equal to 2 (its actually in WinUser.h - #included by Windows.h). Windows sends a
WM_DESTROY message when you click the X in the Title Bar to close the program. In that case
the app calls PostQuitMessage(), which causes the message pump in WinMain() to fall through
and end. Then zero is returned in fnWndProc notifying Windows that the message has been
handled, and to take no further action on it. The second thing the program's Window
Procedure does is pass every other message received in the Window Procedure not equal to
WM_DESTROY to DefWindowProc() for default processing. In other words, you are telling
Windows that you aren't interested in that message and are taking no action on it, but that
the Operating System can do whatever it must with it to make itself happy. In general, when
you handle a message by writing code, you make an early exit by returning zero. Some
messages do require other entities to be returned though. Run the code below to make sure
everything is working for you.

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
#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)       // This is the Window Procedure.  The concept of the
 {                         // Window Procedure is the most important concept to
    PostQuitMessage(0);    // grasp in C/C++ WinApi coding.  You never call this
    return 0;              // function with your code; rather, Windows calls it
 }                         // to inform code here of events occurring.  The events
                           // are reported here as messages.
 return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form1"); // This will be both the name of the app and the name of
 WNDCLASSEX wc;                   // the main window's 'Window Class'.  The concept of the
 MSG messages;                    // Window Class and its associated Window Procedure are
 HWND hWnd;                       // the most important concepts in Windows Programming.

 memset(&wc,0,sizeof(wc));                     // zero out WNDCLASSEX wc
 wc.lpszClassName = szClassName;               // Feed "Form1" into WNDCLASSEX::lpszClassName
 wc.lpfnWndProc   = fnWndProc;                 // Feed pointer ( function address ) to Window Procedure
 wc.cbSize        = sizeof(WNDCLASSEX);        // Set Size
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;   // Set Background HBRUSH (Handle To A BRUSH)
 wc.hInstance     = hInstance;                 // Set HANDLE To Instance (its a virtual process memory thing)
 RegisterClassEx(&wc);                         // Let Operating System know about "Form1" Class
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,300,HWND_DESKTOP,0,hInstance,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))   // The message pump retrieves messages from the program's
 {                                       // message queue with GetMessage(), does some translation
    TranslateMessage(&messages);         // work in terms of character messages, then calls the
    DispatchMessage(&messages);          // Window Procedure associated with the HWND of the message
 }                                       // being processed.  Note that an app can have many Window
                                         // Procedures.
 return messages.wParam;
}
I'd like to add something to what Ganado said when he talked about the various options for creating desktop Windows applications. I also discuss the issue somewhat in that link I provided for you....

http://www.jose.it-berater.org/smfforum/index.php?topic=3389.0

Personally, I've done just about them all. In the 90s I experimented with Borland's Object Windows Library (OWL) and Microsoft's Microsoft Foundation Classes (MFC). At that time I was also huge into Microsoft's Visual Basic versions 1 through 6 circa 1991 - 1998. Later I spent a year with both Petzold's .NET books - Visual Basic.NET and C#.

Essentially I gave up on them all and reverted to my first love in coding which is Win32 Petzold style coding. The reason I did that was because of something I alluded to earlier - I'm a 'minimalist'. I want the smallest code possible with the most minimal code dependencies. While it is true that the code you are having a difficult time understanding seems to take a lot of mysterious lines of code to put up a blank window with nothing in it, using class frameworks doesn't eliminate lines of code - it just shifts the code to a combination of header files and libraries, and in the process multiplies the actual code needed by the thousands and creates sometimes (many times) troubling code dependencies. Nontheless, as much as I hate to admit it, this technique 'is right' for most people, and likely for you too. But I'm a 'purest' and stick to Win32 code as I've posted. I'll add comments to Form2 for you and post it in a bit. But, if, as you say, you are a real beginner at C++, expect that this is going to take some time to learn. Let me just mention one issue before I end this.

The C based Win32 API (Application Programming Interface) is an object oriented framework in - guess what - C - not C++. C supports object oriented programming but it can be awkward. C++ removes a lot of the casting difficulties with C object oriented programming, but nontheless when doing Win32 coding one is frequently faced with something termed 'function pointer' notation when making object method calls. Function pointers are more of a 'C' thing than a C++ thing, but you really have to learn to master function pointers if you want to become proficient in Win32 coding or even understand the documentation. I've probably just scared you off!
Thanks everyone but it all looks so hard... maybe I'll wait 'til I have more time to learn
how to do it right. Thanks!!
I guess Alexander abandoned his study of C++ Win32 coding partly because I scarred him of its difficulty. He mentioned he didn't understand my code. I said I'd comment it some. I'll post it anyway. Maybe it'll do somebody some good. Maybe Alexander will return when he has time. So here is my previously posted Form2 app made into a mini-tutorial....

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
/*
   Form2.cpp
   
   In Form1.cpp we registered a Window Class in WinMain() with the RegisterClassEx() Windows Api
   function, which takes a single argument of a WNDCLASSEX object.  Immediately afterward we 
   instantiated an object of the Class with the CreateWindowEx() function, and that function call
   created our main window.  In Form2.cpp we'll do the same to create our main application window, 
   but we'll make additional CreateWindowEx() calls to illustrate the creation of 'child window 
   controls'.  Specifically, we'll create an "edit" control and a "button" control as childs of
   our main program window.  The user can enter text in the edit control, or not, and when the
   button is pressed, we'll retrieve the text, if any, from the edit control, and display it in a 
   Message Box. 

   Two things.  First, note I have a #Debug preprocessor directive in the code.  If #Debug isn't 
   commented out, an Output.txt log file is created in the app's directory.  Here is that output 
   from a program run...   

   Entering WinMain()
     Right Before CreateWindow() Call In WinMain() - << Text From WinMain()...

     Entering fnWndProc_OnCreate()
       hWnd = 0x00000000000904E6
     Leaving fnWndProc_OnCreate()

     hWnd = 0x00000000000904E6 - << Text From WinMain()
     Right After CreateWindow() Call In WinMain() - << Text From WinMain()

     Entering fnWndProc_OnCommand()
       LOWORD(wParam) = 1500
     Leaving fnWndProc_OnCreate()

     Entering fnWndProc_OnClose()
       hWnd = 0x00000000000904E6
       Entering fnWndProc_OnDestroy()
         hWnd = 0x00000000000904E6
       Leaving fnWndProc_OnDestroy()
     Leaving fnWndProc_OnClose()
   Leaving WinMain()

  Note carefully that at the point of the CreateWindowEx() call in WinMain() to create the app's
  main program window, we get the debug output that code execution is in fnWndProc_OnCreate().  
  That function call is actually occurring 'inside' the CreateWindowEx() call!  What you are seeing 
  here is a C based object Constructor call.  In fnWndProc_OnCreate() we get the main app's HWND
  before it is revealed to us by the #Debug output statement in WinMain().  With this logic we see
  the symmetric C based object Destructor call in fnWndProc_OnDestroy().
  
  Second, you are seeing a truely elegant code design here in terms of the Windows Api.  One 
  function - CreateWindowEx(), is used to create every type of window Windows supports.  In 
  WinMain() we Register a 'custom' Window Class named "Form2" with which we base our main program 
  window.  We feed that name into the CreateWindowEx() call in its second parameter - szClassName.
  In fnWndProc_OnCreate() we see two more CreateWindowEx() calls, but neither of those calls 
  contain in the second parameter a Class Name we Registered in the app.  The 1st CreateWindowEx()
  call contains in its 2nd parameter the string "edit".  The 2nd CreateWindowEx() call asks for
  the instantiation of an object of class "button".  We never Registered an "edit" or "button"
  object in the "Form2" app.  Think about it.  What's going on here?

  What's going on here is that Windows the operating system has predefined classes of specialized
  'window' objects that can be instantiated in application programs by CreateWindow() or 
  CreateWindowEx() function calls.  There are several categories of these objects or 'controls'.
  Edit Controls and button controls are in the category known as the 'Standard Controls'.  Then
  there are the 'Common Controls'.  These are a bit more complex to use and they are quite 
  sophisticated.  Then there are ActiveX Controls.  With those the CreateWindow() calls are actually
  encapsulated within the control and the user creates them indirectly through the COM (Component 
  Object Model) subsystem.  Finally, one can create controls oneself and these would be 'Custom 
  Controls' - a somewhat advanced topic, as are ActiveX Controls. But the truly elegant part of 
  this code design of the Windows Api is that one function call - CreateWindow(), is used to create
  all these very different types of windows.  Below is Form2.cpp....   
*/

Last edited on
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
// cl Form2.cpp /O1 /Os /link Kernel32.lib User32.lib                       // 91,648 Bytes
// g++ Form2.cpp -luser32 -lkernel32 -oForm2_gcc.exe -mwindows -m64 -s -Os  // 17,920 Bytes
// cl Form2.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib        //  4,608 Bytes
//#define TCLib
#define Debug
#include <windows.h>
#ifdef TCLib
  #include "stdio.h"
  #include "tchar.h"
#else
  #include <cstdio>
  #include <tchar.h>
#endif
#define  IDC_BUTTON 1500
#define  IDC_EDIT   1505
#ifdef Debug
  FILE* fp=NULL;
#endif

LRESULT CALLBACK fnWndProc_OnCreate(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 HINSTANCE hIns = NULL;    // This is a C - based Constructor Function whose code execution will be occurring  
                           // 'inside' the CreateWindowEx() call down in WinMain().  Unlike a C++ language
 #ifdef Debug              // Constructor, failure is a possibility.  If -1 is returned by this function, the
 fprintf(fp,"  Entering fnWndProc_OnCreate()\n");  // CreateWindowEx() function in WinMain() will fail and return
 fprintf(fp,"    hWnd = 0x%p\n",hWnd);             // a NULL for its HWND.  Just below we'll make two CreateWindowEx() 
 #endif                                            // calls for two 'Child Window Controls'.  The code just at left 
 hIns=((LPCREATESTRUCT)lParam)->hInstance;         // retrieves the HINSTANCE from the CREATESTRUCT pointed to by LPARAM.
 CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,50,40,200,25,hWnd,(HMENU)IDC_EDIT,hIns,0);
 CreateWindowEx(0,_T("button"),_T("Retrieve Text"),WS_CHILD|WS_VISIBLE,95,90,120,30,hWnd,(HMENU)IDC_BUTTON,hIns,0);
 #ifdef Debug
 fprintf(fp,"  Leaving fnWndProc_OnCreate()\n\n");
 #endif 

 return 0; 
}


LRESULT CALLBACK fnWndProc_OnCommand(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 TCHAR szBuffer[256];                                          // Child Window Controls communicate with their parent
                                                               // by sending WM_COMMAND messages.  Packed tightly within
 if(LOWORD(wParam)==IDC_BUTTON && HIWORD(wParam)==BN_CLICKED)  // the WPARAM and LPAREM parameters is information the
 {                                                             // parent (our main app window) can use to determine which
    #ifdef Debug                                               // child window control is sending the message, and what
    fprintf(fp,"  Entering fnWndProc_OnCommand()\n");          // the nature of the user's interaction with the child
    fprintf(fp,"    LOWORD(wParam) = %u\n",LOWORD(wParam));    // window control was that triggered the sending of the
    #endif                                                     // message, e.g., the user clicked a button or typed a 
    GetWindowText(GetDlgItem(hWnd,IDC_EDIT),szBuffer,256);	    // character in an edit control.  Note that the GetDlgItem()
    MessageBox(hWnd,szBuffer,_T("Button Click"),MB_OK);        // function returns the HWND of a child window given the
    #ifdef Debug                                               // HWND of the parent and the Control ID of the child window
    fprintf(fp,"  Leaving fnWndProc_OnCreate()\n\n");          // control as specified in the HMENU parameter of the 
    #endif                                                     // CreateWindow() call that created the control.  Its never
 }                                                             // necessary to use global variables in Windows programs to
                                                               // persist HWNDs because Windows will always be able to
 return 0;	                                                    // return these to us through a GetDlgItem() function call.
}                                                              // Windows keeps track of all sorts of stuff like this.


LRESULT CALLBACK fnWndProc_OnClose(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 #ifdef Debug                                                  // The WM_CLOSE message provides the application the opportunity
 fprintf(fp,"  Entering fnWndProc_OnClose()\n");               // of querying the user for confirmation that the user indeed
 fprintf(fp,"    hWnd = 0x%p\n",hWnd);                         // wants to exit the application.  In this app all I did with the
 #endif                                                        // message was call DestroyWindow().  Note the Window Procedure
 DestroyWindow(hWnd);                                          // is 'reentrant'.  fnWndProc_OnDestroy() will execute within
 #ifdef Debug                                                  // fnWndProc_OnClose().  This is clearly seen in the Output.txt
 fprintf(fp,"  Leaving fnWndProc_OnClose()\n");                // log file if #Debug is active/defined, i.e., not commented out.
 #endif  
 
 return 0;   	
}


LRESULT CALLBACK fnWndProc_OnDestroy(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 #ifdef Debug                                                  // This is a C - Based Destructor Call for our main program
 fprintf(fp,"    Entering fnWndProc_OnDestroy()\n");           // window.  In more complex programs than this, it is a good
 fprintf(fp,"      hWnd = 0x%p\n",hWnd);                       // place to make sure memory allocations are released, databases
 #endif                                                        // or files are closed, etc.  Here, all we need to do is call
 PostQuitMessage(0);                                           // PostQuitMessage() to stop the 'message pump' in WinMain()
 #ifdef Debug                                                  // from running so that the program can end.
 fprintf(fp,"    Leaving fnWndProc_OnDestroy()\n");
 #endif  
 
 return 0;   	
}


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)                                                // I don't personally much care for placing a lot of code
 {                                                          // in 'case' constructs under a switch statement.  So at
   case WM_CREATE:                                          // left you see I created seperate 'message handling functions'
     return fnWndProc_OnCreate(hWnd, msg, wParam, lParam);  // to put actual code to be executed for each message to be
   case WM_COMMAND:                                         // handled.  The idea illustrated here is pretty simple; in  my
     return fnWndProc_OnCommand(hWnd, msg, wParam, lParam); // actual production code I don't use switch logic but rather
   case WM_CLOSE:
     return fnWndProc_OnClose(hWnd, msg, wParam, lParam);   
   case WM_DESTROY:                                         // for loop logic to iterate through a struct array containing
     return fnWndProc_OnDestroy(hWnd, msg, wParam, lParam); // the message and a function pointer to the message handling
 }                                                          // function that handles that specific message.  

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


continued....
Last edited on
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
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[] =_T("Form2");              // Program Entry Point for Windows GUI app.  I personally like to keep my
 HWND hWnd = NULL;                              // WinMain() functions short.  The only code I put in them is code to
 WNDCLASSEX wc;                                 // Register my main app window, the CreateWindowEx() call to create that window,
 MSG messages;                                  // and the 'Message Pump' that keeps the program alive and functioning.  If
                                                // the app instantiates multiple top level windows, I put the code to register
 #ifdef Debug                                   // those additional classes in the WM_CREATE handler of the main app's window.
 fp=fopen("Output.txt","w");                    // After all, that is the 'Constructor' for the entire app.  
 fprintf(fp,"Entering WinMain()\n");
 #endif
 memset(&wc,0,sizeof(wc));                      // Zero out WNDCLASSEX object                    
 wc.lpszClassName = szClassName;                // Start filling out WNDCLASSEX object....
 wc.lpfnWndProc   = fnWndProc;                  // Function Pointer to Window Procedure
 wc.cbSize        = sizeof(WNDCLASSEX);         // Size of WNDCLASSEX object
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;    // Color for background of main app window
 wc.hInstance     = hInstance;                  // Virtual Memory Address Thing
 RegisterClassEx(&wc);                          // Register Custom Window Class
 #ifdef Debug
 fprintf(fp,"  Right Before CreateWindow() Call In WinMain() - << Text From WinMain()...\n\n");
 #endif 
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,200,HWND_DESKTOP,0,hInstance,0);  // C Based Object Constructor
 #ifdef Debug
 fprintf(fp,"  hWnd = 0x%p - << Text From WinMain()\n",hWnd);
 fprintf(fp,"  Right After CreateWindow() Call In WinMain() - << Text From WinMain()\n\n"); 
 #endif
 ShowWindow(hWnd,iShow);                        // Show Window
 while(GetMessage(&messages,NULL,0,0))          // Message Pump
 {                                       
    TranslateMessage(&messages);         
    DispatchMessage(&messages);          
 }
 #ifdef Debug
 fprintf(fp,"Leaving WinMain()\n");
 fclose(fp);
 #endif 
                                         
 return messages.wParam;
}
About the above, I foresee this as being confusing in my fnWndProc_OnCommand()....

GetWindowText(GetDlgItem(hWnd,IDC_EDIT),szBuffer,256);

I believe the term for that is 'concision'. There is a function call within a function call. This is pretty basic stuff for experienced coders, but for beginners it may be too much. So let me explain it. First, there are two Windows Api function calls involved, GetWindowText() and GetDlgItem(). GetWindowText() takes three parameters...

1) The HWND of the object being queried for its text;
2) A pointer to a char/wchar_t buffer where the returned text from the object will be copied;
3) The count of chars/wchar_ts that will fit in the aforementioned buffer.

In terms of GetDlgItem(), it returns the HWND of a control/object given the control/object's parent and the Control ID of the control/object set in the HMENU parameter of the CreateWindow() call that created the control/object. That sounds awefull complicated. Its not. In fnWndProc_OnCreate() we created a textbox (edit control) like so....

CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,50,40,200,25,hWnd,(HMENU)IDC_EDIT,hIns,0);

The 10th parameter typed as the HMENU parameter was loaded like so....

(HMENU)IDC_EDIT

Near the top of the program you'll find....

#define IDC_EDIT 1505

So, note that the 1st parameter of GetWindowText() takes a HWND of the control whose text we want. GetDlgItem() returns that HWND given the edit control's parent, which we get through the parameter list of fnWndProc_OnCommand(). The 1st parameter of all my message handlers is the HWND of the parent or main window of the app. So feed that HWND into GetDlgItem(), as well as the Control ID (1505) of the edit control, and GetDlgItem() will return the HWND of the edit control - which GetWindowText() wants to know to retrieve text. If I weren't such a minimalist I could have coded....

TCHAR szBuffer[256];

HWND hEdit=GetDlgItem(hWnd,IDC_EDIT);
GetWindowText(hEdit,szBuffer256);

I just can't help myself that I coded it like I did which I know is confusing to beginners.

Somewhere in this thread of Alexander's he asked me about my command line compiling....

http://www.cplusplus.com/forum/windows/273083/

I never answered, although as mentioned a few posts above, my command line compiling tutorial was never published, as I guess the administrator here is busy with life or whatever. So I'll just say a few words about it here. Its actually easier than using any IDE I know of (especially Visual Studio nowadays).

When any version of Visual Studio of which I'm aware is installed, quite a few shortcuts for all kinds of things are put on the Start menu. Right now I'm writing this on an old Windows 7 machine with Visual Studio 2008 installed. On my Start menu is this (among other things).....

 
Microsoft Visual Studio 2008  >> Visual Studio Tools  >> Visual Studio 2008 x64 Win64 Command Prompt


Visual Studio 2015 and 2019 are possibly somewhat different (Microsoft likes to change things), but you ought to be able to figure it out.

Added Later:

Just checked. On my Win 10 box with VStudio 2019 its this for x64....

 
x64 Native Tools Command Prompt For VS 2019


So first thing either click on that selection to open the command prompt window, or even better, create a shortcut on your desktop to the file pointed to by that shortcut. I kind of never liked using the Start Menu. I like shortcuts on my desktop instead.

When the command prompt window opens it will be to some Visual Studio created directory/folder I don't ever use. If you want to use that directory its up to you, but I keep all my code in subdirectories under a C:\Code directory I create. For example, that Form2 app is in this directory on my various boxes....

C:\Code\VStudio\Forms\Form2

So where I'm headed, use a Change Directory command (CD\ or ChDir\) like so....

 
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>CD\


After doing that you are back to the C:\ Root directory....

 
C:\>


Then use the CD command again to change the folder to wherever you have my Form2 app's code. For me, like I said, its....

 
C:\Code\VStudio\Forms\Form2


So I'd do this....

 
C:\>CD C:\Code\VStudio\Forms\Form2


....which then gives me this....

 
C:\Code\VStudio\Forms\Form2>


To build the Form2.cpp file copy the correct command line string from the top of my app and paste it into the command prompt and hit [ENTER]. The only one you'll be able to use is this one because you don't have my TCLib.lib....

 
cl Form2.cpp /O1 /Os /link Kernel32.lib User32.lib


After I did that here is my command prompt window indicating a successful build....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

C:\>CD C:\Code\VStudio\Forms\Form2

C:\Code\VStudio\Forms\Form2>cl Form2.cpp /O1 /Os /link Kernel32.lib User32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Form2.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form2.exe
Kernel32.lib
User32.lib
Form2.obj

C:\Code\VStudio\Forms\Form2>


After I did that I checked my directory and found the 39,424 byte Form2.exe file that was created. Microsoft keeps bloating stuff as that file is from the compiler that shipped with Visual Studio 2008. With VStudio 2019 the binary is 91,648 bytes.

In any case, the main deal with command line building is understanding the compiler 'switches' you need to build your apps. Do a 'Cls' (Clear Screen) in the command line window, then do this to find out about all the compiler and linker switches....

 
C:\Code\VStudio\Forms\Form2>CL /?


Cl.exe is Microsoft's Compiler/linker. Actually, there is a seperate linker (Link.exe), but in most cases one can let cl chain to it. So the 'Help' is accessed with just the slash symbol '/' and a '?' symbol, after the cl.

I'll continue this some tomorrow.
Last edited on
Topic archived. No new replies allowed.