Code Review Please

Pages: 12
Regarding C++ and the Windows API

It is more usual to use a GUI toolkit to program for Windows with C++ -- MFC or WTL/ATL, in the Microsoft world; Qt, wxWidgets, ... elsewhere.

But it is possible to "concoct" you own Windows class, if you wish to. I've only done this for educational purposes, but the trickiest part is associating window handles with class instances. ATL does this in a very scary way (injecting code to reroute the WindowProc calls, which knows how to map window handles to ATL window instances; not sure about MFC). I just stored the class instance using SetWindowLongPtr.

That allowed me to write my own message to handle mapping code and avoid the standard C switch statement.
Last edited on
So bottom line: "Should I develop using the Windows 32 API directly like I am trying to, or should I find/use a toolkit of some sort, as you mentioned (MFC or something)?
Regarding text functions:

By the three text function, do you mean TextOut, ExtTextOut and TabbedTextOut? Or PolyTextOut?

Of course, there's also DrawText and DrawTextEx.

While I'm not totall sure, TextOut was probably the orignal function (back in WIN16 days). ExtTextOut has superseded it but is more painful to use (due to the number of params).

According to some references, calls to TextOut are passes through to ExtTextOut, so it more efficient to use ExtTextOut. But DrawText is easier to use for simple text output (as it takes fewer parameters). It also supports only single line text.

According to MSDN, calls to all the text calls eventually end up as calls to the graphics driver's TextOut or ExtTextOut. So if the driver support TextOut, this might not be the case. But I get the impression that TextOut is usually implemented using ExtTextOut.

DrawText (and DrawTextEx) are more efficient for certain operations (e.g. fitting text into a rectangle), and TabbedTextOut and PolyTextOut support specific functionality.

I find I generally use DrawText, except for short strings, for which I use TextOut. The others I use when I have a particular need.
Spending some time getting to understand the WIN32 API is worthwhile, but in my experience either MFC or WTL/ATL is used for commercial application development (in C++).

Outside of Microsoft houses (i.e. companies that use Visual Studio, etc) other toolkits are popular. Esp. Qt, going by job ads. But I only have passing experience of them.

You can't excape using the underlying API even if you use MFC, which is pretty complete. Also, MFC was designed to make it easy for C WIN16/WIN32 programmers to pick up, so it parallels the underlying API in many ways (so much so that many O-O purists dislike it!)

So, I think it would be helpful for you to carry on using the API directly, till you've covered the basic operations. This will prove useful in the future, as all the toolkits rely on the WIN32 API in the end (when running on Windows, that is). At that point you might want to consider your options.

But I would plan to work with a GUI toolkit at some point!

Unfortunately, neither MFC nor ATL (which WTL relies on) are provided with the Express version of VC++.

Andy

P.S. Another toolkit you might want to look at in FLTK. I see Stroustrup uses it to teach GUI programming in one of his books.
Last edited on
What is the most appropriate way to get multiple lines of text to print to the window?
The basic implementation I tried (with various different attempts) was this:
1
2
3
4
5

            const char Windowtext[] = "Testing this out today";
            TextOut(hdc, 5, 5, Windowtext, strlen(Windowtext));
            const char WindowText2[] = "Testing this out tomorrow";
            TextOut(hdc, 5, 5, WindowText2, strlen(WindowText2));


This seemed the most logical, but did not work.
Also what is the difference between 5, 5 and 0, 0. Why not just 0, 0.
Sorry for all the questions, just trying to sort this all out in my head. Thanks for
all the feedback so far, it's been very helpful.
Last edited on
OK:
1
2
3
4
5

            const char Windowtext[] = "Testing this out today";
            TextOut(hdc, 0, 0, Windowtext, strlen(Windowtext));
            const char WindowText2[] = "Testing this out tomorrow";
            TextOut(hdc, 0, 20, WindowText2, strlen(WindowText2));


This works. Is this the proper/best way to handle what I am trying to do. I am going to obviously being using a lot of text. I will have a few chunks of text that are specifically listing out data about the application, then I am going to end up having 20-100 lines of text pertaining to information
on their computer. Is the way listed above the best and most optimal way to handle multi-line text in C++?
OK something else is driving me crazy.
What is the bottom line difference between the original data type I tried to use, the one you presented (as above), and string. I haveerta tried switching them out and can't seem to understand the difference. I have good working code below..but do not understand the difference. The only part of the following example that doesn't work is the string part...it's throwing an error because I tried to build the "string" when it was set as a const char but it threw other errors. This part is confused.

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
#include <windows.h>
#include <iostream>

const char g_szClassName[] = "myWindowClass";

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // Paint Variables
    HDC         hdc ;
    PAINTSTRUCT ps ;
    RECT        rect ;

    switch(msg)
    {
        case WM_PAINT: {
            hdc = BeginPaint (hwnd, &ps) ;
            GetClientRect(hwnd, &rect);

            const char Windowtext[] = "Thank you for using Computer Information Display.";
            TextOut(hdc, 0, 0, Windowtext, strlen(Windowtext));
            const char Windowtext2[] = "Developed By: Joyel Puryear (Infotechnologist.biz)";
            TextOut(hdc, 0, 20, Windowtext2, strlen(Windowtext2));
            const char Windowtext3[] = "Your computer information is listed below:";
            TextOut(hdc, 0, 50, Windowtext3, strlen(Windowtext3));

            // Get OS version.
            std::string Windowtext4 = "Operating System: ";

            OSVERSIONINFO osvi;

            osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

            GetVersionEx (&osvi);

            if ((osvi.dwMajorVersion ==5)&&(osvi.dwMinorVersion==1)) {
                Windowtext4 += Windowtext4 += "Windows XP";
            }else if ((osvi.dwMajorVersion ==5)&&(osvi.dwMinorVersion==0)) {
                Windowtext4 += Windowtext4 += "Windows 2000";
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==0)) {
                Windowtext4 += Windowtext4 += "Windows NT 4.0";
            }else if ((osvi.dwMajorVersion ==3)&&(osvi.dwMinorVersion==51)) {
                Windowtext4 += Windowtext4 += "Windows NT 3.51";
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==90)) {
                Windowtext4 += Windowtext4 += "Windows ME";
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==10)) {
                Windowtext4 += Windowtext4 += "Windows 97";
            }else if((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==0)) {
                Windowtext4 += Windowtext4 += "Windows 95";
            }

            TextOut(hdc, 0, 75, Windowtext4, Windowtext4.length);

            EndPaint (hwnd, &ps);

            return 0 ;
        }
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Step 1: Registering the Window Class
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "The title of my window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}
The handling of multiple lines of text, as in more than one separate string, can be done using TextOut(). I use that approach when I want to place separate bits of text at different places around the window. Or need to position strings esp accurately.

If, however, I want to display a multi-line block of text I switch to DrawText(). TextOut() only supports single line text. DrawText(), with the appropriate flags, handles multi-line text. See MSDN entry for DrawText() for info.

ExtTextOut() will also work, but is that much more complicated. I find I use TextOut() and DrawText() most of the time.

std::string is a string class. To get a const char* to pass to C-API calls, you need to call its c_str() method. See help for std::string::c_str() : e.g.

http://www.cplusplus.com/reference/string/string/c_str()

Also, std::string::length() is a function, so needs a ()

TextOut(hdc, 0, 75, Windowtext4.c_str(), Windowtext4.length());

Regarding the meaning of 0,0 and 5,5 - see my updated version of the code. This should surely be obvious from the MSDN description of the function.

Andy
Last edited on
Perfect, that helps a lot. Thanks again man. This has really helped me figure out a lot about windows. Going to go do some app development now. Thanks again for all the help getting on my feet with some of these concepts.
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
            // Get OS version.
            const char Windowtext4[] = "Operating System: ";

            OSVERSIONINFO osvi;

            osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

            GetVersionEx (&osvi);

            if ((osvi.dwMajorVersion ==5)&&(osvi.dwMinorVersion==1)) {
                strcat(Windowtext4, "Windows XP");
            }else if ((osvi.dwMajorVersion ==5)&&(osvi.dwMinorVersion==0)) {
                strcat(Windowtext4, "Windows 2000");
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==0)) {
                strcat(Windowtext4, "Windows NT 4.0");
            }else if ((osvi.dwMajorVersion ==3)&&(osvi.dwMinorVersion==51)) {
                strcat(Windowtext4, "Windows NT 3.51");
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==90)) {
                strcat(Windowtext4, "Windows ME");
            }else if ((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==10)) {
                strcat(Windowtext4, "Windows 97");
            }else if((osvi.dwMajorVersion ==4)&&(osvi.dwMinorVersion==0)) {
                strcat(Windowtext4, "Windows 95");
            }

            TextOut(hdc, 0, 75, Windowtext4, 100);

How do I get Concat's done on a constant char. I tried doing this with a string using += but it throws an error when trying to use a string as a TextOut parameter...so I tried this instead..the += didn't work, so I looked it up and it said use strcat but that's not working properly either.

Thanks.
If you're writing a little sysinfo tool, check out CodeProject.com, CodeGuru.com, etc for examples. Seems to be one of the standard tools to write (just checked and see C# versions are in vogue...)
From what I keep reading the std::string are C++ data types. All of the char and constant char are C? So shouldn't I try to use only C++ stuff since I am programming in C++, thoughts?
If you program in C++, then it is only logical you use std:string and yet it still depends. Sometimes your C++ program need to use external libraries which are written in C so to call them you need your C char and char* isn't it ?

OK I have a question. I understand most of everything that was explained in this thread. I have taken a lot of what you said to heart. I was able to get the text outputted to the screen..I learnt about the different text functions and their uses. I have greatly expanded my C++ knowledge on this one thread. Now I have one more question that I am really TOTALLY baffled on.

I still do not understand the different between the following:
1
2
3
4
char *Whatever = "Testing this out today";
std:string whatever = "Testing this out today";
const char Whatever[] = "Testing this out today";
const TCHAR szWindowtext[] = _T("Testing this out today");


There are so many..>I am totally lost here. I know that each one has their different uses.

So my question...which one of these "Examples" should I be using for different stuff? Another question is in...generally I would just call "int variablename = 3;
But according to other examples I have seem sometimes people use const in front of int, sometimes they don't. That is another thing. I am familair with constants from PHP, but never in C++. I am very confused. No matter how much I read I can't seem to understand the difference. It seems that you all were recommending me to use the const char Whatever[] = "whatever" example when it came to the function..then others said it was OK to use String but it required more work:

1
2
            std::string tester = "What the hell";
            TextOut(hdc, 0, 100, tester.c_str(), strlen(tester.c_str()));

I really want to sort out in my head which of these ways to create variables I should use for what purposes, and which ones I should avoid. After this, I should be able to put this post to rest. I have gleamed a great deal of information from you here. Once I have this understanding I should be set for awhile. Thanks again for all the help on this board so far.
If I am declaring a constant string to pass to a C-API I declare it as a char array.

const char Whatever[] = "Testing this out today";

or

const TCHAR Whatever[] = _T("Testing this out today");

(the only difference here is that TCHAR will be compiled to either char or wchar_t, depending on the #define _UNICODE (if defined, it's wchar_t))

But if I need to build a string, I would use a std::string (or std::wstring)

std:string whatever = "Testing this out today : ";
whatever += getTodaysDateAsString();
...

I would not use either

std:string whatever = "Testing this out today";

or the equivalent

std:string whatever("Testing this out today");

for just a fixed string. (When operator= is used as part of a variable reclaration statement, it triggers a single parameter constructor, not operator=)

These create a string object, which allocates memory of the heap and then copies the string into the new memory. The char array exists in memory but you have no way of accessing it. So you end up doing an unnecessary copy.

I would also avoid

char *Whatever = "Testing this out today";

as it allocates a non-const pointer to a const array. I think this is something to do with C compatibility (?), but in C++ trying to change a char in this string will probably cause an access violation!

Andy

P.S. I would not use strlen(tester.c_str()) -- it is better to use string.length() instead. Most implementations of string store the string length in a member variable which they can return directly. By calling strlen() against the returned const char*, the function has to re-count the characters. If the string is long, this can cost!



Last edited on
I mark all variable that have a fixed value as const. This allows the compiler to spot if I'm trying to change something I meant to stay the same. It also allows the compiler to make additional assumptions when optimizing, which can result in more optimal machine code.

For further information, read up about "const correctness", e.g.
http://www.parashift.com/c++-faq-lite/const-correctness.html
Topic archived. No new replies allowed.
Pages: 12