• Welcome to Jose's Read Only Forum 2023.
 

ProgEx44 : Command Line Compiling With GNU Compilers And Using Resource Scripts

Started by Frederick J. Harris, January 05, 2013, 05:46:59 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

 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;
}