How to structure a long program

Pages: 12
I am fluent in c++ and are trying now to change some of my programs in window programs. I am working with win api only and are reading the excellent book of Charles Petzold.

Although many code is already working well, I am struggling with a obviously typical beginner question. I have searched in all forums without results.

If I have to paint different user informations (on the screen) in the same window. (Eg. Menu item A shows information X, Menuitem B shows information Y) How is this done. Each window has a winproc function with a wm_paint case selection there. Is it possible to distinguish different paint-cases with switch within wm_paint? Or is there for each different painting a separate child-window with a separate winproc function necessary. Or do I have to work with subclassing for wm_paint?

I really can't see how to structure long programs efficiently or in general how to structure different paintings in a windows?

Who can advice me? Help would be appreciated very much. Thank you in advance

If I have to paint different user informations (on the screen) in the same window. (Eg. Menu item A shows information X, Menuitem B shows information Y) How is this done. Each window has a winproc function with a wm_paint case selection there. Is it possible to distinguish different paint-cases with switch within wm_paint?


I would allocate memory dynamically (either C++ new or WinApi HeapAlloc()), and store those pointers as part of the Window Class instance through use of GetWindowLongPtr(), where the pointers would be stored as part of the WNDCLASSEX::cbWndExtra bytes.

In the event/message handling code for Menu Item A I would write the necessary data to the dynamic memory through the pointers. Same for Menu Item B, and so forth. At the termination of the message handler one would call InvalidateRect() to force a WM_PAINT message. In WM_PAINT handler code one would retrieve the pointers to the information contained within the 'instance' data in the WNDCLASSEX::cbWndExtra bytes, and TextOut() or DrawText() it to the screen.

There should be no global variables in your program if designed as above. Do you need a short example???
Last edited on
Hi Thank you for your very helpful comment. I guess I have checked what you mean. But a short example would be great. Thx in advance
What you had in mind, I think...

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
151
152
153
154
// cl Main.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib /FeMnuWrk8.exe
// Builds (Release)to 40,960 bytes VC15 (Visual Studio 2008) x64 /MT Linkage
// Main.cpp
#include <windows.h>
#include <tchar.h>

#define NUM_STRINGS            4

#define IDR_MAIN_MENU       2000
#define IDM_FILE_OPEN       2100
#define IDM_FILE_EXIT       2105

#define IDM_MUSIC_BLUEGRASS 2200
#define IDM_MUSIC_ROCK      2205
#define IDM_MUSIC_COUNTRY   2210

#define IDM_HELP_HELP       2300
#define IDM_HELP_ABOUT      2305


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
    case WM_CREATE:
     {
         // Create Menu
         HMENU hMenu    = CreateMenu();
         HMENU hSubMenu = CreatePopupMenu();

         AppendMenu(hSubMenu,  MF_STRING,            IDM_FILE_OPEN,       _T("&Open"));
         AppendMenu(hSubMenu,  MF_STRING,            IDM_FILE_EXIT,       _T("E&xit"));
         AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&File"));

         hSubMenu = CreatePopupMenu();
         AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_BLUEGRASS, _T("&Bluegrass Music"));
         AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_ROCK,      _T("&Rock Music"));
         AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_COUNTRY,   _T("&Country Music"));
         AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&Music"));

         hSubMenu = CreatePopupMenu();
         AppendMenu(hSubMenu,  MF_STRING,            IDM_HELP_HELP,       _T("&Help"));
         AppendMenu(hSubMenu,  MF_STRING,            IDM_HELP_ABOUT,      _T("&About"));
         AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&Help"));

         SetMenu(hwnd, hMenu);

         // Allocate Memory
         HANDLE hHeap=GetProcessHeap();
         TCHAR* pMemory=NULL;
         for(size_t i=0; i<NUM_STRINGS; i++)   // Allocate pointers for NUM_STRINGS
         {                                     // strings and store each pointer at
             pMemory=(TCHAR*)HeapAlloc         // offsets zero through three within
             (                                 // WNDCLASS::cbWndExtra bytes.  This
               hHeap,                          // is how one does OOP in C.  Its the
               HEAP_ZERO_MEMORY,               // same as including the four strings
               32*sizeof(TCHAR)                // as member variables of a C++ Class.
             );                                // SetWindowLongPtr() is used for
             if(!pMemory)                      // storing the pointers in cbWndExtra
                return -1;                     // bytes.
             SetWindowLongPtr(hwnd,i*sizeof(void*),(LONG_PTR)pMemory);
         }
         return 0;
     }
    case WM_COMMAND:
     {
         switch(LOWORD(wParam))
         {
           case IDM_MUSIC_BLUEGRASS:            // Arrays of strings are copied to WNDCLASS::cbWndExtra bytes, and
             {                                  // InvalidateRect() forces WM_PAINT to display strings.
                TCHAR* pGroup[]={_T("Flatt And Scruggs"),_T("Stanley Brothers"),_T("Reno And Smiley"),_T("Jim And Jesse")};
                for(size_t i=0; i<NUM_STRINGS; i++)
                    _tcscpy((TCHAR*)GetWindowLongPtr(hwnd,i*sizeof(void*)),pGroup[i]);
                InvalidateRect(hwnd,NULL,TRUE);
                break;
             }
           case IDM_MUSIC_ROCK:
             {
                TCHAR* pGroup[]={_T("The Rolling Stones"),_T("Creedence Clearwater Revival"),_T("Jimmy Hendrix"),_T("Grand Funk Railroad")};
                for(size_t i=0; i<NUM_STRINGS; i++)
                    _tcscpy((TCHAR*)GetWindowLongPtr(hwnd,i*sizeof(void*)),pGroup[i]);
                InvalidateRect(hwnd,NULL,TRUE);
                break;
             }
           case IDM_MUSIC_COUNTRY:
             {
                TCHAR* pGroup[]={_T("Lorreta Lynn"),_T("Conway Twitty"),_T("Jimmy Rodgers"),_T("Hank Williams")};
                for(size_t i=0; i<NUM_STRINGS; i++)
                    _tcscpy((TCHAR*)GetWindowLongPtr(hwnd,i*sizeof(void*)),pGroup[i]);
                InvalidateRect(hwnd,NULL,TRUE);
                break;
             }
         }
         return 0;
     }
    case WM_PAINT:
     {
         PAINTSTRUCT ps;
         TCHAR* pStr;
         int iTmp;
         HDC hDC;

         hDC=BeginPaint(hwnd,&ps);                                  // Retrieve Strings stored in WM_COMMAND processing
         iTmp=SetBkMode(hDC,TRANSPARENT);                           // and TextOut() them to app window.
         for(size_t i=0; i<NUM_STRINGS; i++)
         {
             pStr=(TCHAR*)GetWindowLongPtr(hwnd,i*sizeof(void*));
             if(pStr)
                TextOut(hDC,125,80+i*20,pStr,_tcslen(pStr));
         }
         SetBkMode(hDC,iTmp);
         EndPaint(hwnd,&ps);
         return 0;
     }
    case WM_DESTROY:
     {
         TCHAR* pMemory=NULL;                                       // Stored pointers (acquired in WM_CREATE) must be
         HANDLE hHeap=NULL;                                         // released in WM_DESTROY.

         hHeap=GetProcessHeap();
         for(size_t i=0; i<NUM_STRINGS; i++)
             HeapFree(hHeap,0,(LPVOID)GetWindowLongPtr(hwnd,i*sizeof(void*)));
         PostQuitMessage(0);
         return 0;
     }
 }

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


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("MnuWrk8");
 WNDCLASS wc;
 MSG messages;
 HWND hWnd;

 wc.style         = 0,                         wc.lpfnWndProc   = fnWndProc;
 wc.lpszClassName = szClassName,               wc.cbClsExtra    = 0;
 wc.cbWndExtra    = 4*sizeof(void*),           wc.hInstance     = hIns,
 wc.hIcon         = NULL,                      wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW,   wc.lpszMenuName  = NULL;
 RegisterClass(&wc);
 hWnd=CreateWindow(szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,420,300,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


Note I only build from the command line and don't use any Visual Garbage Generating IDEs. My command line string for VC is at top.
The program example has three main menu items created programatically in WM_CREATE handler code, and they are File, Music, and Help. I don't believe the File and Help menus do anything, but the Music menu has selections for Blugrass, Rock, and Country music. From your writing I'm guessing English is a 2nd language for you, but in the US those are three broad categories or music. When you click on any of the selections, WM_COMMAND handler code copies strings containing the names of various artists within that genera of music to memory allocated in WM_CREATE code with HeapAlloc(). There are four (NUM_STRINGS) pointers involved actually, and they are stored in the first four 'slots' within the main window instance WNDCLASS::cbWndExtra bytes. That would be 16 bytes if x86 and 32 bytes if x64. Note I only allocated memory for strings 32 characters wide. I hope you don't struggle with this too much. If you are lost let me know and I'll explain in more detail.
Freddie, you helped me so much. Your code works well and does exactly what I need. Thank you very much. But I have an additional question:

My string lists in the menus menu-items A-C are long (between 30 and 100 lines). I know how to allocate memory space dynamically and during the running-time of the program. I also know how to delete the memory in order to avoid memory leaks. However how to delete the memory while the program is running? E.g.: in the IDM_Music_Rock Handle I allocate the needed memory and fill it with the strings and paint it (with an invalidate rect call). But end this menu or calling another music menu: how to destroy the memory (without destroying the menu?). Do I need a Button? I would know how to do this, but find it not very elegant.

Is there another way?

PS Your right, of course, with your guess on my English as second language. My mother language is (Swiss)German.
Mein Deutsch ist nicht so gut als Ihr English! :) Hier in den Vereinigten Staaten hatte Ich nie die Angelegenheit mit Deutschen zu sprechen gehabt. Habe Ich das richtig gesagt???

Anyway, not sure I understand your question. Could you elaborate further? But I'll try anyway.

First, you can always delete memory (using delete [] if acquired with new, or HeapFree() if acquired with HeapAlloc()), but do you really need to delete/free it? You'll note in my program I maintain the memory until the program ends, and simply copy different strings to the allocated space. That's fine so long as one doesn't over-run any buffers.

If that isn't a possibility, and you need to continually be acquiring/releasing memory then just be careful that when WM_PAINT messages occur (and they can occur any time and unexpectedly due to user interaction with the GUI), that NULL pointers are not retrieved in the WM_PAINT handler code, otherwise a crash can occur. One way to help with this, if you are re-using string pointer variables, is to always remember to set the pointer
variable to NULL after freeing the memory. And if in your use of the variable you always test for a NULL, then you can avoid crashes. That's just a trick I've learned over the years (I'm fairly old at this).

It sounds to me like you are using/needing arrays of strings. Is that so? Maybe if you could provide more detail on what you are attempting, I could provide better advice.
Last edited on
Ihr Deutsch ist fast perfekt! Haben Sie Deutsch in der Schule gelernt? In Switzerland we have four languages. Twenty miles from where I live people speak French....

I am sorry when my question was not clear enough. I fully understand that your code maintains the memory until the program ends. However this - at least I think that - is not flexible enough for my needs.

I go back some steps and try to describe on what I am working. I am working on a kind of database system. My program lets the user input e.g. contact information of people, name, adresss, zip, town and so on. The program collects the user input with a dialog box containing edit boxes. As data structure I created a class. The collected data is stored in a binary file. I do the same for other kind of data, e.g. test results (I am an economist and no teacher by the way 😉). Everything is working fine.

Now, I would like to print composed data on the screen. I have coded (dialog box, again) masks, where the user can chose between different options, what has to be printed. Now: I would of course like to print different reports and for some standard reports I have created menu items.

My problem is: I can easily paint one report. I read the needed informations from the binary file(s) and Text or draw them out by the wm_paint handler.

However, I have no good solution for how to print different reports. That's because my window has only one wndprocfunction with one wm_paint handler and i do not know how to make different wm_paint handlers for the same window. I have suboptimal solutions, e.g. several child windows each with its own wndprocfunction. Another thing I tried was subleasing wm_paint, which works well, however wm_vscroll is never sent to the procedure, which is subclassed to. So I am looking for a better working solution.

And my reports are rather long, so I need vscroll, which I can code although it is a tricky thing. I already thought about buffering, e.g. Getting out the needed information from the files to a bitmap and paint it out in one step....

Is that clear enough?

PS I am fully aware that a professional database system with query and stuff would be a much better solution. I am doing a fun project, which I will never use. It is for the fun of learning to program c++ in a windows environment.

PSS I have read Petzolds book, which is create stuff and understand already a lot of the windows concepts. However I have still problems putting things together, e.g. how to structure the code in functions, how to pass variables in order to avoid global variables. Things which are easy for me programming c++ console programs. In order to not disturb you to much: To you have advice for further good books, or do I have to re-read Petzold again and more careful? Should I start with less ambitious projects? I am a good self-learner and willing to learn windows better. I learned programming back at college, Turbo Pascal. I have done nothing for twenty years. I have teached myself. C++ in the last months and started know with win api stuff.

Have a nice weekend.


Ihr Deutsch ist fast perfekt! Haben Sie Deutsch in der Schule gelernt? In Switzerland we have four languages. Twenty miles from where I live people speak French....


I taught myself German. Many years ago I had a dream of someday going to your area, i.e., Germany and Switzerland, and studying the historical origins of the discipline of Forestry there. I’m a forester, and presently work for the Pennsylvania Bureau of Forestry, where, for a long time I have been mostly involved with developing computer systems for them. Earlier in my career though I was mostly involved with on the ground field forestry, where I became intrigued with the motivations of folks here to properly manage their forests. It had always been my understanding that forestry was actually developed in Germany and France, and generally on large estates. It appears the folks who owned the forest land managed it conservatively and did not overly exploit it to the point of diminishing its productive capacity. I was interested in their motivations, which quickly becomes a sociological or anthropological exercise. This is because 'motivations' are related to the social structure or institutional structure of a society or culture. In old Germany and France the inheritance of land was governed by old laws and customs involving primogeniture, i.e., the eldest son inherits all.
Here in america these laws were the very first to be overturned soon after the American Revolution of 1776. Same thing happened in France in 1789. Of course, the idea was to over turn the aristocracy. I was interested in these things so that's why I started studying German. But alas, my life is nearly passed – I’m old now, and it’s a dream that will never be fulfilled I fear.

But back to computer programming. I believe I can help you with the various ‘loose ends’ in your Petzold education you mentioned. It will take several posts on my part to accomplish it, so please bear with me as I can’t provide everything at once.

But first, I’d like to introduce you to an alternate program architecture that I use. I mention this for several reasons. First, it’s a better way to go about things. For now, please just take my word for that (in your first post you asked about how to structure long programs);

Second, the examples I’m going to post or link you to will most likely use this coding architecture I'm showing you, and if you spend some time with them I believe you will find your answers.

So please bear with me as I try to explain this. First I’ll provide my last program to you, which you’ve studied and understand, in this ‘new’ architecture involving separate message/event handling functions. It’s the same exact program as the last - just structured completely differently…
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//Main.cpp
// g++ Main.cpp -oForm1.exe -mwindows -m64 -Os -s
// cl Main.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib /FeMnuWrk9.exe
// 41,984 bytes VC15 (Visual Studio 2008) x64 /MT linkage
// cl Main.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib /FeMnuWrk9.exe
// 5,632 bytes VC15 (Visual Studio 2008) x64 TCLib Static Linkage
#include <windows.h>
#ifdef TCLib
   #include "string.h"
   #include "tchar.h"
#else
   #include <string.h>
   #include <tchar.h>
#endif
#include "MnuWrk.h"


LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
{
 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;

 // Create Menu
 HMENU hMenu    = CreateMenu();
 HMENU hSubMenu = CreatePopupMenu();

 AppendMenu(hSubMenu,  MF_STRING,            IDM_FILE_OPEN,       _T("&Open"));
 AppendMenu(hSubMenu,  MF_STRING,            IDM_FILE_EXIT,       _T("E&xit"));
 AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&File"));

 hSubMenu = CreatePopupMenu();
 AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_BLUEGRASS, _T("&Bluegrass Music"));
 AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_ROCK,      _T("&Rock Music"));
 AppendMenu(hSubMenu,  MF_STRING,            IDM_MUSIC_COUNTRY,   _T("&Country Music"));
 AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&Music"));

 hSubMenu = CreatePopupMenu();
 AppendMenu(hSubMenu,  MF_STRING,            IDM_HELP_HELP,       _T("&Help"));
 AppendMenu(hSubMenu,  MF_STRING,            IDM_HELP_ABOUT,      _T("&About"));
 AppendMenu(hMenu,     MF_STRING | MF_POPUP, (UINT)hSubMenu,      _T("&Help"));

 SetMenu(Wea.hWnd, hMenu);

 // Allocate Memory
 HANDLE hHeap=GetProcessHeap();
 TCHAR* pMemory=NULL;
 for(size_t i=0; i<NUM_STRINGS; i++)   // Allocate pointers for NUM_STRINGS
 {                                     // strings and store each pointer at
     pMemory=(TCHAR*)HeapAlloc         // offsets zero through three within
     (                                 // WNDCLASS::cbWndExtra bytes.  This
      hHeap,                           // is how one does OOP in C.  Its the
      HEAP_ZERO_MEMORY,                // same as including the four strings
      32*sizeof(TCHAR)                 // as member variables of a C++ Class.
     );                                // SetWindowLongPtr() is used for
     if(!pMemory)                      // storing the pointers in cbWndExtra
        return -1;                     // bytes.
     SetWindowLongPtr(Wea.hWnd,i*sizeof(void*),(LONG_PTR)pMemory);
 }

 return 0;
}


LRESULT fnWndProc_OnCommand(WndEventArgs& Wea)
{
 switch(LOWORD(Wea.wParam))
 {
   case IDM_MUSIC_BLUEGRASS:            // Arrays of strings are copied to WNDCLASS::cbWndExtra bytes, and
    {                                   // InvalidateRect() forces WM_PAINT to display strings.
        TCHAR* pGroup[]={_T("Flatt And Scruggs"),_T("Stanley Brothers"),_T("Reno And Smiley"),_T("Jim And Jesse")};
        for(size_t i=0; i<NUM_STRINGS; i++)
            _tcscpy((TCHAR*)GetWindowLongPtr(Wea.hWnd,i*sizeof(void*)),pGroup[i]);
        InvalidateRect(Wea.hWnd,NULL,TRUE);
        break;
    }
   case IDM_MUSIC_ROCK:
    {
        TCHAR* pGroup[]={_T("The Rolling Stones"),_T("Creedence Clearwater Revival"),_T("Jimmy Hendrix"),_T("Grand Funk Railroad")};
        for(size_t i=0; i<NUM_STRINGS; i++)
           _tcscpy((TCHAR*)GetWindowLongPtr(Wea.hWnd,i*sizeof(void*)),pGroup[i]);
        InvalidateRect(Wea.hWnd,NULL,TRUE);
        break;
    }
   case IDM_MUSIC_COUNTRY:
    {
        TCHAR* pGroup[]={_T("Lorreta Lynn"),_T("Conway Twitty"),_T("Jimmy Rodgers"),_T("Hank Williams")};
        for(size_t i=0; i<NUM_STRINGS; i++)
           _tcscpy((TCHAR*)GetWindowLongPtr(Wea.hWnd,i*sizeof(void*)),pGroup[i]);
        InvalidateRect(Wea.hWnd,NULL,TRUE);
        break;
    }
 }

 return 0;
}


LRESULT fnWndProc_OnPaint(WndEventArgs& Wea)
{
 PAINTSTRUCT ps;
 TCHAR* pStr;
 int iTmp;
 HDC hDC;

 hDC=BeginPaint(Wea.hWnd,&ps);                              // Retrieve Strings stored in WM_COMMAND processing
 iTmp=SetBkMode(hDC,TRANSPARENT);                           // and TextOut() them to app window.
 for(size_t i=0; i<NUM_STRINGS; i++)
 {
     pStr=(TCHAR*)GetWindowLongPtr(Wea.hWnd,i*sizeof(void*));
     if(pStr)
        TextOut(hDC,125,80+i*20,pStr,_tcslen(pStr));
 }
 SetBkMode(hDC,iTmp);
 EndPaint(Wea.hWnd,&ps);

 return 0;
}


LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 TCHAR* pMemory=NULL;                                       // Stored pointers (acquired in WM_CREATE) must be
 HANDLE hHeap=NULL;                                         // released in WM_DESTROY.

 hHeap=GetProcessHeap();
 for(size_t i=0; i<NUM_STRINGS; i++)
     HeapFree(hHeap,0,(LPVOID)GetWindowLongPtr(Wea.hWnd,i*sizeof(void*)));
 PostQuitMessage(0);

 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs 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 WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("MnuWrk9");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = szClassName,                       wc.lpfnWndProc = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX),                wc.style       = 0;
 wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION),    wc.hInstance   = hIns;
 wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION),   wc.hCursor     = LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW,           wc.cbWndExtra  = NUM_STRINGS*sizeof(void*);
 wc.lpszMenuName  = NULL;                              wc.cbClsExtra  = 0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,150,150,420,300,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


What’s going on there is that the Window Procedure no longer is using switch logic to map incoming messages to the code which handles that message. Rather, a for loop is iterating through an array of EVENTHANDLER objects, whose definition can be seen in the required header below (MnuWrk.h)…
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
// MnuWrk.h
#ifndef MnuWrk_h
#define MnuWrk_h

#define                       NUM_STRINGS            4

#define                       IDR_MAIN_MENU       2000
#define                       IDM_FILE_OPEN       2100
#define                       IDM_FILE_EXIT       2105

#define                       IDM_MUSIC_BLUEGRASS 2200
#define                       IDM_MUSIC_ROCK      2205
#define                       IDM_MUSIC_COUNTRY   2210

#define                       IDM_HELP_HELP       2300
#define                       IDM_HELP_ABOUT      2305

#define dim(x)                (sizeof(x) / sizeof(x[0]))

struct WndEventArgs
{
 HWND                         hWnd;
 WPARAM                       wParam;
 LPARAM                       lParam;
 HINSTANCE                    hIns;
};

LRESULT fnWndProc_OnCreate    (WndEventArgs&);
LRESULT fnWndProc_OnCommand   (WndEventArgs&);
LRESULT fnWndProc_OnPaint     (WndEventArgs&);
LRESULT fnWndProc_OnDestroy   (WndEventArgs&);

struct EVENTHANDLER
{
 unsigned int                 iMsg;
 LRESULT                      (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER            EventHandler[] =
{
 {WM_CREATE,                  fnWndProc_OnCreate},
 {WM_COMMAND,                 fnWndProc_OnCommand},
 {WM_PAINT,                   fnWndProc_OnPaint},
 {WM_DESTROY,                 fnWndProc_OnDestroy}
};

#endif 


There is an unsigned int iMsg member and a function pointer member fnPtr in EVENTHANDLER. The for loop iterates through an array of these attempting to match the iMsg member to the incoming Windows message very much like the switch construct did. If a match is made the message handling function is called through the function pointer member.

Its not unnatural to at first be horrified by the seeming complexity of this. But its really not that bad. I go to great pains to derive the whole concept at this link…

ProgEx39 -- Function Pointers For Modularizing Our Window Procedures
December 03, 2009, 03:50:17 AM

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

It has occurred to me already that modern C++ coders who did not come to the language originally through C first are sometimes 'weak' on function pointers, which are a very important if not advanced topic. I believe my link above will partly rectify all that and you'll see how they work.

I expect it may take you some time to digest all that. But you yourself said there is some awkwardness in your solutions, and I’m just providing this in an attempt to help.

Next…


However, I have no good solution for how to print different reports. That's because my window has only one wndprocfunction with one wm_paint handler and i do not know how to make different wm_paint handlers for the same window. I have suboptimal solutions, e.g. several child windows each with its own wndprocfunction. Another thing I tried was subleasing wm_paint, which works well, however wm_vscroll is never sent to the procedure, which is subclassed to. So I am looking for a better working solution.


and…


PSS I have read Petzolds book, which is create stuff and understand already a lot of the windows concepts. However I have still problems putting things together, e.g. how to structure the code in functions, how to pass variables in order to avoid global variables. Things which are easy for me programming c++ console programs.


In terms of Object Oriented Programming theory, one devises objects to encapsulate behavior. In any given non-trivial application there may be many behaviors. In your reply you stated you have dialogs which capture various user inputs. Then there is a need to display various outputs. That is a separate behavior and requires a separate object.

Now, when Windows was first created in the 1983 – 1985 time frame C++ was not a mainstream language – C was. However, at that time the concepts and benefits of Object Oriented Programming were well known, and those were the design principals used – believe it or not. So what we have in Win32/64 Windows Api programming is OOP done in C.

So, what you need to display some kind of report based on crunching your file/database information is another ‘object’ – a display object. And attempting to use the same Window Procedure with its WM_PAINT handler which is servicing your main application Window (object) is a violation of Object Oriented Programming’s principal of each object having separate duties.

Now, in your WinMain() function if you are using standard SDK procedures (not simply creating Dialog Boxes with Windows Dialog Engine), you have a CreateWindow() or CreateWindowEx() function call to create your main application window based on a Window Class you likely registered in WinMain with RegisterClass() or RegisterClassEx(). So that takes care of your main application object. That object contains logic to present other user interface items such as perhaps dialog boxes, and menus to present options to the user. And the message handling done there supports that.

But you need to create another window of a different Class to provide some of the other functionalities you need. You have already found that trying to provide these additional functionalities within the context of your main application’s window leads to awkward and horrible code, for example, your attempt to make one WM_PAINT handler do many things.

Here is what I would recommend. Register another Window Class – perhaps a ‘Display’ or ‘Output Screen’ Class in the WM_CREATE handler code of your main application. In my alternate architecture example above that would occur in fnWndProc_OnCreate(…). Here would be a very simple example with a main application window and three additional top level windows (not child windows in any way) which take care of three additional ‘modalities’ of a program. Note there are four WM_PAINT handlers in this code. Its a very simple example, but there are a lot of seperate files, so you are going to have to learn how to deal with multiple module/file projects to get this to compile. The Main application window is represented by Main.cpp and Main.h. By clicking buttons on the Main application window you can create three additional top level windows/forms/dialogs named Form1, Form2, and Form3. Those objects are represented in code by Form1.cpp and Form1.h, Form2.cpp and Form2.h, and Form3.cpp and Form3.h. In whatever IDE and build system you are using you need to create all these files and include them in the project. All these files for me are in a...

C:\Code\CodeBlks\Forms\Form23

...directory. Here are the files…
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
// Main.h
#ifndef MAIN_H
#define MAIN_H

#define dim(x)                (sizeof(x) / sizeof(x[0]))
#define IDC_BUTTON_FORM1      1600
#define IDC_BUTTON_FORM2      1605
#define IDC_BUTTON_FORM3      1610

struct                        WndEventArgs
{
 HWND                         hWnd;
 WPARAM                       wParam;
 LPARAM                       lParam;
 HINSTANCE                    hIns;
};

struct EVENTHANDLER
{
 unsigned int                 iMsg;
 LRESULT                      (*fnPtr)(WndEventArgs&);
};

LRESULT fnWndProc_OnCreate    (WndEventArgs&);  //We need foreward declarations of
LRESULT fnWndProc_OnCommand   (WndEventArgs&);  //the various functions in a main
LRESULT fnWndProc_OnClose     (WndEventArgs&);  //header file so that the event
                                                //handlers can be attached below.
LRESULT fnForm1_OnCreate      (WndEventArgs&);
LRESULT fnForm1_OnPaint       (WndEventArgs&);
LRESULT fnForm1_OnClose       (WndEventArgs&);

LRESULT fnForm2_OnCreate      (WndEventArgs&);
LRESULT fnForm2_OnPaint       (WndEventArgs&);
LRESULT fnForm2_OnClose       (WndEventArgs&);

LRESULT fnForm3_OnCreate      (WndEventArgs&);
LRESULT fnForm3_OnPaint       (WndEventArgs&);
LRESULT fnForm3_OnClose       (WndEventArgs&);

const EVENTHANDLER            Form1EventHandler[]=
{
 {WM_CREATE,                  fnForm1_OnCreate},
 {WM_PAINT,                   fnForm1_OnPaint},
 {WM_CLOSE,                   fnForm1_OnClose}
};

const EVENTHANDLER            Form2EventHandler[]=
{
 {WM_CREATE,                  fnForm2_OnCreate},
 {WM_PAINT,                   fnForm2_OnPaint},
 {WM_CLOSE,                   fnForm2_OnClose}
};

const EVENTHANDLER            Form3EventHandler[]=
{
 {WM_PAINT,                   fnForm3_OnPaint},
 {WM_CLOSE,                   fnForm3_OnClose}
};
#endif 


1
2
3
4
5
//Form1.h         // Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM1_H   // references fnForm1_WndProc as the Window Procedure for the Form1 Class.
#define FORM1_H   // This would be needed to register the class.
LRESULT CALLBACK fnForm1_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif 


1
2
3
4
5
//Form2.h        //Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM2_H  //references fnForm2_WndProc as the Window Procedure for the Form1 Class.
#define FORM2_H  //This would be needed to register the class.
LRESULT CALLBACK fnForm2_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif 


1
2
3
4
5
//Form3.h        //Needs to be included in Main.cpp because the WM_CREATE handler there
#ifndef FORM3_H  //references fnForm3_WndProc as the Window Procedure for the Form1 Class.
#define FORM3_H  //This would be needed to register the class.
LRESULT CALLBACK fnForm3_WndProc(HWND, unsigned int, WPARAM, LPARAM);
#endif 

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
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//Main.cpp                //Program displays a main Form/Window/Dialog With Three Buttons on it to simulate
#include <windows.h>      //a program started from a main Form with three modules/options/modalities within
#include <tchar.h>        //which it operates.  When you click the top button a new Form/Window/Dialog is
#include <stdio.h>        //created with CreateWindow() of the "Form1" Window Class, and in the WM_CREATE
#include "Main.h"         //handler of this new window it disables the main form with the three buttons and
#include "Form1.h"        //therefore is an example of a modal dialog.  When you dismiss this modal dialog
#include "Form2.h"        //and click on Button/Option #2 on the Main Form, a CreateWindow() call creates a
#include "Form3.h"        //window of "Form2" Window Class, and in the WM_CREATE handler...

const EVENTHANDLER         EventHandler[]=        //Since we foreward declared above
{                                                 //the various event handling functions
 {WM_CREATE,               fnWndProc_OnCreate},   //of the various forms, windows, dialogs
 {WM_COMMAND,              fnWndProc_OnCommand},  //above, we can fill out the fields of
 {WM_CLOSE,                fnWndProc_OnClose}     //our EVENTHANDLER structures for the
};                                                //various objects.


LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)      //...for this window it hides or makes invisible the main
{                                                  //window.  After dismssing this window you'll find that you
 DWORD dwStyle=WS_CHILD|WS_VISIBLE;                //can click on the Option #3 button as many times as you like
 TCHAR szClassName[16];                            //because the window-form-dialog it creates neither disables
 WNDCLASSEX wc;                                    //nor makes invisible the main window.  Further note that these
                                                   //Option #3 windows can be interacted with regardless of what-
 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance; //ever is going on with the other windows.
 CreateWindow(_T("button"),_T("Option #1"),dwStyle,65,15,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM1,Wea.hIns,0);
 CreateWindow(_T("button"),_T("Option #2"),dwStyle,65,55,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM2,Wea.hIns,0);
 CreateWindow(_T("button"),_T("Option #3"),dwStyle,65,95,120,25,Wea.hWnd,(HMENU)IDC_BUTTON_FORM3,Wea.hIns,0);

 //Register Window Classes For Form1, Form2 and Form3
 wc.cbSize=sizeof(WNDCLASSEX),                            wc.style=CS_HREDRAW | CS_VREDRAW;
 wc.cbClsExtra=0,                                         wc.cbWndExtra=0;
 wc.hInstance=Wea.hIns,                                   wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);
 wc.hIconSm=0,                                            wc.hCursor=LoadCursor(NULL, IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH),    wc.lpszMenuName=NULL;
 _tcscpy(szClassName,_T("Form1")),                        wc.lpszClassName=szClassName;
 wc.lpfnWndProc=fnForm1_WndProc;
 RegisterClassEx(&wc);

 _tcscpy(szClassName,_T("Form2"));
 wc.lpfnWndProc=fnForm2_WndProc;
 wc.lpszClassName=szClassName;                //Note that a WM_CREATE call is akin to a constructor call in typical
 RegisterClassEx(&wc);                        //C++ class architecture.  When you receive this call/message Windows
                                              //has finished doing what it needs to support the Window object, and
 _tcscpy(szClassName,_T("Form3"));            //is 'passing the ball' to you.  In my apps with multiple windows I
 wc.lpszClassName=szClassName;                //typically use the WM_CREATE handler to register any window classes
 wc.lpfnWndProc=fnForm3_WndProc;              //I need in the app, so that I can make CreateWindow() calls when I
 RegisterClassEx(&wc);                        //need to instantiate a window of some type.

 return 0;
}


long btnForm1_Click(WndEventArgs& Wea)        //This is an 'Event Handler' for a click of the top button on the
{                                             //main Form.  It uses CreateWindowEx() to instantiate a new Window
 HWND hWnd;                                   //of class "Form1"  Note that the last parameter of the call is
                                              //Wea->hWnd.  That is the HWND of the main program Window.  The
 hWnd=CreateWindowEx                          //last parameter is the Creation Parameters parameter.  It can be
 (                                            //retrieved (as can all the others) through the CREATIONSTRUCT a
   0,                                         //pointer to which is received in the lParam of the WM_CREATE
   _T("Form1"),                               //message for the newly instantiating Window.
   _T("Form1"),
   WS_OVERLAPPEDWINDOW,
   50,25,310,185,
   Wea.hWnd,
   (HMENU)0,
   GetModuleHandle(0),
   Wea.hWnd
 );
 ShowWindow(hWnd,SW_SHOWNORMAL);
 UpdateWindow(hWnd);

 return 0;
}


long btnForm2_Click(WndEventArgs& Wea)
{
 HWND hWnd;

 hWnd=CreateWindowEx
 (
   0,
   _T("Form2"),
   _T("Form2"),
   WS_OVERLAPPEDWINDOW,
   200,250,310,185,
   Wea.hWnd,
   (HMENU)0,
   GetModuleHandle(0),
   Wea.hWnd
 );
 ShowWindow(hWnd,SW_SHOWNORMAL);
 UpdateWindow(hWnd);

 return 0;
}


long btnForm3_Click(WndEventArgs& Wea)
{
 HWND hWnd;

 hWnd=CreateWindowEx
 (
   0,
   _T("Form3"),
   _T("Form3"),
   WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,CW_USEDEFAULT,300,260,
   0,
   (HMENU)0,
   GetModuleHandle(0),
   Wea.hWnd
 );
 ShowWindow(hWnd,SW_SHOWNORMAL);
 UpdateWindow(hWnd);

 return 0;
}


LRESULT fnWndProc_OnCommand(WndEventArgs& Wea)
{
 switch(LOWORD(Wea.wParam))
 {
    case IDC_BUTTON_FORM1:
      return btnForm1_Click(Wea);
    case IDC_BUTTON_FORM2:
      return btnForm2_Click(Wea);
    case IDC_BUTTON_FORM3:
      return btnForm3_Click(Wea);
 }

 return 0;
}


LRESULT fnWndProc_OnClose(WndEventArgs& Wea)        //Search And Destroy Mission For Any Form3
{                                                //Windows Hanging Around.
 HWND hForm;

 if(MessageBox(Wea.hWnd,_T("Do You Wish To Exit?"),_T("Exit App?"),MB_YESNO)==IDYES)
 {
    do                                           //If FindWindow() returns something other
    {                                            //than zero, it found a window matching
      hForm=FindWindow(_T("Form3"),_T("Form3")); //the description of what you are looking
      if(hForm)                                  //for.  In that case, send a WM_CLOSE
         SendMessage(hForm,WM_CLOSE,0,0);        //message.  If NULL is returned then just
      else                                       //break out of the loop and terminate the
         break;                                  //app.
    }while(TRUE);
    DestroyWindow(Wea.hWnd);
    PostQuitMessage(0);
 }

 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs 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 WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Multiple Forms");
 WNDCLASSEX wc={};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 wc.hInstance=hInstance,                      wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,250,500,260,180,HWND_DESKTOP,0,hInstance,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}
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
//Form1.cpp
#include  <Windows.h>
#include  <tchar.h>
#include  "Form1.h"
#include  "Main.h"


LRESULT fnForm1_OnCreate(WndEventArgs& Wea)
{
 CREATESTRUCT* pCreateStruct;
 HWND hMain;

 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 hMain=(HWND)pCreateStruct->lpCreateParams;
 SetWindowLongPtr(Wea.hWnd,GWLP_USERDATA,(LONG_PTR)hMain);
 EnableWindow(hMain,FALSE);

 return 0;
}


LRESULT fnForm1_OnPaint(WndEventArgs& Wea)
{
 PAINTSTRUCT ps;
 HDC hDC;

 hDC=BeginPaint(Wea.hWnd,&ps);
 TextOut(hDC,0,0,_T("This Is Form1.  It Disables The Main"),36);
 TextOut(hDC,0,16,_T("Window, And That Makes It Modal.  Note"),38);
 TextOut(hDC,0,32,_T("That We Passed The Handle Of The Main"),37);
 TextOut(hDC,0,48,_T("Window In The Last Parameter Of The"),35);
 TextOut(hDC,0,64,_T("CreateWindow() Call, And Retrieved It In"),40);
 TextOut(hDC,0,80,_T("fnForm1_OnCreate().  We Then Stored It So"),41);
 TextOut(hDC,0,96,_T("We Could EnableWindow(TRUE) The Main"),36);
 TextOut(hDC,0,112,_T("Window When This Modal Form Is"),30);
 TextOut(hDC,0,128,_T("Dismissed."),10);
 EndPaint(Wea.hWnd,&ps);

 return 0;
}


LRESULT fnForm1_OnClose(WndEventArgs& Wea)
{
 HWND hMain;

 hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
 EnableWindow(hMain,TRUE);
 DestroyWindow(Wea.hWnd);
 return 0;
}


LRESULT CALLBACK fnForm1_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(Form1EventHandler); i++)
 {
     if(Form1EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*Form1EventHandler[i].fnPtr)(Wea);
     }
 }

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


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
#include  <Windows.h>
#include  <tchar.h>
#include  "Main.h"
#include  "Form2.h"


LRESULT fnForm2_OnCreate(WndEventArgs& Wea)
{
 CREATESTRUCT* pCreateStruct;
 HWND hMain;

 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 hMain=(HWND)pCreateStruct->lpCreateParams;
 SetWindowLongPtr(Wea.hWnd,GWLP_USERDATA,(LONG_PTR)hMain);
 ShowWindow(hMain,SW_HIDE);

 return 0;
}


LRESULT fnForm2_OnPaint(WndEventArgs& Wea)
{
 PAINTSTRUCT ps;
 HDC hDC;

 hDC=BeginPaint(Wea.hWnd,&ps);
 TextOut(hDC,0,0,_T("This Is Form2.  It SW_HIDEs The Main"),36);
 TextOut(hDC,0,16,_T("Window, And SW_SHOWs It Upon Closing."),37);
 TextOut(hDC,0,32,_T("This Technique Can Be Used Similiarly"),37);
 TextOut(hDC,0,48,_T("To A Modal Dialog If It Isn't Necessary To"),42);
 TextOut(hDC,0,64,_T("View Simultaneously A Form Underneath The"),41);
 TextOut(hDC,0,80,_T("Dialog With Which You Can't Interact"),36);
 TextOut(hDC,0,96,_T("Anyway"),6);
 EndPaint(Wea.hWnd,&ps);

 return 0;
}


LRESULT fnForm2_OnClose(WndEventArgs& Wea)
{
 HWND hMain;

 hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
 EnableWindow(hMain,TRUE);
 DestroyWindow(Wea.hWnd);
 ShowWindow(hMain,TRUE);

 return 0;
}


LRESULT CALLBACK fnForm2_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(Form2EventHandler); i++)
 {
     if(Form2EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*Form2EventHandler[i].fnPtr)(Wea);
     }
 }

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


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
//Form3.cpp
#include  <Windows.h>
#include  <tchar.h>
#include  "Main.h"
#include  "Form3.h"


LRESULT fnForm3_OnPaint(WndEventArgs& Wea)
{
 PAINTSTRUCT ps;
 HDC hDC;

 hDC=BeginPaint(Wea.hWnd,&ps);
 TextOut(hDC,0,0,_T("This Is Form3.  Not Only Does It Neither"),40);
 TextOut(hDC,0,16,_T("Hide Nor Disable The Main Window, But"),37);
 TextOut(hDC,0,32,_T("You'll Find That You Can Create As Many"),39);
 TextOut(hDC,0,48,_T("Of These As You Like By Continually"),35);
 TextOut(hDC,0,64,_T("Clicking The Bottom Button On The Main"),38);
 TextOut(hDC,0,80,_T("Form.  However, You'll Have To Drag One"),39);
 TextOut(hDC,0,96,_T("From On Top Of The Other Because They"),37);
 TextOut(hDC,0,112,_T("All Appear In The Same Location (I"),34);
 TextOut(hDC,0,128,_T("Changed That).  You May Further Note"),36);
 TextOut(hDC,0,144,_T("That Since These Windows Are Neither"),36);
 TextOut(hDC,0,160,_T("Disabled Nor Hidden At Any Time, You"),36);
 TextOut(hDC,0,176,_T("May Interact With Them Irregardless Of"),38);
 TextOut(hDC,0,192,_T("The State Of Form1 Or Form2. Pretty"),35);
 TextOut(hDC,0,208,_T("Neat, Don't You Think?"),22);
 EndPaint(Wea.hWnd,&ps);

 return 0;
}


LRESULT fnForm3_OnClose(WndEventArgs& Wea)
{
 HWND hMain;

 MessageBox
 (
  Wea.hWnd,
  _T("Good Way To Release Any Resources, Memory, etc., You May Have Allocated"),
  _T("Window Close Report!"),
  MB_OK
 );
 hMain=(HWND)GetWindowLongPtr(Wea.hWnd,GWLP_USERDATA);
 EnableWindow(hMain,TRUE);
 DestroyWindow(Wea.hWnd);
 ShowWindow(hMain,TRUE);

 return 0;
}


LRESULT CALLBACK fnForm3_WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(Form3EventHandler); i++)
 {
     if(Form3EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*Form3EventHandler[i].fnPtr)(Wea);
     }
 }

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


Here is my command line compilation string for compiling this all using VC15 from Visual Studio 2008...

 
C:\Code\CodeBlks\Forms\Form23>cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib /FeForm23.exe


Here is my full console window output screen from the build followed by a dir command to list the files in my build directory and their sizes...

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
C:\Code\CodeBlks\Forms\Form23>cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib /FeForm23.exe
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Main.cpp
Form1.cpp
Form2.cpp
Form3.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form23.exe
Main.obj
Form1.obj
Form2.obj
Form3.obj
kernel32.lib
user32.lib
gdi32.lib

C:\Code\CodeBlks\Forms\Form23>dir
 Volume in drive C is OSDisk
 Volume Serial Number is 3E79-B713

 Directory of C:\Code\CodeBlks\Forms\Form23

11/06/2016  07:09 AM    <DIR>          .
11/06/2016  07:09 AM    <DIR>          ..
07/01/2016  09:51 AM    <DIR>          .objs
07/01/2016  09:50 AM             1,738 Form1.cpp
09/18/2011  08:40 AM               327 Form1.h
11/06/2016  07:09 AM             5,530 Form1.obj
07/01/2016  09:51 AM             1,633 Form2.cpp
09/18/2011  08:38 AM               326 Form2.h
11/06/2016  07:09 AM             5,103 Form2.obj
02/06/2015  02:11 PM             1,192 Form23.cbp
07/01/2016  09:44 AM               569 Form23.depend
11/06/2016  07:09 AM            44,544 Form23.exe
07/01/2016  10:00 AM             1,588 Form23.layout
07/01/2016  09:51 AM             1,989 Form3.cpp
09/18/2011  08:39 AM               328 Form3.h
11/06/2016  07:09 AM             6,689 Form3.obj
11/06/2016  07:06 AM             8,316 main.cpp
11/06/2016  07:08 AM             1,893 Main.h
11/06/2016  07:09 AM             9,201 Main.obj
              16 File(s)         90,966 bytes
               3 Dir(s)  181,920,874,496 bytes free

C:\Code\CodeBlks\Forms\Form23>


So we're looking at 44 k for that all for a stand alone executable with /MT linkage, i.e., it doesn't require any runtime because the C/C++ runtime has been statically linked into it. I consider that to be quite excessive, and symptomatic of the general problem of C++ having morphed itself over the years into 'bloatware'. I can build that exact same program using SDK techniques in another programming language - PowerBASIC that will come in a third of that size. So in order to not produce 'bloatware' for my C++ work I rewrote the C Standard Library to be more efficient. Using my TCLib.lib (Tiny C Lib) instead of the C/C++ runtimes I can build an x64 binary out of that using this command line string that only comes to 8,704 bytes...
 
cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib /FeForm23.exe


Here is the console output from that build...

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
C:\Code\CodeBlks\Forms\Form23>cl Main.cpp Form1.cpp Form2.cpp Form3.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib gdi32.lib /FeForm23.exe
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Main.cpp
Form1.cpp
Form2.cpp
Form3.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form23.exe
Main.obj
Form1.obj
Form2.obj
Form3.obj
TCLib.lib
kernel32.lib
user32.lib
gdi32.lib

C:\Code\CodeBlks\Forms\Form23>dir
 Volume in drive C is OSDisk
 Volume Serial Number is 3E79-B713

 Directory of C:\Code\CodeBlks\Forms\Form23

11/06/2016  07:21 AM    <DIR>          .
11/06/2016  07:21 AM    <DIR>          ..
07/01/2016  09:51 AM    <DIR>          .objs
07/01/2016  09:50 AM             1,738 Form1.cpp
09/18/2011  08:40 AM               327 Form1.h
11/06/2016  07:21 AM             5,341 Form1.obj
07/01/2016  09:51 AM             1,633 Form2.cpp
09/18/2011  08:38 AM               326 Form2.h
11/06/2016  07:21 AM             4,914 Form2.obj
02/06/2015  02:11 PM             1,192 Form23.cbp
07/01/2016  09:44 AM               569 Form23.depend
11/06/2016  07:21 AM             8,704 Form23.exe
07/01/2016  10:00 AM             1,588 Form23.layout
07/01/2016  09:51 AM             1,989 Form3.cpp
09/18/2011  08:39 AM               328 Form3.h
11/06/2016  07:21 AM             6,500 Form3.obj
11/06/2016  07:20 AM             8,527 main.cpp
11/06/2016  07:08 AM             1,893 Main.h
11/06/2016  07:21 AM             8,928 Main.obj
07/29/2016  12:57 PM               131 Math.h
07/06/2016  01:16 PM               309 memory.h
08/01/2016  07:41 AM             1,313 stdio.h
06/29/2016  01:04 PM             1,512 stdlib.h
06/29/2016  01:43 PM             1,645 string.h
08/05/2016  11:08 AM            32,112 Strings.cpp
08/08/2016  05:01 AM            13,799 Strings.h
08/01/2016  07:43 AM             1,892 tchar.h
08/08/2016  11:47 AM            40,460 TCLib.lib
              25 File(s)        147,670 bytes
               3 Dir(s)  181,920,690,176 bytes free

C:\Code\CodeBlks\Forms\Form23>


That's more satisfactory to me.

Well, I've presented a lot of information to you that I hope will be helpful. There's more to come if you find the above all satisfactory. I just wished to get you started on solutions to your issues.
Last edited on
Freddie, knowing the fact that you taught yourself German, I must say, I am impressed. German is no easy language to learn.

I am also impressed by your posts. It opens a new world to me. I have read everything once and think I have grabbed the general outlines, especially the fact that I need other objects. When I learned Turbo Pascal at college the concept of object oriented programming was not there. I learned the concept with c++ a few months ago and see know, that i have to learn more..

Your alternative program architecture - frankly - I have to digest first, which I will, because my first post was right. I am mainly interested in code structure and architecture at this point of my learning.

I will now carefully study your posts and links which may need some time. But be sure I will come back and I know, where to find you...

It is funny we have similar professional backgrounds. I am a environmental economist, back in my youth I was one of the first environmental economists in my country, which was a new approach then. I have worked many years for the Swiss environmental protection agency in the field of climate change. I was member of the Swiss delegation participating on the climate change negotiations (UN) and I was in the team who introduced some market oriented new instruments in Switzerland like carbon taxes (something you don't favour in the US (😉).

You are right, the forest management has its roots in Germany and France. The main objective of our forestry preservation is to preserve and exploit forrests on the same time and in a sustainable manner. In Switzerland we have a forrest bill with the mandatory rule, that each tree which is cut has to be replaced by planting another tree. Also when I started my professional career (I am now 52) we had a lot of air pollution (sulphur, NOx, mainly, coming from heating and cars). At this time we had a lot of political discussions on the subject of "the Waldsterben" which ended in a lot of new mainly technical rules in order to tackle air pollution. Some in the US, I think.

I agree with you that aristocrats were helpful with regard to conservation (although I think you agree, that there were other problematic issues related to aristocracy). One of the main ideas of environmental economics is, that many environmental problems are related to missing property rights. Air - for instance - is free. Everybody has an incentive to overuse it, nobody and interest to preserve it clean. Fish exploitation works well in lakes, where ownership is clear. On the contrary, seas are overfished because ownership is not settled there. So one solution to many environmental problems is define ownership.

So, I will start now studying your codes and links and come back in due time. Thank you very much for your help and your inspiring thoughts.

Let me know if I can send you some information on forest preservation in our area. Reading German so well, you will find a lot of stuff on the web, of course. But may be I can help searching or ask friends which are in Forrest conservation (see links).

http://www.waldwissen.net/lernen/forstgeschichte/wsl_hueterbueb/index_DE

http://e-collection.library.ethz.ch/eserv/eth:28539/eth-28539-01.pdf




Hi again. I understand now your advice and it is great!

I have to register more classes and create the relevant windows in the wm_create Händler of the main window. Easy and brilliant!

The same is true for your alternative architecture. It allows a better structuring and avoids switch statements over 100 lines of code. That is exactly what I was searching for at least 3 weeks. I am so happy for your advice. Thank you very much!

One final question: Is it worth looking at superbasic? I have to confess, I have so far never worried much about the size of my codes, as long as everything compiled fine and run well. But it is true: c++ is kind of heavy overloaded.

Thanks again! I sure will further follow your advice on this forum.
Well, I'm glad I was able to help Cal. I was worried I was coming on too strong pushing all that code at you. But I thought I had your ills diagnosed pretty good, and I thought I knew the cure. Fact is, I think very highly of Charles Petzold and that's largely how I got started Win Api coding GUIs for Windows. And I had struggled with those very same things you were struggling with. And I knew the things I was showing you weren't found in Petzold anywhere. Fact is, it took me a long time to figure that all out myself.

So, having gotten through all that, you had touched on other issues in your early posts I didn't address yet, simply because I wanted to get you started on those two architectural issues you just got through. You had mentioned about writing reports and scrolling.

Basically, I do piles of report writing in my work apps. I can think of four separate ways I go about it, and you had mentioned part of it.

First, there is writing one's own scroll code. I do that a lot. And like you said, its tricky as all h***. I've done so much of it over the years that I've developed sort of 'canned' routines for it that I can just plug it into my apps without having to expend too many mental calories or loose too much hide getting it to work. I'll see if I can find an example of that to post for you.

Second, I think almost everybody has Microsoft's Office installed which of course comes with Microsoft Word. Now, Word utilizes a software technology known as COM/OLE (Component Object Model / Object Linking and Embedding). What this allows is something known as COM Automation. What it means is that one can programmatically 'drive' Word from code in one's app. The beauty of using Word is that one can create really neat reports because one can set fonts, margins, alignment, etc., etc., and it can all be done in one's code. Same thing can be done with Excel too. I've yet to ever see anyone asking how to do this (with Word) in any C++ forums I visit or contribute too. Occasionally someone will ask about Excel, but never Word. But I'd be very happy to show you how to do it.

Third, one can always just write outputs into a child control, e.g., edit control or listbox. I don't do that too much though because its too easy. Why do something the easy way when there's a hard way to do it! :)

And fourth, one can always write data to a text file, then shell execute it using Notepad.exe.

And as far as where to put that code - I usually keep it in the same code file as wherever the Graphical User Interface element is, e.g., a button or menu selection, the activation of which triggered the code to run.

As far as code size in the binary is concerned, I'm really weird about that. With hard drives as big as they are now, and RAM into the gigabyte range, it seems silly to worry about it. But I do anyway I guess cuz I'm old and in my formative years of coding these things were important. And I guess the other part of it is simply an issue of code craftsmanship.

Lets assume someone gave you an algorithm to write to do something or other. Assume you did your best at it and after writing the code and testing it you had 200 lines of code written and it executed in 1 second. But somebody else - perhaps a coworker, worked on it too and came up with his/her own solution in the same programming language you used, which involved 50 lines of code which executed in a quarter of a second. And that solution worked just as well as yours. How would that make you feel? I'd feel like crawling in a hole or under a rock.

So what I'm getting at is that the final code size of an application - including all its dependencies, gives some very rough idea of the efficiency or code quality of the application. At least in some rough kind of way.

My first coding was done in college using mainframes and Fortran. I've no idea how large those binaries were. But when I got my first PC in the 80s the basic programs I wrote were rather small. In DOS with only 64 k chunks of memory addressable one couldn't really write large programs easily. Soon after that I taught myself C but those programs were small too, like my QuickBasic ones.

In the 90s I got my first Windows PC and taught myself Visual Basic. God, how I loved that! I'm speaking here of the native code versions before Microsoft came out with .NET. Versions 1 through 6 produced native code executables (their underlying compiler was Microsoft's C/C++ compiler). That was actually the world's most popular and used programming language, and some die hards are still using it. But the executable sizes and dependencies were enormous! A couple megabytes just for a "Hello, World!" Windows GUI app. I just figured it had to be that way because Windows was a more complicated operating system than DOS.

But then I discovered Petzold and saw that his GUI C programs were just as small or even smaller than my old DOS basic or C programs. Most of Petzold's Release builds with old VC6 circa 1998 were under 50 - 60 k.

The thing about Petzold's C programs though is that they weren't very fancy. I mean, he showed how to use all the basic Windows Controls and the 'Common Controls', but never got into the more high powered fancy stuff like ActiveX Controls that Visual Basic used. As a specific example, there isn't a grid control among any of the standard Windows Controls or the 'Common Controls'. About the closest thing to a grid would be the Listview Control, which is one of the Common Controls. At that time - lets say the late 90s, if you asked in any internet forums how one would build real fancy GUI apps in C like the ones Visual Basic could create with fancy grids and charting controls you would be told you can't do it in C; you need to use C++ and a Class Framework such as MFC (Microsoft Foundation Classes), or Borland's OWL (Object Windows Library). If that wasn’t a good enough answer for you and you persisted with your questions and pointed out that the underlying library code 'knew' how to do it so how do they do it you would be met with silence. Nobody knows how to do it. You just have to use the Class Frameworks.

And when you did that you immediately lost all the benefits of the small code size C gave you. The MFC library was originally about a megabyte, and now its much larger than that. Same thing for all the other Class Frameworks that have dll dependencies.

In around 2000 or so I taught myself C++ and I guess I was hoping it would be something like Visual Basic but it wasn't. About 2000 or so Microsoft said its Visual Basic product line was over and they were coming out with something they called .NET, and they were deprecating the old native code Visual Basic which I loved. I tried .NET but I hated it. I hated the huge 40 megabyte dependency of the framework. I hated the slowness of the interpreted language. I also wasn’t too enamored of the endless OOP gunk. On the C++ front I hated the Class Frameworks such as MFC. To me, it didn't make coding easier. I thought it was harder than direct C Api coding a la Petzold, and I hated the heavy dependencies. So it seemed to me my only choice for a programming language going forward was good old C.

But then I discovered PowerBASIC. And what truly amazed me was that everything I had learned in C and in Petzold about creating Windows GUI programs worked perfectly in PowerBASIC, only it was a lot easier. For example - and get ready to be completely startled, here is the same basic GUI Petzold style Api program first in C/C++, then in PowerBASIC, that just creates the very simplest window...

continued...
C++
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
// Main.cpp
#include <windows.h>  // VC15 (Visual Studio 2008) 37,376 bytes Memory Image,  40,960 bytes on disk  

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_DESTROY:
    {
       PostQuitMessage(0);
       return 0;
    }
 }

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 char szClassName[]="Form1";
 WNDCLASSEX wc={};
 MSG messages;
 HWND hWnd;

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


PowerBASIC
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
' Form1.bas
#Compile Exe              'Disk image: 6656 bytes   Memory image: 5312 bytes.
#Include "Windows.inc"

Function fnWndProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  If wMsg = %WM_DESTROY Then
     Call PostQuitMessage(0)
     Function=0 : Exit Function
  End If

  fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function

Function WinMain(ByVal hInstance As Long, ByVal hPrevIns As Long, ByVal lpCmdLn As Asciiz Ptr, ByVal iShow As Long) As Long
  Local szClassName As Asciiz*16
  Local wc As WndClassEx
  Local Msg As tagMsg
  Local hWnd As Dword

  szClassName       = "Form1"
  wc.lpszClassName  = Varptr(szClassName)
  wc.lpfnWndProc    = CodePtr(fnWndProc)
  wc.cbSize         = SizeOf(wc)
  wc.hInstance      = hInstance
  wc.hbrBackground  = %COLOR_BACKGROUND
  RegisterClassEx(wc)
  hWnd=CreateWindowEx(0,szClassName,szClassName,%WS_OVERLAPPEDWINDOW Or %WS_VISIBLE,200,100,325,300,0,0,hInstance,ByVal 0)
  While GetMessage(Msg,%NULL,0,0)
    TranslateMessage(Msg)
    DispatchMessage(Msg)
  Wend

  Function=msg.wParam
End Function


I'd hope you'll take a close look at that. To this day it still amazes me. Both will produce the identical 32 bit program, and neither have any dependencies other than a base Windows operating system containing the standard kernel32.dll, user32.dll, and gdi32.dll. But perhaps the most amazing thing are the code sizes! The C++ program compiled with the /MT linker switch to statically link the required C Runtime code into it comes in 37,376 bytes memory image Release build optimized for small code size. The PowerBASIC program comes in 6,656 bytes with the same options! And I must reiterate there are no hidden runtime dependencies with that. Doesn't that cause one to ask why and how was it done?

Well, there's a lot to say about that I won't go into at this point. But after discovering PowerBASIC in the late 90s another amazing event occurred. Recall I said that in the C world nobody really knew any of the coding magic behind the fancy ActiveX Controls used by Visual Basic, and if one wanted to use these in C Family languages one needed to use C++ in conjunction with 'heavy' Class Frameworks such as MFC which resulted in massive executables and code dependencies? Well, a genius kind of materialized out of nowhere in the PowerBASIC Community of coders and started posting in the PowerBASIC Forums around 2000 all the required PowerBASIC code necessary to utilize all these complex COM based OLE ActiveX Controls used by Visual Basic and MFC in PowerBASIC apps! His name was Jose Roca and he was from Spain. This was almost beyond human comprehension. There were probably never more than a couple hundred people in the whole world who completely understood these things, and most of them would have been the high level software engineers at Microsoft who designed COM, OLE, MFC, ATL, and Visual Basic.

Jose didn't even like C and he hated C++ but he simply read the Microsoft documentation on this stuff and then wrote the PowerBASIC code to build the virtual function tables and everything else in memory to support these complex COM/OLE objects, then he posted the code and told everybody, "Here's the code you are all looking for to use the Visual Basic ActiveX Controls and OLE objects in PowerBASIC". Have fun! To this day nobody understands how Jose did that. When you are a genius you can do stuff like that I guess.

To say I studied Jose’s code would be an understatement. I studied it for years until I could duplicate all that low level code in C, C++, and PowerBASIC. So at that point I had a development language straight out of heaven for a code junkie like me. I could use anything from Petzold and do the straight Win Api coding I liked with no code dependencies; I could use fancy Visual Basic ActiveX Controls (charts, grids, etc.) to my hearts content, my apps ran at the speed of C, and they were so ridiculously small I could write major enterprise apps with 50,000 lines of code that were only a few hundred k in size and with no dependencies.

I never heard of SuperBasic. I did a Wikipedia search on it and didn't come up with much. Does it run on a PC? As far as I know the main versions of basics out there are FreeBasic, RealBasic, PureBasic, HotBasic, and PowerBASIC. I believe they all with the exception of PowerBASIC use GCC's C++ mingw compiler as their back end. In other words, they consist of a language parser layer which converts Basic source code into C or C++, then shell out to a C compiler to create the executable.

The main strength of PowerBASIC is that it is a complete build system completely independent of the C/C++ ecosystem. The compiler itself is written completely in assembler. That's rather amazing. It is incredibly fast. And I've benchmarked the generated code. Its as fast as good C code, but more about that in a bit.

At this point you might be wondering why I'm doing C++ if I like PowerBASIC so much? Well, there's bad news. Poison bad news. In 2012 Bob Zale, the creator of PowerBASIC, died. He was only 66 I believe. Compilers were his passion and life’s work. It was a small niche company and he was far and away the main coder. At the time of his death he was working on a 64 bit version of his compiler but he never lived to complete it. Since his death his wife Vivian has been trying to revive the company but she only recently gave up and stated her desire to sell the company to someone who could continue Bob's lifelong work on his compiler. So that's where things are at now.

So a couple years ago I saw what the future held for PowerBASIC and decided to move all my code development work over to C++. That meant converting several massive mission critical enterprise level applications from PowerBASIC to C++.

continued...
As I previously stated I had more or less given up on C++ years ago. But not entirely. In addition to the desktop software I develop at work, I have always done our data collector programs for our handheld field computers using C and eMbedded Visual C++ 3.0 and 4.0. That would be for the Windows CE Operating System. With embedded systems with limited hardware capabilities small efficient code is important. And PowerBASIC didn't have a compiler for Windows CE. So I just toughed it out with C slowly going mad. One day in the early 2000s though it just about got to be too much for me and I flipped out. I'd spent like a whole afternoon parsing a complicated string that would have been about ten minutes work with PowerBASIC, and I decided to pick C++ up again and go about things differently from when I was teaching myself the language several years before. I decided to just take from the language only what I needed and ignore the rest. The only thing I really needed was a decent String Class. That's something C can't easily provide, and it was string manipulations that were killing me. So I eventually built my own String Class and that set me out on a course of finally adopting C++ and more or less slowly abandoning C. My String Class is something near and dear to my heart. I've spent years working on it and wrote hundreds of versions of it, gradually improving it all the time.

But the last real hurdle I had to overcome to convert my PowerBASIC code and algorithms over to C++ was the issue of dynamic multi-dimensional arrays. My most important forestry algorithms for determining timber volumes involved four dimensional arrays where all four subscripts would not be known until runtime, i.e., they needed to be fully dynamic. Neither C nor C++ have any dynamic multi-dimensional array capabilities. There might be some true believers who would dispute that, but they don't know what they are talking about. But using templates and C++ classes I was finally able to overcome that hurdle several years back.

So at that point, and armed with my String Class and multi-dimensional array capabilities I was finally able to do in C++ anything I could do in PowerBASIC. For sure not as easily but I could do it. There was only one remaining issue that griped me to no end, and that was code size. Even by not utilizing anything whatsoever from the C++ Standard Library, and using my own String Class which allowed me to build my programs much smaller than usage of the C++ String Class would allow, I still couldn't touch PowerBASIC's small build sizes.

But everything worked perfectly and I tried to ignore the issue and just accept that nothing could beat PowerBASIC's ultra tight code generation. But it kept aggravating me and so I did some internet searches on the issue and I found out I wasn't alone. Truth be told there were a whole lot of folks aggravated by it and there was indeed something that could be done about it over and above doing Release Builds and optimizing for small code size (which usually doesn't help much). In fact, the solution to the problem came right from the source of the problem itself, and that would be Microsoft and the Microsoft Systems Journal and its contributor Matt Pietrek. I first posted about my explorations into the topic here...

Reducing Executable Size In C/C++ Programs By Eliminating The C Runtime
October 16, 2014, 03:25:27 PM
http://www.jose.it-berater.org/smfforum/index.php?topic=5000.0

I kind of left it at that point because of issues I had to attend to at work, but I picked up on it again January of this year....

Reducing Windows Executable File Size With Matt Pietrek's LIBCTINY.LIB Revisited
February 12, 2016, 01:47:02 PM
http://www.jose.it-berater.org/smfforum/index.php?topic=5085.0

Reducing Program Size In UNICODE And X64 Builds With Matt Pietrek’s LibCTiny.lib
on: February 26, 2016, 01:08:56 AM
http://www.jose.it-berater.org/smfforum/index.php?topic=5102.0

Minimize Program Size By Eliminating The C Runtime Library
March 23, 2016, 01:17:52 AM
http://www.jose.it-berater.org/smfforum/index.php?topic=5126.0

TCLib Now Uses Msvcrt.dll
July 07, 2016, 09:27:26 PM
http://www.jose.it-berater.org/smfforum/index.php?topic=5140.0

So that work is about behind me now and I succeeded with it beyond my wildest expectations. At this point I can build 64 bit C++ programs with binary sizes even smaller than PowerBASIC 32 bit binaries. Using my TCLib.lib and eliminating the loading of the C Runtime I can build the above posted C++ GUI at 3,560 bytes in x64. That's ten times smaller than the 37,376 bytes of VC 15 (Visual C++ 9; VS 2008). A build with the latest edition of Microsoft's C++ compiler (VC19) from Visual Studio 2015 is exactly the same, i.e., 3,560 bytes. And amazingly enough, adding in my String Class adds almost nothing to the code size - a few k, even though it involves over a thousand lines of code.

Just to give you another example of the improvement, lets take parsing a CSV string. The canononical way of doing that in C++ would be this..

Pages: 12