Requirements
- Some ability with Windows. This article does not micromanage every last tiny step of the process.
- Some knowledge of C++ is required. Teaching C++ is outside the scope of this article.
Software Download
This seems like a reasonable place to start. Go to SourceForge and search for mingw. Probably the top result will be MinGW itself.
The reason for going via search is so that (a) you can see what sort of other stuff is available; (b) so that this document does not go out of date when SF reorganises itself. The scope of this article is just to get you started, so it won't touch on anything else.
After entering the MinGW page, go to Files. There's a lot of stuff that could be downloaded, but fortunately one of the downloads is a package of related utilities. At the time of writing the most recent is MinGW-3.1.0-1. This contains the following packages:
- GCC-3.2.3-20030504-1.tar.gz
- binutils-2.13.90-20030111-1
- mingw-runtime-3.1
- w32api-2.4
- gdb-5.2.1-1
- mingw32-make-3.80.0-3
- mingw-utils-0.2.tar.gz
So we'll download this. Click the SourceForge Download MinGW-3.1.0-1.exe link, and proceed as normal through the SourceForge download process. If your browser gives you the option of running the executable, this should be ok.
At this stage I doubt that package changes will affect the contents of this article, so if there is a more recent package you'll probably be ok to download that instead. I'll also assume that we won't need to upgrade any of the packages.
Installation
Run MinGW-3.1.0-1.exe from wherever you saved it. I'll be installing in D:\MinGW.
You could associate C++ files with d:\mingw\bin\gcc if you like, but I'll be working mainly within the command line.
You won't want to clutter the bin directory with programs, so add d:\mingw\bin to your PATH. Go to Start -> Settings -> Control Panel -> System -> Advanced -> Environment Variables, then either update the system path or add/modify the user path.
Then open a new DOS box and type "gcc" (not from the MinGW bin directory). You should get the message "gcc: no input files". This shows the installation has been successful.
Your First MinGW Program
For me, DOS boxes are opened in the directory c:\documents and settings\dave. This is as good a place as anywhere. Create a new file with the command notepad hello_dos.cpp.
Then enter:
#include <stdio.h>
int main()
{
printf("Hello, world\n");
return 0;
}
and save the file. Build it with g++ hello_dos.cpp -o hello_dos
and run it; you should get the output: "Hello, world"
Your first MinGW Windows program
Petzold's introductory Hello Windows program was a bit long at 84 lines, although the program existed for a different reason to this one. This one will be considerably simpler.
Create hello_win.cpp with Notepad as above and enter the following:
#include <windows.h>
int WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
MessageBox(0,"Hello, Windows","MinGW Test Program",MB_OK);
return 0;
}
Compile with g++ -mwindows hello_win.cpp -o hello_win
Then run it, and you should get a message box in the centre of the screen. This can also be run from Explorer. Without the -mwindows option, when run from Explorer you would also get a nasty DOS box appear for the duration of the program. However this might be a useful place to send debugging information; if you also include stdio.h you can use printf to display on this window.
For the WinMain prototype, go to MSDN and search for winmain. There are downloadable WinAPI references available elsewhere but I won't touch on them here.
Now for a REAL Windows program
There's quite a lot to a Windows program - creating the window class, the window procedure, not to mention resources and DLLs. I plan to pull this program apart in a future article. So here's a simple Windows program that displays a window.
#include <windows.h>
char *AppTitle="Win1";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
int WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int nCmdShow)
{
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style=CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc=WindowProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInst;
wc.hIcon=LoadIcon(NULL,IDI_WINLOGO);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_WINDOWFRAME;
wc.lpszMenuName=NULL;
wc.lpszClassName=AppTitle;
if (!RegisterClass(&wc))
return 0;
hwnd = CreateWindow(AppTitle,AppTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,100,100,
NULL,NULL,hInst,NULL);
if (!hwnd)
return 0;
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg,NULL,0,0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc;
RECT r;
GetClientRect(hwnd,&r);
dc=BeginPaint(hwnd,&ps);
DrawText(dc,"Hello World",-1,&r,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}
This can also be downloaded from http://www.osix.net:80/modules/folder/index.php?tid=6126&action=vf.
WinMain is the entry point to the program. This function creates and registers the window class which stores information relevant to the application window - the icon, menu, window procedure and so on. Then it creates the window and enters into the application message loop. The GetMessage function returns zero when the application receives a WM_QUIT message.
WindowProc is the function that receives messages for the window and can be confusing in terms of what it should return, so here it is in very simple terms:
- if you process a message, you must return 0.
- if you don't process a message, you must return the result of DefWindowProc.
If you look at other programs you'll see window procedures in slightly different formats. Some will end a case block with a return 0 instead of a break, often also calling DefWindowProc at the end (outside the switch) and not in a default: clause. Whichever way you choose, make sure the code follows the above two rules and you should be ok. None is really more correct than any others, although my choice uses the standard switch structure (case/break not case/return), and associates the default action with the default: clause.
If the application should close when the main window is closed, which is common although not always the case, you should call PostQuitMessage in response to WM_DESTROY which is the last message sent to a window. If you don't, the application main window will close, but the application will still be active and the only way to kill it will be with Task Manager or equivalent.
Drawing on a window isn't necessarily the most intuitive process. All drawing operations operate on a device context (HDC), not a window handle. BeginPaint is one way to get a device context; the main benefit of BeginPaint over GetDC is that BeginPaint's device context is already clipped to the area of window that needs redrawing (specified in ps.rcPaint), thus providing a considerable speedup for applications whose redraw operations are long and complicated. BeginPaint should only be called in response to WM_PAINT; GetDC can be called at any time.
One advantage to using device contexts is that everything that can be drawn on - windows, memory bitmaps, printers etc - has an associated device context, so it is possible for exactly the same code to be used for drawing on the screen and printing. Further, if you decide to change the drawing to use double buffering to eliminate flicker, none of the sometimes complicated redraw code needs changing, only where it is invoked.
References
|