Windows C++ GUI

Pages: 123
OMG!!!!
Don't panic. All will become clear in time.
- Bjarne Stroustrup
Hi Freddie,
Sorry I have caused you all the extra work!

Ian
If I wouldn't have these tangents to occupy my time my only alternative would be to do my work!
Hi Thomas,
I have bought the C++ Principles and Practice book by Stroustrup as you suggested from Amazon UK. I thought that it would be helpful to have a complete reference like the Petzold book which I found the online version of!


Hi Freddie,
You make a very good case for suing the API and compiling it outside of Visual Studio but first things first .........

I need to get comfortable with the basics first and produce something that's half decent in about 1/10 the time I really need, if what you said previously turns out to be correct.

I finished with TabDemo Ian which is a Tab Control Demonstration based on your idea of a main app window with three tabs. I named the tabs in rough accordance with the information you provided me and they are Motor Controller Tab, Site Data Tab, and Project – Start Tab. On the Motor Controller Tab I added a Max Acceleration label and text box, and a Max Speed label and Combo Box. In the Max Speed Combo box I added entries for 100 Revolutions Per Minute To 1000 Revolutions Per Minute. In the other tabs I just put text indicating what they are.

Essentially, this app is a variation of another Demo App I’ve posted here and other places a lot of times showing how to build apps with multiple top level windows. In this app the twist is that the multiple windows aren’t top level windows but child windows of a Tab Control.

I guarantee this app’s code will work no matter whether it is built x86 or x64 with Microsoft’s compilers or GCC (Mingw). I’ve tested with VC15 from Visual Studio 2008 and VC19 from Visual Studio 2015. In terms of GCC I’ve tested with TDM-GCC 4.9 and TDM-GCC 4.4.1.

Changing topics slightly, one of the biggest difficulties in writing large projects is handling complexity. It is for that reason that I don’t use Petzold’s switch logic to map Windows messages to the code which handles that message. I use a for loop which iterates through an object which relates the numeric value of a message (it’s a #define) to the address of the message handling procedure/code. I have tutorial material written up about that here…

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

The effect of this is to help modularize the code, which helps a lot with the complexity issue. On the second cut through, in an app with multiple windows, such as this one where each tab is essentially a separate window with its own registered Window Class, the code for each window can be kept in separate *.cpp and *.h files. The idea then is to use the linker hard to pull it all together into the finished binary. That’s what I’ve done here. So this project now consists of these files…

1
2
3
4
5
6
7
8
Main.cpp
TabDemo.h
Motor.cpp
Motor.h
Site.cpp
Site.h
Project.cpp
Project.h


If you use an IDE to build the project such as Visual Studio or CodeBlocks you’ll need to copy the code from here with the files named as above and include them in the project. Here is a listing of just the includes and #defines from the project, as well as the build results for all the different compilation/linkages I’ve done…

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
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /FeTabDemo.exe TCLib.lib kernel32.lib user32.lib comctl32.lib
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /MT /FeTabDemo.exe kernel32.lib user32.lib comctl32.lib
// g++ Main.cpp Motor.cpp Site.cpp Project.cpp -lKernel32 -luser32 -lComctl32 -oTabDemo.exe -mwindows -m64 -s -Os
//   6,144 Bytes VC19 (VStudio 2015) x86 UNICODE TCLib Linkage
//   8,192 Bytes VC19 (VStudio 2015) x64 UNICODE TCLib Linkage
//  11,776 Bytes TDM-GCC 4.4.1 (circa 2009) x86 UNICODE msvcrt.dll Linkage
//  44,032 Bytes TDM-GCC 4.9 x64 UNICODE msvcrt.dll Linlage
//  98,816 Bytes VC19 (Visual Studio 2015) x86 UNICODE LIBCMT.LIB Linkage
// 115,200 Bytes VC19 (Visual Studio 2015) x64 UNICODE LIBCMT Linkage
//#define TCLib
//#define Debug
//#define OLD_GCC
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
#include <commctrl.h>
#ifdef TCLib
   #include "stdio.h"
   #include "tchar.h"
   extern "C" int _fltused = 0;
#else
   #include <cstdio>
   #include <tchar.h>
#endif
#include "TabDemo.h"
#include "Motor.h"
#include "Site.h"
#include "Project.h"
#ifdef Debug
  FILE* fp = NULL;
#endif 


The smallest build of 6,144 bytes was for x86 using my custom C Runtime Library which I coded myself named TCLib.lib. That only works for Microsoft compilers. For x64 it was 8,192 bytes. All numbers are for stand alone builds not requiring setup programs or redistributables. After that my trusty old TDM-GCC from the 10.05 Code::Blocks Release circa 2008 or so came in about 12 k – just a bit bigger than my own TCLib linked code. Reason for that is I built my lib in a somewhat similar manner to what Mingw did. After that they get pretty bloated, which is the way folks want to produce code nowadays. I guess they get paid by the byte of code generated or something.

There’s a #define Debug which if uncommented causes the app to generate a ot of log file data to show what’s going on. The #define OLD_GCC was necessary to get the code to run with my circa 2008 GCC installation, as with that InitCommonControlsEx() didn’t exist, so I had to use just InitCommomControls(). You’ll see that in the code.

There are no global variables in any of my code. I don’t believe in using them. I take a strongly object oriented view of my Windows apps, where all data which an app uses is in some way related to some specific window object. To show how I organize this note in TabDemo.h an AppData object is defined…

1
2
3
4
5
6
7
8
9
10
struct       AppData
{
 int         iData1;
 int         iData2;
 int         iData3;
 int         iData4;
 double      dblData1;
 double      dblData2; 
 TCHAR       szData[32];
};


In the Constructor function for the main application object, which is fnWndProc_OnCreate(), I use Windows HeapAlloc() to allocate memory for one of these out of the heap, and I store a pointer to that memory within the main window’s instantiated memory using the WNDCLASS::cbWndExtra bytes. I set a few of the members there, and when the Destructor for the main window is called – fnWndProc_OnDestroy(), if #Debug is defined, those values set in the Constructor are output to the log file. This setup shows how state data can be persisted across member function calls without using global variables, or even statics, which in my mind are just cheap globals.

I’ll start posting the code. Main.cpp is larger than the 8,192 byte limit for a post here, so I’ll have to break that up into two posts....
Last edited on
//Main.cpp
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
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /FeTabDemo.exe TCLib.lib kernel32.lib user32.lib comctl32.lib
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /MT /FeTabDemo.exe kernel32.lib user32.lib comctl32.lib
// g++ Main.cpp Motor.cpp Site.cpp Project.cpp -lKernel32 -luser32 -lComctl32 -oTabDemo.exe -mwindows -m64 -s -Os
//   6,144 Bytes VC19 (VStudio 2015) x86 UNICODE TCLib Linkage
//   8,192 Bytes VC19 (VStudio 2015) x64 UNICODE TCLib Linkage
//  11,776 Bytes TDM-GCC 4.4.1 (circa 2009) x86 UNICODE msvcrt.dll Linkage
//  44,032 Bytes TDM-GCC 4.9 x64 UNICODE msvcrt.dll Linlage
// 115,200 Bytes VC19 (Visual Studio 2015) x64 UNICODE LIBCMT Linkage
//  98,816 Bytes VC19 (Visual Studio 2015) x86 UNICODE LIBCMT.LIB Linkage
//#define TCLib
//#define Debug
//#define OLD_GCC
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
#include <commctrl.h>
#ifdef TCLib
   #include "stdio.h"
   #include "tchar.h"
   extern "C" int _fltused = 0;
#else
   #include <cstdio>
   #include <tchar.h>
#endif
#include "TabDemo.h"
#include "Motor.h"
#include "Site.h"
#include "Project.h"
#ifdef Debug
  FILE* fp = NULL;
#endif


LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
 TCHAR* szTabs[]={(TCHAR*)_T("Motor Controller Parameters"), (TCHAR*)_T("Site Data"), (TCHAR*)_T("Project Start")};
 CREATESTRUCT* pCreateStruct=NULL;
 AppData* pAppData=NULL;
 BOOL blnReturn=FALSE;
 HWND hTabCtl=NULL;
 HWND hTabs=NULL;
 WNDCLASS wc;
 TCITEM tie;

 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 Wea.hInst=pCreateStruct->hInstance;
 #ifdef Debug
 fp=fopen("Output.txt","w");
 if(!fp)
    return -1;
 fprintf(fp,"Entering fnWndProc_OnCreate()\n");
 fprintf(fp,"  Wea.hWnd       = %p\n",Wea.hWnd);
 #endif
 pAppData=(AppData*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(AppData));
 if(!pAppData)
 {
    #ifdef Debug
    fclose(fp);
    #endif
    return -1;
 }
 SetWindowLongPtr(Wea.hWnd,0*sizeof(void*),(LONG_PTR)pAppData);
 pAppData->iData1=1, pAppData->iData2=2, pAppData->iData3=3, pAppData->iData4=4;
 pAppData->dblData1=3.14159;
 pAppData->dblData2=237000;
 _tcscpy(pAppData->szData,_T("Persisted Application Data"));
 #ifdef OLD_GCC
    InitCommonControls();
    blnReturn=TRUE;
 #else
    INITCOMMONCONTROLSEX icx;
    icx.dwSize=sizeof(icx);
    icx.dwICC=ICC_TAB_CLASSES;
    blnReturn=InitCommonControlsEx(&icx);
 #endif
 #ifdef Debug
 fprintf(fp,"  blnReturn      = %d\n",blnReturn);
 fprintf(fp,"  pAppData       = %p\n",pAppData);
 #endif
 if(blnReturn)
 {
    hTabCtl=CreateWindow(WC_TABCONTROL, _T(""),WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,10,10,465,300,Wea.hWnd,(HMENU)IDC_TAB,Wea.hInst,NULL);
    #ifdef Debug
    fprintf(fp,"  hTabCtl        = %p\n",hTabCtl);
    fprintf(fp,"  Wea.hInst      = %p\n",Wea.hInst);
    #endif

    // Register Tab Classes
    wc.lpszClassName=szTabs[0];                  wc.lpfnWndProc=fnWndProc_MotorController;
    wc.style=0;                                  wc.hIcon=NULL;
    wc.hInstance=Wea.hInst;                      wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
    wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
    RegisterClass(&wc);

    wc.lpszClassName=szTabs[1];                  wc.lpfnWndProc=fnWndProc_SiteData;
    wc.style=0;                                  wc.hIcon=NULL;
    wc.hInstance=Wea.hInst;                      wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=2*sizeof(void*);
    wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
    RegisterClass(&wc);

    wc.lpszClassName=szTabs[2];                  wc.lpfnWndProc=fnWndProc_ProjectStart;
    wc.style=0;                                  wc.hIcon=NULL;
    wc.hInstance=Wea.hInst;                      wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=sizeof(void*);
    wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
    RegisterClass(&wc);

    for(size_t i=0; i<sizeof(szTabs)/sizeof(szTabs[0]); i++)
    {
        tie.mask    = TCIF_TEXT | TCIF_IMAGE;
        tie.iImage  = -1;
        tie.pszText = szTabs[i];
        TabCtrl_InsertItem(hTabCtl, i, &tie);
    }

    hTabs=CreateWindow(szTabs[0],szTabs[0],WS_CHILD|WS_VISIBLE|WS_BORDER,5,25,450,250,hTabCtl,(HMENU)IDW_MOTOR_CONTROLLER_TAB,Wea.hInst,0);
    #ifdef Debug
    fprintf(fp,"  hTabs(MotorController) = %p\n",hTabs);
    #endif
    hTabs=CreateWindow(szTabs[1],szTabs[1],WS_CHILD|WS_VISIBLE|WS_BORDER,0,0,0,0,hTabCtl,(HMENU)IDW_SITE_DATA_TAB,Wea.hInst,0);
    #ifdef Debug
    fprintf(fp,"  hTabs(SiteData)        = %p\n",hTabs);
    #endif
    hTabs=CreateWindow(szTabs[2],szTabs[2],WS_CHILD|WS_VISIBLE|WS_BORDER,0,0,0,0,hTabCtl,(HMENU)IDW_PROJECT_START_TAB,Wea.hInst,0);
    #ifdef Debug
    fprintf(fp,"  hTabs(PrijectStart)    = %p\n",hTabs);
    #endif
 }
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");
 #endif

 return 0;
}


LRESULT CALLBACK fnWndProc_OnCommand(WndEventArgs& Wea)
{
 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnCommand()\n");
 fprintf(fp,"  Wea.hWnd = %p\n",Wea.hWnd);
 fprintf(fp,"Leaving fnWndProc_OnCommand()\n\n");
 #endif

 return 0;
}


continued Main.cpp....
// 2nd part Main.cpp...

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
LRESULT CALLBACK fnWndProc_OnNotify(WndEventArgs& Wea)
{
 LPNMHDR lpNmhdr;
 HWND hTab=NULL;
 int iTab=0;

 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnNotify()\n");
 fprintf(fp,"  Wea.hWnd          = %p\n",Wea.hWnd);
 #endif
 lpNmhdr=(LPNMHDR)Wea.lParam;
 #ifdef Debug
 fprintf(fp,"  lpNmhdr->hwndFrom = %p\n",lpNmhdr->hwndFrom);
 fprintf(fp,"  IDC_TAB           = %d\n",IDC_TAB);
 #endif
 if(lpNmhdr->idFrom==IDC_TAB)
 {
     #ifdef Debug
     fprintf(fp,"  lpNmhdr->code     = %u\n",lpNmhdr->code);
     #endif
     switch(lpNmhdr->code)
     {
       case TCN_SELCHANGING:
         iTab=TabCtrl_GetCurFocus(lpNmhdr->hwndFrom);
         #ifdef Debug
         _ftprintf(fp,_T("  Got TCN_SELCHANGING Message!\n"));
         _ftprintf(fp,_T("  iTab              = %d\n"),iTab);
         #endif
         switch(iTab)
         {
           case 0:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_MOTOR_CONTROLLER_TAB);   // hide Tab01
             #ifdef Debug
             _ftprintf(fp,_T("  hTab01            = %p\n"),hTab);
             #endif
             break;
           case 1:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_SITE_DATA_TAB);          // hide Tab02
             #ifdef Debug
             _ftprintf(fp,_T("  hTab02            = %p\n"),hTab);
             #endif
             break;
           case 2:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_PROJECT_START_TAB);      // hide Tab03
             #ifdef Debug
             _ftprintf(fp,_T("  hTab03            = %p\n"),hTab);
             #endif
             break;
         }
         SetWindowPos(hTab,HWND_BOTTOM,0,0,0,0,SWP_HIDEWINDOW);
         break;
       case TCN_SELCHANGE:
         #ifdef Debug
         _ftprintf(fp,_T("  Got TCN_SELCHANGE Message!\n"));
         #endif
         iTab=TabCtrl_GetCurFocus(lpNmhdr->hwndFrom);
         switch(iTab)
         {
           case 0:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_MOTOR_CONTROLLER_TAB);    // hide Tab01
             #ifdef Debug
             _ftprintf(fp,_T("  hTab01            = %p\n"),hTab);
             #endif
             SetWindowPos(hTab,HWND_TOP,5,25,441,200,SWP_SHOWWINDOW);
             break;
           case 1:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_SITE_DATA_TAB);           // hide Tab02
             #ifdef Debug
             _ftprintf(fp,_T("  hTab02            = %p\n"),hTab);
             #endif
             SetWindowPos(hTab,HWND_TOP,5,25,441,200,SWP_SHOWWINDOW);
             break;
           case 2:
             hTab=GetDlgItem(lpNmhdr->hwndFrom,IDW_PROJECT_START_TAB);       // hide Tab03
             #ifdef Debug
             _ftprintf(fp,_T("  hTab03            = %p\n"),hTab);
             #endif
             SetWindowPos(hTab,HWND_TOP,5,25,441,200,SWP_SHOWWINDOW);
             break;
         }
         #ifdef Debug
         _ftprintf(fp,_T("  iTab              = %d\n"),iTab);
         #endif
         InvalidateRect(hTab,NULL,FALSE);
         break;
     }
 }
 #ifdef Debug
 fprintf(fp,"Leaving fnWndProc_OnNotify()\n\n");
 #endif

 return 0;
}


LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 AppData* pAppData=NULL;
 HWND hTabCtrl=NULL;
 HWND hTab=NULL;
 int blnFree=0;

 #ifdef Debug
 fprintf(fp,"Entering fnWndProc_OnDestroy()\n");
 fprintf(fp,"  Wea.hWnd = %p\n",Wea.hWnd);
 #endif
 hTabCtrl=GetDlgItem(Wea.hWnd,IDC_TAB);
 hTab=GetDlgItem(hTabCtrl,IDW_MOTOR_CONTROLLER_TAB);
 if(hTab)
    DestroyWindow(hTab);
 hTab=GetDlgItem(hTabCtrl,IDW_SITE_DATA_TAB);
 if(hTab)
    DestroyWindow(hTab);
 hTab=GetDlgItem(hTabCtrl,IDW_PROJECT_START_TAB);
 if(hTab)
    DestroyWindow(hTab);
 pAppData=(AppData*)GetWindowLongPtr(Wea.hWnd,0*sizeof(void*));
 if(pAppData)
 {
    #ifdef Debug
    fprintf(fp,"  pAppData->iData1   = %d\n",pAppData->iData1);
    fprintf(fp,"  pAppData->iData2   = %d\n",pAppData->iData2);
    fprintf(fp,"  pAppData->iData3   = %d\n",pAppData->iData3);
    fprintf(fp,"  pAppData->iData4   = %d\n",pAppData->iData4);
    fprintf(fp,"  pAppData->dblData1 = %f\n",pAppData->dblData1);
    fprintf(fp,"  pAppData->dblData2 = %f\n",pAppData->dblData2);
    _ftprintf(fp,_T("  pAppData->szData   = %s\n"),pAppData->szData);
    #endif
    blnFree=HeapFree(GetProcessHeap(),0,pAppData);
 }
 #ifdef Debug
 fprintf(fp,"  pAppData = %p\n",pAppData);
 fprintf(fp,"  blnFree  = %d\n",blnFree);
 fprintf(fp,"Leaving fnWndProc_OnDestroy()");
 fclose(fp);
 #endif
 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].Code==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("Form1");
 HWND hWnd=NULL;
 WNDCLASSEX wc;
 MSG msg;

 memset(&wc,0,sizeof(wc));
 wc.hInstance=hIns;                       wc.lpszClassName=szClassName;
 wc.lpfnWndProc=fnWndProc;                wc.style=CS_HREDRAW|CS_VREDRAW;
 wc.hIcon=NULL;                           wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.lpszMenuName=NULL;                    wc.cbClsExtra=0;
 wc.cbWndExtra=sizeof(void*);             wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;
 wc.cbSize=sizeof(WNDCLASSEX);
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,_T("TabDemo"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,400,400,500,360,HWND_DESKTOP,0,hIns,0);
 if(hWnd)
 {
    while(GetMessage(&msg,NULL,0,0))
    {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
    }
 }

 return msg.wParam;
}

TabDemo.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
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
#ifndef TabDemo_h
#define TabDemo_h

#define  dim(x)                             (sizeof(x) / sizeof(x[0])) 
#define IDC_TAB                             12000
#define IDW_MOTOR_CONTROLLER_TAB            13000
#define IDW_SITE_DATA_TAB                   14000
#define IDW_PROJECT_START_TAB               15000

#define IDC_MAX_ACCELERATION                15000
#define IDC_MAX_SPEED1                      15005
#define IDC_MAX_SPEED2                      15010
#define IDC_PORT_NUMBERS                    15015


struct                                      AppData
{
 int                                        iData1;
 int                                        iData2;
 int                                        iData3;
 int                                        iData4;
 double                                     dblData1;
 double                                     dblData2; 
 TCHAR                                      szData[32];
};

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


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


LRESULT CALLBACK fnWndProc_OnCreate         (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnCommand        (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnNotify         (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy        (WndEventArgs& Wea);


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


LRESULT CALLBACK MotorController_OnCreate   (WndEventArgs& Wea);
LRESULT CALLBACK MotorController_OnCommand  (WndEventArgs& Wea);
LRESULT CALLBACK MotorController_OnNotify   (WndEventArgs& Wea);
LRESULT CALLBACK MotorController_OnDestroy  (WndEventArgs& Wea);


const EVENTHANDLER                          MotorController_EventHandler[]=
{
 {WM_CREATE,                                MotorController_OnCreate},
 {WM_COMMAND,                               MotorController_OnCommand},
 {WM_NOTIFY,                                MotorController_OnNotify},
 {WM_DESTROY,                               MotorController_OnDestroy}
};

LRESULT CALLBACK SiteData_OnCreate          (WndEventArgs& Wea);
LRESULT CALLBACK SiteData_OnCommand         (WndEventArgs& Wea);
LRESULT CALLBACK SiteData_OnNotify          (WndEventArgs& Wea);
LRESULT CALLBACK SiteData_OnDestroy         (WndEventArgs& Wea);


const EVENTHANDLER                          SiteData_EventHandler[]=
{
 {WM_CREATE,                                SiteData_OnCreate},
 {WM_COMMAND,                               SiteData_OnCommand},
 {WM_NOTIFY,                                SiteData_OnNotify},
 {WM_DESTROY,                               SiteData_OnDestroy}
};

LRESULT CALLBACK ProjectStart_OnCreate      (WndEventArgs& Wea);
LRESULT CALLBACK ProjectStart_OnCommand     (WndEventArgs& Wea);
LRESULT CALLBACK ProjectStart_OnNotify      (WndEventArgs& Wea);
LRESULT CALLBACK ProjectStart_OnDestroy     (WndEventArgs& Wea);


const EVENTHANDLER                          ProjectStart_EventHandler[]=
{
 {WM_CREATE,                                ProjectStart_OnCreate},
 {WM_COMMAND,                               ProjectStart_OnCommand},
 {WM_NOTIFY,                                ProjectStart_OnNotify},
 {WM_DESTROY,                               ProjectStart_OnDestroy}
};

#endif 


// Motor.cpp
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
// Motor.cpp
//#define TCLib
//#define Debug
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif   
#include <windows.h>
#include <commctrl.h>
#ifdef TCLib
   #include "stdio.h"
   #include "tchar.h"
#else
   #include <cstdio>
   #include <tchar.h>
#endif 
#include "TabDemo.h"
#include "Motor.h"
#ifdef Debug
   extern FILE* fp;
#endif


LRESULT CALLBACK MotorController_OnCreate(WndEventArgs& Wea)
{
 CREATESTRUCT* pCreateStruct=NULL;
 TCHAR szBuffer[64],szTmp[16];
 HWND hCtl=NULL;

 #ifdef Debug 
 fprintf(fp,"  Entering MotorController_OnCreate()\n");
 fprintf(fp,"    Wea.hWnd   = %p\n",Wea.hWnd);
 #endif
 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 Wea.hInst=pCreateStruct->hInstance;
 #ifdef Debug
 fprintf(fp,"    Wea.hInst  = %p\n",Wea.hInst);
 #endif
 hCtl=CreateWindowEx(0,_T("static"),_T("Max Acceleration"),WS_CHILD|WS_VISIBLE,10,40,150,25,Wea.hWnd,(HMENU)-1,Wea.hInst,0);
 hCtl=CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,170,40,50,25,Wea.hWnd,(HMENU)IDC_MAX_ACCELERATION,Wea.hInst,0);
 hCtl=CreateWindowEx(0,_T("static"),_T("Max Speed"),WS_CHILD|WS_VISIBLE,10,80,100,25,Wea.hWnd,(HMENU)-1,Wea.hInst,0);
 hCtl=CreateWindowEx(0,_T("combobox"),_T(""),WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST|WS_VSCROLL,170,80,225,125,Wea.hWnd,(HMENU)IDC_MAX_SPEED2,Wea.hInst,0);
 for(size_t i=0; i<10; i++)
 {
     _stprintf(szTmp,_T("%u"),(i+1)*100);
     _tcscpy(szBuffer,szTmp);
     _tcscat(szBuffer,_T(" Revolutions Per Minute"));     
     SendMessage(hCtl,CB_INSERTSTRING,(WPARAM)-1,(LPARAM)szBuffer);
 }
 #ifdef Debug
 fprintf(fp,"  Leaving MotorController_OnCreate()\n");
 #endif
 
 return 0;
}


LRESULT CALLBACK MotorController_OnCommand(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK MotorController_OnNotify(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK MotorController_OnDestroy(WndEventArgs& Wea)
{
 #ifdef Debug
 fprintf(fp,"  Entering MotorController_OnDestroy()\n");
 fprintf(fp,"    Wea.hWnd = %p\n",Wea.hWnd);
 #endif
 #ifdef Debug
 fprintf(fp,"  Leaving MotorController_OnDestroy()\n");
 #endif
 
 return 0;
}


LRESULT CALLBACK fnWndProc_MotorController(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 for(size_t i=0; i<dim(MotorController_EventHandler); i++)
 {
     if(MotorController_EventHandler[i].Code==msg)
     {
        WndEventArgs Wea;
        Wea.hWnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
        return (*MotorController_EventHandler[i].fnPtr)(Wea);
     }
 } 

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


// Motor.h
1
2
3
4
5
6
#ifndef Motor_h
#define Motor_h

LRESULT CALLBACK fnWndProc_MotorController(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

#endif 

Last edited on
// Project.cpp
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
// Project.cpp
//#define TCLib
//#define Debug
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
#include <commctrl.h>
#ifdef TCLib
   #include "stdio.h"
   #include "tchar.h"
#else
   #include <cstdio>
   #include <tchar.h>
#endif
#include "TabDemo.h"
#include "Project.h"
#ifdef Debug
   extern FILE* fp;
#endif


LRESULT CALLBACK ProjectStart_OnCreate(WndEventArgs& Wea)
{
 CREATESTRUCT* pCreateStruct=NULL;
 HWND hCtl=NULL;

 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 Wea.hInst=pCreateStruct->hInstance;
 hCtl=CreateWindowEx(0,_T("static"),_T("This Is The Project - Start Tab"),WS_CHILD|WS_VISIBLE,125,85,250,25,Wea.hWnd,(HMENU)-1,Wea.hInst,0);

 return 0;
}


LRESULT CALLBACK ProjectStart_OnCommand(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK ProjectStart_OnNotify(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK ProjectStart_OnDestroy(WndEventArgs& Wea)
{
 #ifdef Debug
 fprintf(fp,"  Entering ProjectStart_OnDestroy()\n");
 fprintf(fp,"    Wea.hWnd = %p\n",Wea.hWnd);
 #endif
 #ifdef Debug
 fprintf(fp,"  Leaving ProjectStart_OnDestroy()\n");
 #endif

 return 0;
}


LRESULT CALLBACK fnWndProc_ProjectStart(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 for(size_t i=0; i<dim(ProjectStart_EventHandler); i++)
 {
     if(ProjectStart_EventHandler[i].Code==msg)
     {
        WndEventArgs Wea;
        Wea.hWnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
        return (*ProjectStart_EventHandler[i].fnPtr)(Wea);
     }
 }

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




// Project.h
1
2
3
4
5
6
#ifndef Project_h
#define Project_h

LRESULT CALLBACK fnWndProc_ProjectStart(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

#endif 



// Site.cpp
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
// Site.cpp
//#define TCLib
//#define Debug
#ifndef UNICODE
   #define UNICODE
#endif
#ifndef _UNICODE
   #define _UNICODE
#endif
#include <windows.h>
#include <commctrl.h>
#ifdef TCLib
   #include "stdio.h"
   #include "tchar.h"
#else
   #include <cstdio>
   #include <tchar.h>
#endif
#include "TabDemo.h"
#include "Site.h"
#ifdef Debug
   extern FILE* fp;
#endif


LRESULT CALLBACK SiteData_OnCreate(WndEventArgs& Wea)
{
 CREATESTRUCT* pCreateStruct=NULL;
 HWND hCtl=NULL;

 pCreateStruct=(CREATESTRUCT*)Wea.lParam;
 Wea.hInst=pCreateStruct->hInstance;
 hCtl=CreateWindowEx(0,_T("static"),_T("This Is The Site Data Tab"),WS_CHILD|WS_VISIBLE,140,85,250,25,Wea.hWnd,(HMENU)-1,Wea.hInst,0);

 return 0;
}


LRESULT CALLBACK SiteData_OnCommand(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK SiteData_OnNotify(WndEventArgs& Wea)
{
 return 0;
}


LRESULT CALLBACK SiteData_OnDestroy(WndEventArgs& Wea)
{
 #ifdef Debug
 fprintf(fp,"  Entering SiteData_OnDestroy()\n");
 fprintf(fp,"    Wea.hWnd = %p\n",Wea.hWnd);
 #endif
 #ifdef Debug
 fprintf(fp,"  Leaving SiteData_OnDestroy()\n");
 #endif

 return 0;
}


LRESULT CALLBACK fnWndProc_SiteData(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 for(size_t i=0; i<dim(SiteData_EventHandler); i++)
 {
     if(SiteData_EventHandler[i].Code==msg)
     {
        WndEventArgs Wea;
        Wea.hWnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
        return (*SiteData_EventHandler[i].fnPtr)(Wea);
     }
 }

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


// Site.h
1
2
3
4
5
6
7
// Site.h
#ifndef Site_h
#define Site_h

LRESULT CALLBACK fnWndProc_SiteData(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

#endif 

OMG Freddie,
I am deeply indebted to you!

I am expecting delivery of my C++ book, by Stroustrup as recommended by Thomas, this evening. Armed with that, the online Petzold book and a smattering of my rusty previous API programming I should hopefully be able to find my way around and thoroughly understand what you have been so kind to provide me with.

I now plan an heavy night of programming and VS project setups!

Very many thanks once again!

Ian
Hi Fred,
I am having a few problems getting your above code to compile and link properly in VStudio 2015.

I have tried to diagnose and fix these on my own so that I start getting used to the IDE and at the same time start to become familiar with your program which as you say is logically structured

I have sent you a personal email as I wanted to include a screen dump so that you can see how I have included all the .cpp and .h files.

I got rid of the VStudio unsafe code error messages by inserting:

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

at the top of a couple of header files and the main.cpp

However, I am now left with an unresolved external symbol error message from the linker saying that it can't resolve the reference to

Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol __imp__InitCommonControlsEx@4 referenced in function "long __stdcall fnWndProc_OnCreate(struct WndEventArgs &)" (?fnWndProc_OnCreate@@YGJAAUWndEventArgs@@@Z)


I have about run out of ideas and wonder whether you would be so kind as to give me your considered opinion.

If it would be easier to compile and link from the command line then I am happy to try that but would again need a bit of advice. That's always assuming that I can find a compiler and linker that would do the job as all that I have at present came when I installed VStudio 2015.

Hell, I'm rusty!
Ian




Hi Ian,

you need to add Comctl32.lib to your project. Easiest way is #pragma comment(lib, "Comctl32.lib") in main.cpp.
Might be a good idea to set up an account on DropBox if you don't have one and post the comlete project there. Loading the project into VS and hitting F5 to run is very often the asiest way to see where the problem is.
The InitCommonControlsEx() function is in one of the core Windows libraries named comctl32.lib. I didn't think it would be necessary for me to mention that when I posted the code as I assummed the Visual Studio IDE would include that library as part of the typical set of libraries to be linked against when a Win32 GUI Project is set up. I'm almost certain that used to be the case, but perhaps somewhere along the line they changed stuff. They've been known to do that. You'll note that library listed in the command line compilation strings I posted at the top of Main.cpp...

1
2
3
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /FeTabDemo.exe TCLib.lib kernel32.lib user32.lib comctl32.lib
// cl Main.cpp Motor.cpp Site.cpp Project.cpp /O1 /Os /GS- /GR- /MT /FeTabDemo.exe kernel32.lib user32.lib comctl32.lib
// g++ Main.cpp Motor.cpp Site.cpp Project.cpp -lKernel32 -luser32 -lComctl32 -oTabDemo.exe -mwindows -m64 -s -Os 


To set it up in the IDE you have to go up to the main menu to the Project Properties. That of course opens up a rather large and complicated interface component. You should be able to find a Linker Options somewhere. In there somewhere will be some kind of interface/dialog where you'll have to enter comctl32.lib into the series of libraries the build system will link against. There should be others there such as kernel32.lib, user32.lib, and maybe a few others.

At least I hope that is what the issue is Ian. Let me know how it goes. I want it to work for you.

Yes, to get rid of those warnings when building in the IDE one must add CRT_SECURE_NO_WARNINGS in a window somewhere. There's also something to the effect NONCONFORMING_SWPRINTFs or something like that. Alternately, you could set the warning level down to 1. It defaults to Max (4) I believe. Or, one could use the 'secure' string manipulation functions. Half the time when I use them they crash my apps. That's why I gave up on them.
@freddie1,

these are default libs in VS 2015 CE:
kernel32.lib;
user32.lib;
gdi32.lib;
winspool.lib;
comdlg32.lib;
advapi32.lib;
shell32.lib;
ole32.lib;
oleaut32.lib;
uuid.lib;
odbc32.lib;
odbccp32.lib;
Hi Fred,
I do have a DropBox Account I didn't think of uploading the entire project as I can tell that you are not impressed with VStudio!

And by the way, I was just about to post an apology for my poor choice of words, the compiler warning message said The function ****MAY**** be unsafe, not that it WAS unsafe!

The #pragma comment(lib, "Comctl32.lib") has sorted it so that I now have a Tab Demo Window with three tabbed forms titled Motor Contoller Parameters, Site Data and Project Start.

That's brilliant thanks, now I can really start delving in and adding a few bits and pieces to start fleshing it all out.

Regards
Ian

Thanks for posting that Thomas. I see comctl32.lib isn't there, so I'm suspecting my analysis of the issue was correct, and the code should build OK once that lib is added.

In the way of background conceptual information, having unresolved externals is a common plague to programmers. There isn't any line number associated with that error as it isn't generated by the compiler, but rather by the linker. What happens is that a header file will be present where the compiler finds function declarations for various functions, and the compiler is happy with that. When the link stage comes in the linker starts looking through the various libraries for the functions that weren't defined in the *.cpp/*.obj files, because it wants to pull that executable code into the binary being created. When it has gone through all the *.obj files and *.lib files and still hasn't 'resolved' that function call, it finally emits an 'unresolved external' error.

The thing to do to avoid these is to know which libraries various functions you are using are in. You need to understand both the headers and the libraries required. Mioght sound hard but it really isn't. The comctl32.lib is a real common one. It contains all the 'Common Controls', which are things like the Tab Control, Tree View Control, Progress Bar Control, and a bunch of others. These are basically controls that are just a bit more sophisticated than the Standard Windows Controls such as labels, text boxes, and combo boxes. To find where in the documentation one is told which libraries are needed for a specific function call, look up InitCommonControlsEx() on MSDN, and near the bottom it will tell you which library the code is in.
C Family languages aren't usually known for making things easy. If one were building that app in PowerBASIC, which is a very high performance dialect of Basic which creates Win32 exes or dlls, including the following would be enough to use InitCommonControlsEx() ...

 
Declare Function InitCommonControlsEx Lib "ComCtl32.dll" Alias "InitCommonControlsEx" (icc AS INIT_COMMON_CONTROLSEX) As Long


In that build system the location of code for linking is specified right in the function declaration.
Hi Fred,
Just wanted to let you know that I have not disappeared completely but I have been working on a "rush job", nothing to do with C++ programming, so I have not had time to work on this. Spending most of this weekend on it but need to spend time with the family as well so please be patient.

The rush job is on the hardware side of this project anyway!

Regards
Ian
Pages: 123