ProgEx44 -- Command Line Compiling with GNU g++ and using menu and dialog resources
from a resource script.
This code has been tested, compiles clean, and works as intended with Mingw 4.4 and 4.6
32 bit compilers, as well as the 4.7 series x64 bit compiler. In terms of Microsoft
compilers, it has been tested, compiles clean, and works as intended with VC 9 32 and
64 bit.
This example picks up where I left off with ProgEx43, but the code has been modified
to use a resource script for the menu and dialog box. This change will allow for the
demonstration of how to use the command line resource compiler with the GNU toolchain.
Otherwise, its the same program as ProgEx43.
When I installed CodeBlocks I accepted the default installation location of...
C:\Program Files (x86)\CodeBlocks
Therefore, the binaries in the compile tool chain were in...
C:\Program Files (x86)\CodeBlocks\MiGW\bin
And the library files were in...
C:\Program Files (x86)\CodeBlocks\MiGW\lib
Therefore, to set up a command line workspace for myself where my ProgEx44 project folder
was at this location...
C:\Code\CodeBlks\CPrimer\ProgEx44
...I made the following batch file (named ProgEx44.bat)...
CD\
CD C:\Code\CodeBlks\CPrimer\ProgEx44
PATH=C:\Program Files (x86)\CodeBlocks\MinGW\bin;C:\Program Files (x86)\CodeBlocks\MinGW\lib
C:\Windows\system32\cmd.exe
The 1st line changes the directory to the root. The 2nd line changes the current directory
to the ProgEx44 Project directory. The third line provides a tempory path to where the \bin
and \lib files of the Code::Blocks GCC installation toolchain is located. This is a temporary
setting that will last until the command prompt window is closed. The last line invokes the
Windows command processor for the Windows 7 machine which I'm writing this on now.
Note that to use the experimental x86 / x64 tdm64-gcc-4.7.1-3.exe series 4.7 GCC compiler
which installed for me in C:\MinGW64, I'd use this batch file if my project directory for that
was \ProgEx44\x64...
CD\
CD C:\Code\CodeBlks\CPrimer\ProgEx44\x64
C:\Windows\system32\cmd.exe
As I mentioned in ProgEx43's write up, this x64 compiler modified my system environment by
including itself in my environment PATH. So therefore, Windows will always look there in any
case and find the compiler toolchain.
This particular program has its menu and dialog box in a resource script rc file. That needs
to be compiled first using WindRes.exe which is in the compiler's \bin directory...
WindRes -i ProgEx44.rc -o ProgEx44.obj -v
WindRes.exe can make either a *.res or *.obj file. I chose *.obj. If you want to see the
command line switches for WindRes do this...
C:\Code\CodeBlks\CPrimer\ProgEx44>windres -h
Usage: windres [option(s)] [input-file] [output-file]
The options are:
-i --input=<file> Name input file
-o --output=<file> Name output file
-J --input-format=<format> Specify input format
-O --output-format=<format> Specify output format
-F --target=<target> Specify COFF target
--preprocessor=<program> Program to use to preprocess rc file
-I --include-dir=<dir> Include directory when preprocessing rc file
-D --define <sym>[=<val>] Define SYM when preprocessing rc file
-U --undefine <sym> Undefine SYM when preprocessing rc file
-v --verbose Verbose - tells you what it's doing
-c --codepage=<codepage> Specify default codepage
-l --language=<val> Set language when reading rc file
--use-temp-file Use a temporary file instead of popen to read
the preprocessor output
--no-use-temp-file Use popen (default)
-r Ignored for compatibility with rc
@<file> Read options from <file>
-h --help Print this help message
-V --version Print version information
FORMAT is one of rc, res, or coff, and is deduced from the file name
extension if not specified. A single file name is an input file.
No input-file is stdin, default rc. No output-file is stdout, default rc.
windres: supported targets: pe-i386 pei-i386 elf32-i386 elf32-little elf32-big srec symbolsrec tekhex binary ihex
Report bugs to <http://www.sourceware.org/bugzilla/>
C:\Code\CodeBlks\CPrimer\ProgEx44>
After creation of the ProgEx44.obj file you can invoke g++ which is the GNU C++ compiler. This
will compile Main.cpp into Main.obj and link Main.obj with ProgEx44.obj to produce ProgEx44.exe.
Here is that command line for the Code::Blocks installed GNU compiler version 4.6, which makes a
32 bit executable...
C:\Code\CodeBlks\CPrimer\ProgEx44>g++ -Wall Main.cpp ProgEx44.obj -o ProgEx44_x32.exe -mwindows -Os -s
For the experimental x64 bit 4.7 Series compiler where I have my files in \ProgEx44\x64 and the compiler
toolchain has been installed to my environment PATH ...
C:\Code\CodeBlks\CPrimer\ProgEx44\x64>WindRes -i ProgEx44.rc -o ProgEx44.obj -v
C:\Code\CodeBlks\CPrimer\ProgEx44\x64>g++ -Wall Main.cpp ProgEx44.obj -o ProgEx44_x64.exe -mwindows -Os -s
Note I used the -Os switch to optimize for small size, and the -s switch to remove debug symbols.
Here is the 64 bit command line session showing the output from both operations and verifying with
dir after each to see that the sought after file was indeed produced...
-- Begin Command Line Output --
1 C:\Code\CodeBlks\CPrimer\ProgEx44\x64>WindRes -i ProgEx44.rc -o ProgEx44.obj -v
2 Using `gcc -E -xc -DRC_INVOKED ProgEx44.rc'
3 Using popen to read preprocessor output
4
5 C:\Code\CodeBlks\CPrimer\ProgEx44\x64>dir
6 Volume in drive C is OSDisk
7 Volume Serial Number is 3E79-B713
8
9 Directory of C:\Code\CodeBlks\CPrimer\ProgEx44\x64
10
11 01/04/2013 01:57 PM <DIR> .
12 01/04/2013 01:57 PM <DIR> ..
13 01/04/2013 11:46 AM 15,375 Main.cpp
14 01/31/2010 07:12 PM 757 ProgEx44.h
15 01/04/2013 01:57 PM 826 ProgEx44.obj <<< Note compiled resource script output!!!
16 01/03/2013 06:48 PM 901 ProgEx44.rc
17 01/03/2013 06:46 PM 76 ProgEx44_x64.bat
18 5 File(s) 17,935 bytes
19 2 Dir(s) 134,609,035,264 bytes free
20
21 C:\Code\CodeBlks\CPrimer\ProgEx44\x64>g++ -Wall Main.cpp ProgEx44.obj -o ProgEx44_x64.exe -mwindows -m64 -Os -s
22
23 C:\Code\CodeBlks\CPrimer\ProgEx44\x64>dir
24 Volume in drive C is OSDisk
25 Volume Serial Number is 3E79-B713
26
27 Directory of C:\Code\CodeBlks\CPrimer\ProgEx44\x64
28
29 01/04/2013 02:00 PM <DIR> .
30 01/04/2013 02:00 PM <DIR> ..
31 01/04/2013 11:46 AM 15,375 Main.cpp
32 01/31/2010 07:12 PM 757 ProgEx44.h
33 01/04/2013 01:57 PM 826 ProgEx44.obj
34 01/03/2013 06:48 PM 901 ProgEx44.rc
35 01/03/2013 06:46 PM 76 ProgEx44_x64.bat
36 01/04/2013 02:00 PM 41,472 ProgEx44_x64.exe <<< Note compiled 64 bit executable!!!
37 6 File(s) 59,407 bytes
38 2 Dir(s) 134,608,990,208 bytes free
39
40 C:\Code\CodeBlks\CPrimer\ProgEx44\x64>
-- End Command Line Output --
Line #1 above invokes WindRes so as to compile the rc file into an obj file. Line 2 and 3 are
the output from that command, and on line 5 I issue a dir command to see if the ProgEx44.obj file
was indeed produced, and sure enough it was as evedenced by its listing in line 15 where I added
a note to that effect.
Then in line 21 I invoke g++ - the GNU C++ compiler. There was no output from my command line but
the dir on line 23 proves that the command succeeded as line 36 shows the ProgEx44_x64.exe file,
which when run, worked perfectly.
The program displays a little window with a menu containing File, Options, and Help. When you
invoke File >> Open or File >> Save an 'Open' or 'Save' Common Dialog Box is displayed. When you
choose a file the path and filename are displayed with TextOut() in the littlw window.
When you choose 'Options' an Explorer Window is opened showing the root C: drive files & folders.
When you go to Help and choose 'About' a dialog box from the resource script is created. Here are the
three files you'll need to create the app. They are ProgEx44.rc, ProgEx44.h and ProgEx44.cpp.
/* ProgEx42.rc */
#include <windows.h>
#include "ProgEx44.h"
MainMenu MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open...", IDM_FILE_OPEN
MENUITEM "&Save...", IDM_FILE_SAVE
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_FILE_EXIT
END
POPUP "O&ptions"
BEGIN
MENUITEM "&Explorer", IDM_OPTIONS_EXPLORER
END
POPUP "&Help"
BEGIN
MENUITEM "&About", IDM_ABOUT
END
END
dlgAbout DIALOG DISCARDABLE 100, 100, 239, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "&OK",1,174,18,50,14
PUSHBUTTON "&Cancel",2,174,35,50,14
GROUPBOX "About this program...",-1,7,7,225,52
CTEXT "An example program showing how to use Dialog Boxes\r\n\r\nBy Fred",-1,16,18,144,33
END
//ProgEx44.h
#ifndef PROGEX44_H_INCLUDED
#define PROGEX44_H_INCLUDED
#define dim(x) (sizeof(x) / sizeof(x[0]))
#define IDC_STATIC -1
#define IDM_FILE_OPEN 1500
#define IDM_FILE_SAVE 1505
#define IDM_FILE_EXIT 1510
#define IDM_OPTIONS_EXPLORER 1600
#define IDM_ABOUT 1700
#define IDD_DIALOGABOUT 1800
#define IDD_GROUP 1805
#define IDD_OK 1810
#define IDD_CANCEL 1815
struct WindowsEventArguments
{
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
};
typedef WindowsEventArguments* lpWndEventArgs;
long fnWndProc_OnCreate (lpWndEventArgs Wea);
long fnWndProc_OnCommand (lpWndEventArgs Wea);
long fnWndProc_OnPaint (lpWndEventArgs Wea);
long fnWndProc_OnDestroy (lpWndEventArgs Wea);
struct EVENTHANDLER
{
unsigned int iMsg;
long (*fnPtr)(lpWndEventArgs);
};
const EVENTHANDLER EventHandler[]=
{
{WM_CREATE, fnWndProc_OnCreate},
{WM_COMMAND, fnWndProc_OnCommand},
{WM_PAINT, fnWndProc_OnPaint},
{WM_DESTROY, fnWndProc_OnDestroy}
};
#endif // PROGEX42_H_INCLUDED
//Main.cpp
#define UNICODE // You can comment UNICODE and _UNICODE out if you use Visual Studio, as in
#define _UNICODE // that environment the the IDE feeds these into the toolchain for you.
#include <windows.h> // However, to avoid various warnings, you might want to include ...
#include <tchar.h>
#include <commdlg.h> // _CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;
#include "ProgEx44.h"
// in the Properties >> C++ >> Preprocessor definitions
INT_PTR CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hwnd, IDOK);
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
TCHAR* ptrOpenFileName=NULL;
ptrOpenFileName=(TCHAR*)GlobalAlloc(GPTR,256*sizeof(TCHAR));
if(ptrOpenFileName)
SetWindowLongPtr(Wea->hWnd,GWLP_USERDATA,(LONG_PTR)ptrOpenFileName);
else
return -1;
return 0;
}
long fnWndProc_OnFileOpen(lpWndEventArgs Wea)
{
static TCHAR szFilter[]=_T("C Files (*.C),CPP Files (*.cpp)\0*.c;*.cpp\0\0");
static TCHAR szTitleName[_MAX_FNAME+_MAX_EXT];
static TCHAR szFileName[_MAX_PATH];
TCHAR* ptrOpenFileName=NULL;
TCHAR lpszBuffer[256*sizeof(TCHAR)];
OPENFILENAME ofn;
GetCurrentDirectory(256*sizeof(TCHAR),lpszBuffer);
memset(&ofn,_T('\0'),sizeof(OPENFILENAME));
ofn.lStructSize=sizeof(OPENFILENAME);
ofn.lpstrFilter = szFilter;
ofn.nMaxFile=_MAX_PATH;
ofn.nMaxFileTitle=_MAX_FNAME+_MAX_EXT;
ofn.lpstrInitialDir = lpszBuffer;
ofn.lpstrDefExt = _T("cpp");
ofn.hInstance=GetModuleHandle(_T(""));
ofn.hwndOwner = Wea->hWnd;
ofn.Flags=OFN_HIDEREADONLY | OFN_CREATEPROMPT;
ofn.lpstrFile=szFileName;
ofn.lpstrFileTitle=szTitleName;
GetOpenFileName(&ofn);
ptrOpenFileName=(TCHAR*)GetWindowLongPtr(Wea->hWnd,GWLP_USERDATA);
_tcscpy(ptrOpenFileName,ofn.lpstrFile);
InvalidateRect(Wea->hWnd,NULL,FALSE);
return 0;
}
long fnWndProc_OnFileSave(lpWndEventArgs Wea)
{
static TCHAR szFilter[]=_T("C Files (*.C),CPP Files (*.cpp)\0*.c;*.cpp\0\0");
static TCHAR szTitleName[_MAX_FNAME+_MAX_EXT];
static TCHAR szFileName[_MAX_PATH];
TCHAR* ptrOpenFileName=NULL;
TCHAR lpszBuffer[256*sizeof(TCHAR)];
OPENFILENAME ofn;
GetCurrentDirectory(256*sizeof(TCHAR),lpszBuffer);
memset(&ofn,_T('\0'),sizeof(OPENFILENAME));
ofn.lStructSize=sizeof(OPENFILENAME);
ofn.lpstrFilter = szFilter;
ofn.nMaxFile=_MAX_PATH;
ofn.nMaxFileTitle=_MAX_FNAME+_MAX_EXT;
ofn.lpstrInitialDir = lpszBuffer;
ofn.lpstrDefExt = _T("cpp");
ofn.hInstance=GetModuleHandle(_T(""));
ofn.hwndOwner = Wea->hWnd;
ofn.Flags=OFN_HIDEREADONLY | OFN_CREATEPROMPT;
ofn.lpstrFile=szFileName;
ofn.lpstrFileTitle=szTitleName;
GetSaveFileName(&ofn);
ptrOpenFileName=(TCHAR*)GetWindowLongPtr(Wea->hWnd,GWLP_USERDATA);
_tcscpy(ptrOpenFileName,ofn.lpstrFile);
InvalidateRect(Wea->hWnd,NULL,FALSE);
return 0;
}
long fnWndProc_OnAbout(lpWndEventArgs Wea)
{
INT_PTR pReturn=DialogBoxParam(GetModuleHandle(_T("")),_T("dlgAbout"), Wea->hWnd, AboutDlgProc, 0);
if(pReturn==IDOK)
MessageBox(Wea->hWnd,_T("Dialog exited with IDOK."),_T("Notice"),MB_OK|MB_ICONINFORMATION);
else if(pReturn==IDCANCEL)
MessageBox(Wea->hWnd,_T("Dialog exited with IDCANCEL."),_T("Notice"),MB_OK|MB_ICONINFORMATION);
else if(pReturn==-1)
MessageBox(Wea->hWnd,_T("Dialog failed!"),_T("Error"),MB_OK|MB_ICONINFORMATION);
return 0;
}
long fnWndProc_OnOptions(lpWndEventArgs Wea)
{
system("explorer c:\\");
return 0;
}
long fnWndProc_OnExit(lpWndEventArgs Wea)
{
SendMessage(Wea->hWnd,WM_CLOSE,0,0);
return 0;
}
long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
switch(LOWORD(Wea->wParam))
{
case IDM_FILE_OPEN:
return fnWndProc_OnFileOpen(Wea);
case IDM_FILE_SAVE:
return fnWndProc_OnFileSave(Wea);
case IDM_ABOUT:
return fnWndProc_OnAbout(Wea);
case IDM_OPTIONS_EXPLORER:
return fnWndProc_OnOptions(Wea);
case IDM_FILE_EXIT:
return fnWndProc_OnExit(Wea);
}
return 0;
}
long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
TCHAR* ptrOpenFileName=NULL;
PAINTSTRUCT ps;
HDC hDC;
hDC=BeginPaint(Wea->hWnd,&ps);
ptrOpenFileName=(TCHAR*)GetWindowLongPtr(Wea->hWnd,GWLP_USERDATA);
TextOut(hDC,2,2,ptrOpenFileName,(int)_tcslen(ptrOpenFileName));
EndPaint(Wea->hWnd,&ps);
return 0;
}
long fnWndProc_OnDestroy(lpWndEventArgs Wea)
{
TCHAR* ptrOpenFileName=NULL;
ptrOpenFileName=(TCHAR*)GetWindowLongPtr(Wea->hWnd,GWLP_USERDATA);
GlobalFree(ptrOpenFileName);
PostQuitMessage(0);
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WindowsEventArguments Wea;
for(unsigned int i=0; i<dim(EventHandler); i++)
{
if(EventHandler[i].iMsg==msg)
{
Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*EventHandler[i].fnPtr)(&Wea);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
TCHAR szClass[]=_T("Command Line Compiling");
TCHAR szMenuName[]=_T("MainMenu");
WNDCLASSEX wc;
HWND hWnd;
MSG Msg;
wc.lpszClassName=szClass, wc.lpfnWndProc=WndProc;;
wc.cbSize=sizeof(WNDCLASSEX), wc.style=0;
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=szMenuName, wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
hWnd=CreateWindow(szClass,_T("Dialog Engine Dialog Boxes"),WS_OVERLAPPEDWINDOW,350,350,425,200,NULL,NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
while(GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (int)Msg.wParam;
}