The Daily Click ::. Forums ::. Non-Klik Coding Help ::. Creating Windows in C/C++
 

Post Reply  Post Oekaki 
 

Posted By Message

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
24th April, 2004 at 01:59:44 -

It's quite easy to make a command-line application in C/C++, but what happens if you want to make something in an actual
window with buttons and other Win32 elements? Well, it's not quite as hard as it sounds.

Here I'll show you how to make a really basic application and try to explain what's going on as best as possible. Please note
that this is only available under Windows, UNIX/Linux/MAC/etc users will not be able to use the following code. If something
I say doesnt make sense, be sure to tell me and I'll try to explain in more detail.

// First you need to include the header file used in creating Win32 applications

#include <windows.h>

// When creating a window you need to define a "window class" which stores information about the window you will be creating.
// If you wish to create more than one window with the same (or similar) design, you only need to create the window class
// once, then use it as many times as you like for each window. You uniquely define your window using a basic character
// array (constant as it wont be changed during design/runtime).
const char className[] = "ShadowCasters Window";

// If you're new to C/C++ you'll probably have no idea what this line is. Basically when you create a function you need to
// first define it so that C/C++ knows that it exists and can be called during execution of your program. Doing this is
// called creating a "prototype". The line below creates a prototype for a function we will use to handle any events
// processed by our frame (for example, if the [X] button is clicked, and so on).
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// In C/C++ command-line applications you'll use int main(int, char**) for your main function (that is, where your program
// starts when you run it). When defining Win32 applications you create a different style function, as shown below. The first
// parameter is the handle to the instance of the application; the second is the handle to the previous instance of the
// application (usually NULL); the third is a string containing the command line arguments (note: unlike the command-line
// version of the main function, this does not split the commands for you, nor does it contain the program title; the final
// parameter is mainly used in shortcuts, and defines the initial state of the window (i.e. normal, maximized or minimized).
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Declaration of the object we will use to store the Extended Window Class
WNDCLASSEX wc;

// Before we can add any settings we need to set the size of all the information we will be storing so there is
// enough space reserved for it. This is quite simple to do using the C/C++ sizeof() function.
wc.cbSize = sizeof(WNDCLASSEX);

// The style attribute defines the behaviour of windows created in this class. You can use one or more of the
// following: CS_BYTEALIGNWINDOW, CS_BYTEALIGNCLIENT, CS_OWNDC, CS_CLASSDC, CS_PARENTDC, CS_DBLCLKS, CS_GLOBALCLASS,
// CS_HREDRAW, CS_VREDRAW, CS_NOCLOSE and CS_SAVEBITS. In this example we wont be explicitly setting any of these
// attributes.
wc.style = 0;

// Remember the function that was defined before? We're now using a reference to that to specify that we want it to
// run whenever a window in this class receives an event.
wc.lpfnWndProc = WndProc;

// If you want to allocate extra memory to this class, it can be specified here (usually always set to 0).
wc.cbClsExtra = 0;

// To allocate extra memory to each window in this class, specify it here (usually always set to 0).
wc.cbWndExtra = 0;

// The windows we create in this class will belong to this application, so we set the hInstance to that of the parent
// application.
wc.hInstance = hInstance;

// This sets the large 32x32px icon (in this case we are using the default application icon). If you dont wish to
// create your own, the existing icons you can use are: IDI_APPLICATION, IDI_ASTERISK (also IDI_INFORMATION),
// IDI_ERROR (also IDI_HAND), IDI_EXCLAMATION (also IDI_WARNING), IDI_QUESTION and IDI_WINLOGO. If you do, however,
// want to load your own, the first parameter must be the instance of the application containing the icon, and the
// second must be a reference to the resource itself.
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

// Here we set the default cursor to use when the mouse pointer is over the window. In most cases you'll want to use
// IDC_ARROW (which is the regular pointer) but you can also specify: IDC_APPSTARTING, IDC_CROSS, IDC_HAND, IDC_HELP,
// IDC_IBEAM, IDC_ICON, IDC_NO, IDC_SIZE, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_NWSE, IDC_SIZEWE, IDC_UPARROW
// and IDC_WAIT.
wc.hCursor = LoadCursor(NULL, IDC_ARROW);

// Next we set the background colour for the window. You can define your own by specifying NULL as the value here,
// then setting the colour manually when the WM_PAINT event is called (which is done during the WndProc function
// below). If you want to specify a system colour, you can use instead one of the following: COLOR_ACTIVEBORDER,
// COLOR_ACTIVECAPTION, COLOR_APPWORKSPACE, COLOR_BACKGROUND, COLOR_BTNFACE, COLOR_BTNSHADOW, COLOR_BTNTEXT,
// COLOR_CAPTIONTEXT, COLOR_GRAYTEXT, COLOR_HIGHLIGHT, COLOR_HIGHLIGHTTEXT, COLOR_INACTIVEBORDER,
// COLOR_INACTIVECAPTION, COLOR_INACTIVECAPTIONTEXT, COLOR_MENU, COLOR_MENUTEXT, COLOR_SCROLLBAR, COLOR_WINDOW,
// COLOR_WINDOWFRAME and COLOR_WINDOWTEXT. If you use one of the system colours you must remember to add 1 to the
// returned value as below (note also that you need to cast the value to a HBRUSH type).
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

// The system-menu associated with the windows in this class, generally set to NULL in most cases.
wc.lpszMenuName = NULL;

// Now we set the name of the window class, as we defined earlier.
wc.lpszClassName = className;

// Finally now we set the 16x16 icon which is displayed in the top-left of the window (you can use the same default
// icons as before).
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

// Now we have specified all the values, the class needs to be registered in order for us to use it. We simply use
// a pre-defined function and pass it a reference to the object we just created. In this line of code we are
// accomplishing two things at once: if the attempt to register the class fails, then it runs the code inside the
// block, otherwise it continues executing from after the block.
if(!RegisterClassEx(&wc))
{
// Since the class wasnt registered, we need to tell the user that something has gone wrong, and exit the
// application. This is quite easily done using the MessageBox function. The first parameter is the parent
// window, but since we dont have one, we simply leave this as NULL. Next is the message we want to display,
// followed by the heading. Finally we specify the icon to display, and the button configuration. The icon
// can be: MB_ICONHAND (also MB_ICONSTOP, MB_ICONERROR), MB_ICONQUESTION, MB_ICONEXCLAMATION (also
// MB_ICONWARNING) and MB_ICONASTERISK (also MB_ICONINFORMATION). The button configuration can be:
// MB_ABORTRETRYIGNORE, MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO and MB_YESNOCANCEL. You can also specify
// which of the buttons you wish to be the default using: MB_DEFBUTTON1, MB_DEFBUTTON2, MB_DEFBUTTON3 and
// MB_DEFBUTTON4. There are other options you can use, but are not relevant for the sake of this project.
MessageBox(NULL, "Failed to register the window class", "Error", MB_ICONEXCLAMATION | MB_OK);

// Since we got an error, we need to exit the program rather than try and continue. Note that a value of 1
// representes failure, while 0 represents success.
return 1;
}

// Now the class is created, we need to create the actual window; we start by creating a new handle.
HWND hwnd;

// The Win32 API has a function called CreateWindow (and CreateWindowEx) which will return a handle to a window, all
// we need to do is specify a few parameters (note: CreateWindowEx simply allows extended options). To create our
// window we'll use the extended parameter WS_EX_CLIENTEDGE which tells the system to create a sunken border. You can
// use the following extended parameters: WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_CLIENTEDGE, WS_EX_CONTEXTHELP,
// WS_EX_CONTROLPARENT, WS_EX_DLGMODALFRAME, WS_EX_LEFT, WS_EX_LEFTSCROLLBAR, WS_EX_LTRREADING, WS_EX_MDICHILD,
// WS_EX_NOPARENTNOTIFY, WS_EX_OVERLAPPEDWINDOW, WS_EX_PALETTEWINDOW, WS_EX_RIGHT, WS_EX_RIGHTSCROLLBAR,
// WS_EX_RTLREADING, WS_EX_STATICEDGE, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_EX_TRANSPARENT and WS_EX_WINDOWEDGE. Next
// we specify the name of the registered window class to use for our window (using the variable defined at the
// start). Then you enter the title of your window, followed by regular style attributes, including: WS_BORDER,
// WS_CAPTION, WS_CHILD, WS_CHILDWINDOW, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_DISABLED, WS_DLGFRAME, WS_GROUP,
// WS_HSCROLL, WS_ICONIC, WS_MAXIMIZE, WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPED,
// WS_OVERLAPPEDWINDOW, WS_POPUP, WS_POPUPWINDOW, WS_SIZEBOX, WS_SYSMENU, WS_TABSTOP, WS_THICKFRAME, WS_TILED,
// WS_TILEDWINDOW, WS_VISIBLE and WS_VSCROLL. Usually you will use WS_OVERLAPPEDWINDOW to create a standard window.
// The next four parameters specify the left, top, width and height of the window respectivley. Note the use of
// CW_USEDEFAULT to place it cascading against all other windows. The next specifies the parent window (NULL should
// be used if there is no parent), followed by the application menu (again NULL if none). Finally we specify the
// instance of the application and any further extended data.
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, className, "Dont Press The Button!", WS_SYSMENU, CW_USEDEFAULT,
CW_USEDEFAULT, 300, 150, NULL, NULL, hInstance, NULL);

// Now we check to make sure the window was created successfully.
if(hwnd == NULL)
{
// Another message box, similar to the previous example.
MessageBox(NULL, "Failed to create the window", "Error", MB_ICONEXCLAMATION | MB_OK);

// Exit again with an error value.
return 1;
}

// Creating just a window is pretty boring, so let's create a button to put on there as well; first create a handle.
HWND btn;

// Creating the button itself is is done in a similar way to creating the window, but we specify a pre-existing class
// called BUTTON. You can also use: COMBOBOX, EDIT, LISTBOX, MDICLIENT, RICHEDIT (for Rich Edit 1.0), RICHEDIT_CLASS
// (for Rich Edit 2.0), SCROLLBAR and STATIC (for plain text). Because putting every single option for every single
// object on here would take forever, I'm not going to mention any of the other settings you are able to use.
// Instead, you'll need to look those up in the header file for the windows class, or find out more on the internet.
btn = CreateWindow("BUTTON", "Dont Push Me", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
5, 5, 280, 105, hwnd, NULL, hInstance, NULL);

// Like always, we check to make sure the button was created successfully.
if(btn == NULL)
{
// And display a message if it isnt.
MessageBox(NULL, "Failed to create button", "Error", MB_ICONEXCLAMATION | MB_OK);

// Then quit the application with an error value.
return 1;
}

// Now our window and all its children are created, we need to display it. You can do this using the ShowWindow
// function where the first parameter is the handle to the window, and the second is the display state defined by
// a shortcut to the application (entered into the application as a parameter at the start). Note that this mainly
// takes the states: SW_SHOWNORMAL, SW_SHOWMAXIMIZED and SW_SHOWMINIMIZED.
ShowWindow(hwnd,nCmdShow);

// To make sure the window is displayed correctly, we do a quick refresh of all the elements.
UpdateWindow(hwnd);

// When the user interacts with the window, a message is added to the end of a queue. To read these messages we need
// to create a variable to store it in.
MSG msg;

// Because we want our window to display until the user wants to quit, we need to create a loop. The GetMessage
// function not only reads the next message in the queue, but it also returns TRUE for all messages except for
// WM_QUIT, which allows us to loop until a specific quit message is sent. The first parameter is a reference to the
// message variable we just created; the second is the instance of the window (specifying NULL returns all messages
// for any instance in this thread); the third and forth parameters specify the first and last message to receive.
while(GetMessage(&msg, NULL, 0, 0))
{
// Before we can do something with the message we need to convert it from its existing structure into a
// character message.
TranslateMessage(&msg);

// Now we send the message to the function we specified in the lpfnWndProc attribute of the window class
// object (in this case, the WndProc function).
DispatchMessage(&msg);
}

// Finally we return the value of the exit status of the application; this will usually be 0 (successful).
return msg.wParam;
}

// Now all that is left is to specify the function that will process the window events. The first parameter is the handle
// of the object that called the function (i.e. the window or the button), the second is a message related to what the user
// did, and the last two are any parameters that are associated with that action.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Since there are a few different messages we want to catch, we'll use a SWITCH statement instead of a bunch of ifs.
switch(msg)
{

// This case is executed if the user clicks the [X] button in the titlebar.
case WM_CLOSE:
// Send WM_DESTROY to the Current Window and all its children.
DestroyWindow(hwnd);
break;

// This case is executed when the current window is being destroyed.
case WM_DESTROY:
// Send the WM_QUIT message to end the main application loop (with a successful value of 0)
PostQuitMessage(0);
break;

// This case is executed when the button is clicked.
case WM_COMMAND:
// Display a message.
MessageBox(NULL, "OMG YOU PUSHED ME!", "OMG", MB_ICONINFORMATION | MB_OK);
break;

// If a message is received that is not of any of the above types then execute this block.
default:
// Call the default window prodecure to handle the message for you.
return DefWindowProc(hwnd,msg,wParam,lParam);
break;
}

// Return a successful value.
return 0;
}
Mike ¿

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
24th April, 2004 at 02:10:09 -

Note: That looks a lot harder than it actually is, cos 4 out of 5 lines are comments, so it looks a lot longer than if you were to have just the straight code

Mike

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert

Joe.H

Evil Faker

Registered
  19/08/2002
Points
  3305
24th April, 2004 at 05:41:01 -

maybe you could do it without all the comments as well then

 
My signature is never too big!!!

Cazra

Crazy?

Registered
  24/07/2002
Points
  4472

Game of the Week WinnerVIP Member
24th April, 2004 at 06:18:59 -

btn is undefined.

 
n/a

Klikmaster

Master of all things Klik

Registered
  08/07/2002
Points
  2599

Has Donated, Thank You!You've Been Circy'd!VIP MemberPS3 Owner
24th April, 2004 at 07:53:36 -

Stupid Shadow, you defined myButton, then used btn

Image Edited by the Author.

 
n/a

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
24th April, 2004 at 09:06:19 -

Whoops, I thought I updated that, I must have copied+pasted from the wrong file ^__^ Fixed now.

Here's just the code as Joe requested/suggested...

#include <windows.h>


const char className[] = "ShadowCasters Window";
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Failed to register the window class", "Error", MB_ICONEXCLAMATION | MB_OK);
return 1;
}

HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, className, "Dont Press The Button!", WS_SYSMENU, CW_USEDEFAULT,
CW_USEDEFAULT, 300, 150, NULL, NULL, hInstance, NULL);

if(hwnd == NULL)
{
MessageBox(NULL, "Failed to create the window", "Error", MB_ICONEXCLAMATION | MB_OK);
return 1;
}

HWND btn = CreateWindow("BUTTON", "Dont Push Me", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
5, 5, 280, 105, hwnd, NULL, hInstance, NULL);

if(btn == NULL)
{
MessageBox(NULL, "Failed to create button", "Error", MB_ICONEXCLAMATION | MB_OK);
return 1;
}

ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);

MSG msg;

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

return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
MessageBox(NULL, "OMG YOU PUSHED ME!", "OMG", MB_ICONINFORMATION | MB_OK);
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
break;
}

return 0;
}
Mike

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert

Cazra

Crazy?

Registered
  24/07/2002
Points
  4472

Game of the Week WinnerVIP Member
24th April, 2004 at 11:15:02 -

Arg! I wish Borland would stop all it's complaining! *Threatens Borland compiler that I'll replace it with Microsoft Visual C++ one day*

 
n/a

Klikmaster

Master of all things Klik

Registered
  08/07/2002
Points
  2599

Has Donated, Thank You!You've Been Circy'd!VIP MemberPS3 Owner
24th April, 2004 at 12:12:32 -

When I run the program with this code, after compiling in dev C++ a black window opens first, this meant to happen?
In dev c++ you can select to make a window, and it does the code for you, it looked almost the same as yours shadow, but it didnt seem to make the black window, maybe cos its a different compiler

 
n/a

Kris

Possibly Insane

Registered
  17/05/2002
Points
  2017
24th April, 2004 at 14:28:36 -

compiles and runs fine with me (MSVC 6), so it must be your compiler, unless you've changed any of the code?

Edit: Oh, I see what you've done. Sounds like you've compiled a Console application instead of Win32

Image Edited by the Author.

 
"Say you're hanging from a huge cliff at the top of mt. everest and a guy comes along and says he'll save you, and proceeds to throw religious pamphlets at you while simultaniously giving a sermon." - Dustin G

Klikmaster

Master of all things Klik

Registered
  08/07/2002
Points
  2599

Has Donated, Thank You!You've Been Circy'd!VIP MemberPS3 Owner
24th April, 2004 at 17:49:59 -

ah, that must be what i did, i just copied shadow's code into a *.cpp and compiled
but dev c++ comes with all that code if u select win32 application

 
n/a

Cazra

Crazy?

Registered
  24/07/2002
Points
  4472

Game of the Week WinnerVIP Member
24th April, 2004 at 19:00:54 -

I hate Borland....

 
n/a
   

Post Reply



 



Advertisement

Worth A Click