Creating a Window

Hey everyone,

Can you please check this code. It creates a window, but I can't do anything with it. Move, resize, minimize, maximize, etc. It just opens the command prompt and wheel on my screen spins. Any help would be appreciated.

Thank you

// ConsoleApplication2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
#include <WinDef.h>
#include <WinBase.h>

using namespace std;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{

bool test;
LRESULT message = NULL;
switch (msg)
{
case WM_CREATE:
{
break;
}

case WM_CLOSE:
{
test = false;
break;
}


default:
{
return DefWindowProc(hwnd, msg, wparam, lparam);
}

return message;
}

class Window
{
public:

bool reg(HWND hwnd);
bool run(bool test, bool running);
bool destroy(UINT msg, bool running)

private:
HINSTANCE hInstance = NULL;
HWND hwnd;
MSG message;
UINT msg = WM_DESTROY;
WPARAM wparam = NULL;
LPARAM lparam = NULL;
bool running = true, test;
LRESULT won = NULL;

};


int main();
{
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hInstancePrev, LPSTR lpCmdLIne, int nSHowCmd);
{
HINSTANCE hInstance = NULL;
HWND hwnd;
MSG message;
UINT msg = WM_CREATE;
WPARAM wparam = NULL;
LPARAM lparam = NULL;
bool running = true, test;
LRESULT won = NULL;

bool reg(hwnd);
bool run(test);
bool destory(message, running);

}
}

bool reg(HWND hwnd);
{
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = NULL;
wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND
wc.lpszMenuName = NULL;
wc.hCursor = NULL;
wc.hInstance = hInstance;
wc.lpszClassName = L"MyClass";
wc.hIconSm = NULL;
wc.hInstance = NULL;

RegisterClassEx(&wc);

hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, L"MyClass", L"When",WS_OVERLAPPEDWINDOW | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, CW_USEDEFAULT,CW_USEDEFAULT, 200, 200, HWND_DESKTOP, NULL, NULL, NULL, NULL);

}

bool run(bool test);
{
test = ShowWindow(HWND hwnd, SW_HIDE)
}

return test;

}

bool destroy(UINT msg, bool running);
{
msg = WM_DESTROY;
running = false;
return msg;
}






PLEASE learn to use code tags, they make reading and commenting on source code MUCH easier.

http://www.cplusplus.com/articles/jEywvCM9/
http://www.cplusplus.com/articles/z13hAqkS/

HINT: you can edit your post and add code tags.


With that said you have a LOT of missing semi-colons, semi-colons where they aren't needed and other problems with your code.
Here's an example of a (semi) minimal Win32 API app:
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
// INCLUDES ====================================================================
#include <windows.h>

// FUNCTION PROTOTYPES =========================================================
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


// entry point for a Windows application =======================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nWinMode)
{
   // define the window class name
   static const TCHAR szAppName[]  = TEXT("MinimalWindowsApp");

   // create an instance of the window class structure
   WNDCLASSEX wc;

   wc.cbSize        = sizeof(WNDCLASSEX);
   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = (HICON)   LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
   wc.hIconSm       = (HICON)   LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
   wc.hCursor       = (HCURSOR) LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.hbrBackground = (HBRUSH)  (COLOR_WINDOW + 1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = szAppName;

   if (0 == RegisterClassEx(&wc))
   {
      MessageBox(NULL, TEXT("Can't Register the Window Class!"), szAppName, MB_OK | MB_ICONERROR);
      return E_FAIL;
   }

   // define the application title
   static const TCHAR szAppTitle[] = TEXT("Win32 API Skeletal Application");

   // create the window
   HWND hwnd = CreateWindow(szAppName, szAppTitle,
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            NULL, NULL, hInstance, NULL);

   // check if the window was created, exit if fail
   if (NULL == hwnd)
   {
      MessageBox(NULL, TEXT("Unable to Create the Main Window!"), szAppName, MB_OK | MB_ICONERROR);
      return E_FAIL;
   }

   // show and update the window
   ShowWindow(hwnd, nWinMode);
   UpdateWindow(hwnd);

   static BOOL bRet;
   static MSG  msg;

   // enter the main message loop
   while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) // 0 = WM_QUIT
   {
      // check for error
      if (-1 == bRet)
      {
         // handle the error and possibly exit

         // for this app simply report error and exit
         MessageBox(NULL, TEXT("Unable to Continue!"), szAppName, MB_OK | MB_ICONERROR);
         return E_FAIL;
      }
      else
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }

   // the app is done, exit normally, return control to Windows
   return (int) msg.wParam;
}


// processes the messages that Windows sends to the application ================
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   // choose which Windows messages you want to use
   switch(message)
   {
   case WM_PAINT:
      HDC         hdc;
      PAINTSTRUCT ps;
      hdc = BeginPaint(hwnd, &ps);

      // draw some text centered in the client area
      RECT rect;
      GetClientRect(hwnd, &rect);
      DrawText(hdc, TEXT("Hello, Windows!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

      EndPaint(hwnd, &ps);
      return S_OK;

   case WM_DESTROY:
      // exit the application
      PostQuitMessage(0);
      return S_OK;
   }

   // let Windows process any unhandled messages
   return DefWindowProc(hwnd, message, wParam, lParam);
}

Notice the program entry point is the WinMain function. With Win32 API apps there is no main function.
For reference, here is another sample of a simple window. It demonstrates the basics. As mentioned, there is no "main" function. There are instead, two functions---"WinMain" and "WndProc". WinMain sets up the initial parameters for the window, and continues to loop, getting keyboard and mouse inputs (while (GetMessage(...). Such messages are processed by the WndProc function. WndProc is where you will enter all of your functional code:

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

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    const wchar_t szAppName[] = L"HelloWin";
    HWND        hwnd;
    MSG         msg;
    WNDCLASSEX  wndclass;

    wndclass.cbSize = sizeof(wndclass);
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(0, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(0, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = 0;
    wndclass.lpszClassName = szAppName;
    wndclass.hIconSm = LoadIcon(0, IDI_APPLICATION);

    RegisterClassEx(&wndclass);

    hwnd = CreateWindow(szAppName,         // window class name
        L"The Hello Program",              // window caption
        WS_OVERLAPPEDWINDOW,               // window style
        CW_USEDEFAULT,                     // initial x position
        CW_USEDEFAULT,                     // initial y position
        CW_USEDEFAULT,                     // initial x size
        CW_USEDEFAULT,                     // initial y size
        0,                                 // parent window handle
        0,                                 // window menu handle
        hInstance,                         // program instance handle
        0);		                           // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, 0, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    RECT        rect;

    switch (iMsg)
    {
    case WM_CREATE:
        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd, &rect);

        DrawText(hdc, L"Hello, Windows 7!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
These GUI templates for a basic 'beginner' type Windows program I see around the internet always seem overly complicated to me. I'm always affronted by how many lines of code they seem to use. What I fear is that beginners are likely 'put off' by them and immediately look for some easier way of creating a program than using the pure SDK Win32 Api approach. In that vein of thought, let me examine the code posted above by FurryGuy - all 111 lines of it, and see if it can be made more concise. First off, as in his line 12....

 
static const TCHAR szAppName[]  = TEXT("MinimalWindowsApp");


...I see no need to put this entity in the program's static/global address space. My predilection is to avoid global entities whenever possible. Here it is possible. szAppName could be an auto in WinMain(). No biggie - just thought I'd mention it.

Next, to make an absolute 'minimal' Windows GUI app, all the many fields of the WNDCLASS or WNDCLASSEX struct don't need to be filled out. For the program to build and run, however poorly, only three are absolutely necessary. They are...

WNDCLASS::lpszClassName
WNDCLASS::lpfnWndProc
WNDCLASS::cbSize

All the others could be set to zero with a memset() call. Such a program will run and function, but window painting will be poor. By filling out one addition field - WNDCLASS::hbrBackground, the program will be visually much more presentable.

Next, lines such as this...

1
2
3
4
5
if (0 == RegisterClassEx(&wc))
   {
      MessageBox(NULL, TEXT("Can't Register the Window Class!"), szAppName, MB_OK | MB_ICONERROR);
      return E_FAIL;
   }


...always seem to be 'overkill' to me. I think it may have been defensible back in 16 bit Windows where memory was nill and memory allocation errors were commonplace, but in 32 bit or 64 bit Windows it won't typically occur. And anyway, if RegisterClassEx() fails, you'll find it out in developing the program. It won't occur in a Release app. For example, if my memory serves me correctly, RegisterClass() will fail if the WNDCLASSEX::cbSize isn't filled out correctly. And if a Window Class doesn't exist because RegisterClass() failed, a CreateWindow() call based on that class will certainly fail too. So what I'm saying is that if I were to present an absolute 'minimal' Windows SDK based program, I'd just call RegisterClassEX() with no error checking, making sure to state the usual caveats about the necessity of error checking in a full production Release app.

I'd say about the same thing about CreateWindow() or CreateWindowEx() calls. I'd remove error checking on the returned HWND. That would eliminate more lines of code.

Finally, this is an interesting one...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static BOOL bRet;
   static MSG  msg;

   // enter the main message loop
   while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) // 0 = WM_QUIT
   {
      // check for error
      if (-1 == bRet)
      {
         // handle the error and possibly exit

         // for this app simply report error and exit
         MessageBox(NULL, TEXT("Unable to Continue!"), szAppName, MB_OK | MB_ICONERROR);
         return E_FAIL;
      }
      else
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }


I just don't know what to say about that. I've been aware of that issue from the beginning of my coding for Windows since Windows 95 in 1995, the issue of GetMessage() returning a -1 if a HWND turns up in the message queene that is an invalid HWND, but I'd have to say I've never had an issue with it in like 26 years of doing this kind of coding. The message loop or 'message pump' as shown in Anachronon's code is what I have always used; it's what is shown in Petzold's books; and it works for me despite the GetMessage() MSDN documentation explicitely stating not to use it. It is what is found in my mission critical enterprise apps (very large complex multi-threaded apps) used by a large state agency that have been running for years and years without errors, and at this point without even support from me because I'm retired.

So, if I were to present a really 'minimal' Windows app to a beginner, I'd be more inclined to present something like the below, with the usual caveats that it needs to be flushed out with error checking on various function calls that conceivably could fail. But it shows the absolute essentials without being encumbered with piles of lines of confusing code....

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
// cl Form1.cpp /O1 /Os /MT /link Kernel32.lib User32.lib 
#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)       // This is the Window Procedure.  The concept of the
 {                         // Window Procedure is the most important concept to
    PostQuitMessage(0);    // grasp in C/C++ WinApi coding.  You never call this
    return 0;              // function with your code; rather, Windows calls it
 }                         // to inform code here of events occurring.  The events
                           // are reported here as messages.
 return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form1"); // This will be both the name of the app and the name of
 WNDCLASSEX wc;                   // the main window's 'Window Class'.  The concept of the
 MSG messages;                    // Window Class and its associated Window Procedure are
 HWND hWnd;                       // the most important concepts in Windows Programming.

 memset(&wc,0,sizeof(wc));                       // zero out WNDCLASSEX wc
 wc.lpszClassName = szClassName;                 // Feed "Form1" into WNDCLASSEX::lpszClassName
 wc.lpfnWndProc   = fnWndProc;                   // Feed pointer ( function address ) to Window Procedure
 wc.cbSize        = sizeof(WNDCLASSEX);          // Set Size;  RegisterClass() Will Fail If This Isn't Set!
 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);    // Set Background HBRUSH (Handle To A BRUSH)
 RegisterClassEx(&wc);                           // Let Operating System know about "Form1" Class
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,300,HWND_DESKTOP,0,hInstance,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))   // The message pump retrieves messages from the program's
 {                                       // message queue with GetMessage(), does some translation
    TranslateMessage(&messages);         // work in terms of character messages, then calls the
    DispatchMessage(&messages);          // Window Procedure associated with the HWND of the message
 }                                       // being processed.  Note that an app can have many Window
                                         // Handles and many Window Procedures.
 return messages.wParam;
}


One other issue and this just involves coding style. I kind of always hated multi-line function calls, such as spreading the CreateWindow() call over multiple lines. Back in the days of DOS with its 80 X 25 character screens is where I think this originated out of necessity. But with GUIs and GUI fonts, and computer screens becoming wider and wider over the years - now with the same aspect ratio as wide screen TVs, I prefer to 'go wide' and minimize lines of code. Just a personal preference to be sure, and maybe not important, but getting back to my original premise about these 'beginner' templates being so overly ponderous, I think a beginner might be somewhat less 'put off' by my template with its 38 lines of code as compared to FurryGuy's with its 111 lines of code.
These GUI templates for a basic 'beginner' type Windows program I see around the internet always seem overly complicated to me.

Windows requires a lot of setup to be a properly formed GUI app, it isn't as easy as C++ for a minimal app.
1
2
3
4
5
6
#include <iostream>

int main()
{
   std::cout << "Hello World!\n";
}

For that matter what could be considered as a truly minimal Windows GUI app could be as simple as:
1
2
3
4
5
6
7
8
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow)
{
   MessageBox(NULL, TEXT("Hello World!"), TEXT("Hello Message"), MB_OK);

   return 0;
}

Doesn't really take advantage of what Windows has to offer, but there it is.

I'm always affronted by how many lines of code they seem to use.

If the APIWin32 didn't require so much boilerplate code for setup it could be as simple and terse as C++. Or as compactly obscure as MFC.

Your code template still takes 38 lines of code. And that is before it adds any actual functionality other than telling the OS how to create, display and interact with the window.

One other issue and this just involves coding style.

Your preferred coding style is yours. Not mine. And evidently not that of more than a few others. That doesn't mean any of these different approaches are inherently wrong and flawed.

You have 38 lines of no error checking code, and I have 111 lines that have some probably overkill error checking. Errors that aren't likely to happen, but if they do, OUCH! There's more going wrong with Windows than simply trying to get an app running.

I learned Win32 API programming using the means and method that resulted in the code I I posted earlier. Comments, split lines and all the "excess initialization" going on. Learned years and years ago. And even now still works for me.

I did say my code was a SEMI minimal example, not truly minimal. If I were to present a code sample to someone who had never tried to program a Win32 app before it likely wouldn't be what the sample I showed earlier.

I don't code for anything other than a hobby, I'm too old to remotely consider a career as a programmer. Especially since I've had zero hours of formal instruction or the hunger to program for a living.

Now, if I had a desire to revisit my boilerplate template bit of code and "do things right" according to your preferred viewpoint I'd not have "too many lines" of code. What would I cut out and what would I leave? Well, that's up for debate and testing. I won't ever say my code sample is perfect. It isn't.

I'd probably just given up the whole idea as being a waste of time and effort to rethink the wheel. A wheel that isn't flat and can still roll.

I've got a template that works for what I want to do. And is minimal for my wants and needs, I don't have to satisfy the criteria of a boss or coworker. That includes excluding error checking from other/earlier versions has bit me in the butt in the past.

I think a beginner might be somewhat less 'put off' by my template with its 38 lines of code

Your approach works great for you. More power to ya. My approach works for me. Why not expose people who want to learn to different ways of doing things?

The OP hasn't been back since he dropped his query. It kinda makes what we are discussing a sideshow of a sideshow.
I'm sorry if I offended you Furry Guy. I certainly didn't mean to run down your code in any way. Heck, I admit its better than mine. From your posts over the years you've helped a lot of folks out and I respect you a lot for that. Was just trying to make a point that I have grave misgivings that these long winded 'Windows Starter Templates' I see proliferating in Microsoft documentation and around the internet serve the interests of folks just trying to get started with Windows coding. I fear most folks look at them and are intimidated by them and then decide to go with some class framework approach that in the end simply hides a thousand times more code in header files and libraries that aren't in the main program source file.


The OP hasn't been back since he dropped his query. It kinda makes what we are discussing a sideshow of a sideshow.


It is kind of aggravating to me when posters don't even have the decency to thank or acknowledge folks who have gone out of their way to help them, but it happens and it is what it is. But some folks do search for information before posting a question, and this discussion here may have more value than you indicated above because the title of the OP's query was 'Creating A Window' - a topic that might come up in a Windows Programming Forum :).

Sometimes a couple years after I've posted voluminous material with full compileable examples on some esoteric topic such as ODBC Database Access or connecting to Excel with COM I'll get a PM from somebody who unearthed it from a search that it really helped big time, so I think the discussion here has at least some merit.
The first rule when coding in WIN32 is to always check the return value (if any) for non-successful completion of the API.

Over the years when I used to write WIN32 programs, I've had errors from RegisterClass(), CreateWindow() et al. Don't assume anything - test!
@freddie1, I wasn't offended, just a bit peeved and annoyed. :)

You wonder about all the error checking on my message pump, right?

You can thank MS/MSDN for that. That convoluted bit of code is straight from their GetMessage write up:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage

And, yes, I had an app during revision testing that tried to continue after the main window had been destroyed. *OUCH!*

Why do I use LoadImage to load icons and cursors? Because LoadIcon and LoadCursor have been deprecated.
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadicona
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadcursora

WNDCLASS has been deprecated as well, but the Win32 docs do say it can still be used if the small icon is not needed.

As with any API function that has been deprecated one future revision might even remove it. C++14 deprecated std::random_shuffle and C++17 removed it.

Why to I cast msg.wParam to int when I return and end the app?

So I can use the same code compiled as x86 or x64 without any warnings of "possible loss of data." Some Windows data types are different sizes depending on x86/x64 compilation.

A WPARAM is defined as an alias for a UINT_PTR. The size of a UINT_PTR varies depending on bitness.
https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#uint_ptr

Visual Studio's IDE makes it easy to set compilation targets, so why not batch compile for x86 AND x64?

The biggest change I would likely make is get rid of the ANSI/Unicode macroing. Win95/98/Me is DEAD. Even WinXP is 100% Unicode. Default encoding with Visual Studio is Unicode.

No more TEXT("") BS. I would have to use L"" though to ensure I have a Unicode string.

OR I can continue using TEXT("") and let the preprocessor chose the correct encoding.

Many Win32 API functions are macros themselves. LoadIcon is actually an "is this ANSI or Unicode (wide string in MS Speak)" macro for LoadIconA or LoadIconW. The two functions differ as to what type of C string is passed to the function. And the string encoding depends on what character encoding the app is compiled to deal with.

*UGH!* Even more complications to creating a Windows GUI app that unfortunately has to be learned and understood, yet will likely make a newbie's eyes glaze over as being TMI.
If you want something less intimidating for a Windows GUI newbie why not try MFC?
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
#include <afxwin.h>

// application class
class CTheApp : public CWinApp
{
public:
   BOOL InitInstance();
};

// main window class
class CMainWnd : public CFrameWnd
{
public:
   CMainWnd();

protected:
   afx_msg void OnPaint();

   DECLARE_MESSAGE_MAP()
};

// construct a window
CMainWnd::CMainWnd()
{
   Create(NULL, __T("An MFC Application Skeleton"));
}

// initalize the application
BOOL CTheApp::InitInstance()
{
   m_pMainWnd = new CMainWnd;

   m_pMainWnd->ShowWindow(m_nCmdShow);
   m_pMainWnd->UpdateWindow();

   return TRUE;
}

// application's message map
BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
   ON_WM_PAINT()
END_MESSAGE_MAP()

// draw some text centered in the client area
void CMainWnd::OnPaint()
{
   CRect    rect;

   GetClientRect(&rect);

   CPaintDC dc(this);

   dc.DrawText(_T("Hello, MFC!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}

// instantiate the application
CTheApp  theApp;

57 lines, with a lot of whitespace and comments for readability.

Oh, as to why I qualified the C strings as static AND const? I don't have a clue. I checked my notes on the code sample and I can't find a reason for doing it that way.

I must have had what I believed was a valid reason, but I can't remember what the reasoning was.

Changing the qualification(s) is a change I will make. And document why. :)

You can thank MS/MSDN for that. That convoluted bit of code is straight from their GetMessage write up:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage

And, yes, I had an app during revision testing that tried to continue after the main window had been destroyed. *OUCH!*


You're a better man than me FurryGuy. I tried all yesterday morning to get a minus one out of GetMessage() and I just couldn't do it!

In the documentation on GetMessage() it states....


If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer.


That gave me the idea that perhaps I could achieve this difficult feat - a feat I haven't been able to accomplish in 25 years of pure Win32 Api SDK coding since Windows 95 in multiple programming languages, by sending or posting a WM_DESTROY message to a window, then posting a custom message to that window after it had been destroyed. So I cobbled together this piece of code to see what good I could accomplish....

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
// Main.cpp
// cl GetMsg.cpp /O1 /Os kernel32.lib user32.lib 
#ifndef UNICODE
    #define UNICODE
#endif
#ifndef _UNICODE
    #define _UNICODE
#endif
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include "GetMsg.h"
FILE* fp = NULL;


LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
{
 fprintf(fp,"  Entering fnWndProc_OnCreate()\n");
 fprintf(fp,"    Wea.hWnd = 0x%p\n",Wea.hWnd);
 fprintf(fp,"  Leaving fnWndProc_OnCreate()\n\n");
 
 return 0;
}


LRESULT fnWndProc_OnBoobyTrap(WndEventArgs& Wea)
{
 fprintf(fp,"  Entering fnWndProc_OnBoobyTrap()\n");
 fprintf(fp,"    Wea.hWnd = 0x%p\n",Wea.hWnd);
 fprintf(fp,"  Leaving fnWndProc_OnBoobyTrap()\n\n");
 
 return 0;
}


LRESULT fnWndProc_OnClose(WndEventArgs& Wea)
{
 fprintf(fp,"  Entering fnWndProc_OnClose()\n");
 fprintf(fp,"    Wea.hWnd = 0x%p\n",Wea.hWnd);
 //DestroyWindow(Wea.hWnd);
 PostMessage(Wea.hWnd,WM_DESTROY,0,0);
 BOOL blnReturn=PostMessage(Wea.hWnd,BOOBY_TRAP,0,0);
 if(blnReturn)
    fprintf(fp,"    PostMessage(BOOBY_TRAP) Succeeded!\n");
 else
    fprintf(fp,"    PostMessage(BOOBY_TRAP) Failed!\n");  
 fprintf(fp,"  Leaving fnWndProc_OnClose()\n\n");
 
 return 0;
}


LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 fprintf(fp,"  Entering fnWndProc_OnDestroy()\n");
 fprintf(fp,"    Wea.hWnd = 0x%p\n",Wea.hWnd);
 //BOOL blnReturn=PostMessage(Wea.hWnd,BOOBY_TRAP,0,0);
 PostQuitMessage(0);
 //fprintf(fp,"    blnReturn = %d\n",blnReturn);
 fprintf(fp,"  Leaving fnWndProc_OnDestroy()\n\n");
 
 return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs Wea;                            // This procedure loops through the EVENTHANDER struct array
                                              // to try to make a match with the msg parameter of the WndProc.
 for(size_t i=0; i<dim(EventHandler); i++)    // If a match is made the event handling procedure is called 
 {                                            // through a function pointer - (EventHandler[i].fnPtr). If no 
     if(EventHandler[i].Msg==msg)             // match is found the msg is passed onto DefWindowProc().
     { 
        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)
{
 wchar_t szClassName[]=L"GetMsg";
 int blnGetMsg=0;
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;
 
 fp=fopen("Output.txt","w");
 if(!fp)
    return 0;
 fprintf(fp,"Entering WinMain()\n");  
 wc.lpszClassName = szClassName;
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.style         = CS_HREDRAW|CS_VREDRAW;
 wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
 wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
 wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wc.cbWndExtra    = 0;
 wc.cbClsExtra    = 0;
 wc.lpszMenuName  = NULL;
 wc.hInstance     = hIns;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,600,400,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(true)
 {
  blnGetMsg=GetMessage(&messages,NULL,0,0);
  switch(blnGetMsg)
  {
    case -1:
      fprintf(fp,"  blnGetMsg = -1!!!!\n");
      fprintf(fp,"Leaving WinMain()\n");
      fclose(fp);
      return messages.wParam;
    case 0:
      fprintf(fp,"  blnGetMsg = WM_QUIT!\n");
      fprintf(fp,"Leaving WinMain()\n");
      fclose(fp);
      return messages.wParam;
    default:
      TranslateMessage(&messages);
      DispatchMessage(&messages);
  } 
 }
}


And header....

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
//GetMsg.h
#ifndef GetMsg_h
#define GetMsg_h
#define                           dim(x)            (sizeof(x) / sizeof(x[0]))
#define                           BOOBY_TRAP        2000
struct                            WndEventArgs
{
 HWND                             hWnd;
 WPARAM                           wParam;
 LPARAM                           lParam;
 HINSTANCE                        hIns;
};

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

LRESULT fnWndProc_OnCreate        (WndEventArgs& Wea);
LRESULT fnWndProc_OnBoobyTrap     (WndEventArgs& Wea);
LRESULT fnWndProc_OnClose         (WndEventArgs& Wea);
LRESULT fnWndProc_OnDestroy       (WndEventArgs& Wea);

const EVENTHANDLER                EventHandler[]=
{
 {WM_CREATE,                      fnWndProc_OnCreate},
 {BOOBY_TRAP,                     fnWndProc_OnBoobyTrap},
 {WM_CLOSE,                       fnWndProc_OnClose},
 {WM_DESTROY,                     fnWndProc_OnDestroy}
};
#endif 


There are a number of commented out lines in the above code. What that's about is that I was trying desperately to do whatever I could to get GetMessage() to return a -1 for me. Here is one output I got from a program run I believe as the code is just above....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Entering WinMain()
  Entering fnWndProc_OnCreate()
    Wea.hWnd = 4326356
  Leaving fnWndProc_OnCreate()

  Entering fnWndProc_OnClose()
    Wea.hWnd = 4326356
    PostMessage(BOOBY_TRAP) Succeeded!
  Leaving fnWndProc_OnClose()

  Entering fnWndProc_OnDestroy()
    Wea.hWnd = 4326356
  Leaving fnWndProc_OnDestroy()

  Entering fnWndProc_OnBoobyTrap()
    Wea.hWnd = 4326356
  Leaving fnWndProc_OnBoobyTrap()

  blnGetMsg = WM_QUIT!
Leaving WinMain()


Here's another program run output where the custom message of mine 'BOOBY_TRAP' which I posted to the message queene failed, but GetMessage() nonetheless returned WM_QUIT, not minus 1....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Entering WinMain()
  Entering fnWndProc_OnCreate()
    Wea.hWnd = 4719572
  Leaving fnWndProc_OnCreate()

  Entering fnWndProc_OnClose()
    Wea.hWnd = 4719572
    Entering fnWndProc_OnDestroy()
      Wea.hWnd = 4719572
    Leaving fnWndProc_OnDestroy()
    PostMessage(BOOBY_TRAP) Failed!
  Leaving fnWndProc_OnClose()

  blnGetMsg = WM_QUIT!
Leaving WinMain()

So I basically gave up. I'm just not up to the task. I remember a line from one of Clint Eastwood's 'Dirty Harry' movies....


A man's 'gotta know his limitations.


But I did do a Google search on the issue and came up with these two links....

When Does GetMessage Return -1?
http://blog.airesoft.co.uk/2010/10/a-negative-experience-in-getting-messages/

When will GetMessage return -1?
https://devblogs.microsoft.com/oldnewthing/20130322-00/?p=4873

The latter one is from one of Microsoft's devblogs. What I got out of both those articles is that it isn't something a developer should worry about. I don't recall that Petzold used that -1 testing setup in any of his Api books (I have his Win 95 and Win 98 books). And like I said, I've been at this stuff constantly for over 25 years, my applications are still running in a very large state agency as enterprise mission critical applications, and this error has never been encountered. Of course, I have to say that I routinely don't send messages to windows that have been destroyed.

When you get right down to it though, my reason for being so stubborn about this specific issue has nothing to do with coding correctness. That GetMessage() code in the Microsoft's documentation is downright plug ugly. Any coder's 1st impression upon seeing such code is that 'I'm looking at poor, buggy code'. Its not elegant, and for me that's a stopper. But of course, beauty is in the eye of the beholder.

By the way, I used your....

1
2
3
 wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
 wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
 wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);


...code!

Thanks!
That GetMessage() code in the Microsoft's documentation is downright plug ugly.

No real argument from me about that. But then MS doesn't really seem to care about writing elegant looking code.

That code template is more than several years old, with no eye to really making it be anything other than a repository of experimentation for various ideas. A Frankenstein's monster of code snippet stitched together.

And still no feedback from the OP they've seen the discussion, and the various coding styles people use.
I tried all yesterday morning to get a minus one out of GetMessage() and I just couldn't do it!

It was a "one off" situation, a fluke that probably will never happen again. The app's window had been destroyed, but the message pump was still working. So Windows dutifully kept sending messages to an app that hadn't fully quit yet.

Typical "Windows goes 'funky'" situation. A reboot and it never happened again.

It was Win98, using Visual Studio 6 to build the app. Maybe that had something to do with the weird happening. I don't know, honestly.

Never happened with Win10 and VS 2015/7/9. I've had apps (and Windows itself) even now stop taking input on occasion, but they weren't trying to close and exit. Sign out/in, close out any memory/processor intensive apps or reboot and lo! It's working again.
Superflous had jumped into the conversation (but seems to have deleted his posts) to state that FurryGuy had used S_OK and E_FAIL incorrectly in WinMain(), as they are both COM/OLE related equates/defines. I don't know that I'd go that far not being a doctrinaire person myself. I immediately noted FurryGuy's use of those equates but my impression was that FurryGuy likely got them from Microsoft's Win32 Api documentation which he always seems to be 'up on' - much more so than I. But in most cases what WinMain() returns is irrelevant. The only case I can think of where it wouldn't be is a case where the GUI program is 'chained/shelled' to with something like ShellExecute(), which returns to another binary a return code.

But I still think showing truly minimal code to provide the essence of a Windows GUI has merit. Something small enough that someone new to both C++ and the Win32 framework doesn't need to scroll to see a whole working program, which still shows the essences which are the WinMain() starting point, the RegisterClassEx(), function, the CreateWindowEx() function, the 'Message Pump', and finally but perhaps most important - the Window Procedure. And you know, even though I haven't looked at some of this stuff in ages, some of the Microsoft documentation is pretty good, such as....

https://docs.microsoft.com/en-us/windows/win32/learnwin32/creating-a-window

https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winmain

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexw

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa

But you know, and changing the subject back to beginner or Win32 template programs, my 38 line program above isn't really as 'minimal' as it could be. And by that I don't mean putting a MessageBox() call in WinMain() and then ending the program, or using a resource template with rc.exe to create a DialogBox program - techniques which indeed would create a window with less lines of code. What I mean is that using VStudio 2019 and version 15 of Microsoft's C/C++ compiler, the compiled 'stand alone' executable - /MT static linkage with the C Runtime not requiring runtimes to be packaged with it, and Release Build, builds to a mighty 91,646 bytes! And that program accomplishes nothing more than creates a resizable window with no options but to destroy it! In my opinion that's fairly atrocious. The same program built with another programming language - namely PowerBASIC Version 10, comes to 6,656 bytes - only 7% of the C++ built code!

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
#Compile Exe  "Form1_PB.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*8
  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.hbrBackground  = %COLOR_WINDOW+1
  RegisterClassEx(wc)
  hWnd=CreateWindowEx(0,szClassName,szClassName,%WS_OVERLAPPEDWINDOW Or %WS_VISIBLE,200,100,325,300,%HWND_DESKTOP,0,hInstance,ByVal 0)
  While GetMessage(Msg,%NULL,0,0)
    TranslateMessage(Msg)
    DispatchMessage(Msg)
  Wend

  Function=msg.wParam
End Function     


I believe that one is only 32 lines! But we can get even smaller than that in terms of binary size by dropping down to assembler. This one I built with Steve Hutcheson's (Hutch) MASM32 package for x86 and comes to only 2,560 bytes....

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
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName db "Form1",0
AppName  db "Form1",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
  invoke GetModuleHandle, NULL
  mov    hInstance,eax
  invoke GetCommandLine
  mov    CommandLine,eax
  invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
  invoke ExitProcess,eax

  WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,100,100,375,350,NULL,NULL,hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
      invoke GetMessage, ADDR msg,NULL,0,0
      .BREAK .IF (!eax)
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage, ADDR msg
    .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  .IF uMsg==WM_DESTROY
      invoke PostQuitMessage,NULL
  .ELSE
      invoke DefWindowProc,hWnd,uMsg,wParam,lParam		
      ret
  .ENDIF
  xor eax,eax
  ret
WndProc endp
end start


continued....
I didn't take the time to modify the above code to be exactly like the PowerBASIC or C++ program above, but it's close.

So why does my 38 line C++ program under discussion build to such a large executable? Is it because C++ is bloatware? Not exactly. Its because of all the unnecessary stuff Microsoft packs into its executables. You can find that out by doing a DUMPBIN on the C++ Form1.exe produced by VStudio's compiler. We can easily beat it size wise. Let's try. One warning though - whether you like it or not, you're not likely to be able to follow my instructions using the Visual Studio IDE. We're going to be doing some pretty strange and bizarre stuff here - verboten stuff, and we don't want Microsoft's Coding Style Correctness Police to know anything about it! We're going to be working strictly from the command line to VStudio's C++ Build Chain. Its not hard. You'll find a shortcut installed on your Start Menu when you installed Visual Studio. It should look something like so for x86.....

Start Menu >> Visual Studio 2019 >> x86 Native Tools Command Prompt

…. And for x64...

Start Menu >> Visual Studio 2019 >> x64 Native Tools Command Prompt

Rather than the awkward Start Menu, I put these shortcuts on my Desktop, especially since I only do command line compiling.

To follow along, put this code in some folder of your choosing....

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
// cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj kernel32.lib user32.lib
#include <windows.h>  // 2,560 Bytes Exe Size

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

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 MSG messages;
 WNDCLASS wc;
  
 wc.lpszClassName = "Form1",   wc.lpfnWndProc   = fnWndProc;
 wc.hInstance     = hInstance, wc.style         = 0;
 wc.cbClsExtra    = 0,         wc.cbWndExtra    = 0;
 wc.hIcon         = NULL,      wc.hCursor       = NULL;
 wc.lpszMenuName  = NULL,      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 RegisterClass(&wc);
 CreateWindow("Form1","Form1",WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


Then, from the command line interface or console opened by the Visual Studio shortcut navigate to the directory where you put the above code. You might have to change the directory back to the root directory with the Change Directory command first, i.e., ...

CD\ [ENTER]

Next copy this code to the same directory and name it crt_win_a.cpp....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
                          Microsoft Systems Journal, October 1996
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN 
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

extern "C" void __cdecl WinMainCRTStartup(void)
{
 int iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
 ExitProcess(iReturn);
}


Next, using my command line build string above, i.e., ....

cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN

...build that file to crt_win_a.obj. You need to paste that string into the command line window opened to whatever directory the Form1.cpp and crt_win_a.cpp files are located, then press [ENTER]. That will invoke the build chain to create the binary crt_win_a.obj.

Next, now that you have crt_win_a.obj created, which is a link dependency of Form1.cpp, do the same with the command line build string at the top of Form1.cpp....

cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj kernel32.lib user32.lib [ENTER]

My command line console looks like so after performing those two operations.....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Code\VStudio\Forms\Form1\NoCrt>cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

crt_win_a.cpp

C:\Code\VStudio\Forms\Form1\NoCrt>cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

Form1.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1.exe
crt_win_a.obj
kernel32.lib
user32.lib
Form1.obj

C:\Code\VStudio\Forms\Form1\NoCrt>


The above command line session was for x86. It built a 2,048 byte Form1.exe stand alone executable for me using VStudio 2008's Build Chain (even smaller than my asm version above!). For VStudio 2019 it's 3,072 Bytes. Built for x64 it comes to 2,560 bytes on my old VS installation. It works perfectly (same as the 91 KB one) as you could find out by experimenting with it. So there is a minimal GUI Windows program! Thought someone might be interested!
@freddie1, I am so beyond worrying about how many lines of code are used, or the "The Only True Use Of Whitespace" or the final size of the executable. And other minutiae. I am a hobbyist, not a professional programmer.

Your source code layout works for you, mine works for me.

Now regarding superfluous and his over-the-top rant about using S_OK and E_FAIL. At first I was loath to explain my design choices to someone who IMO clearly was more intent on insisting his design choices were the WORD OF G-D.

But you did mention the issue, and you are someone that listens. Even if you don't agree with the choice(s).

A lot of what I am about to 'splain you probably already know, so please bear with me.

No, those two #defined constants are NOT only COM constants. They are generic HRESULT constants used to indicate success or failure.
https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values

Please note the category/sub-category that webpage resides in. Security and Identity / Cryptography. Not COM.

What is an HRESULT? A typedef for LONG. Nothing about EXCLUSIVE use by COM.

Weirdly enough MAPI uses HRESULT return codes also. AKA SCODE return codes.

Windows for the most part doesn't process an app's return code. Convention has return 0; as the app encountered no errors and returning a non-zero value (usually -1) indicates an error happened so the programmer purposefully terminates the app abnormally.

Me, I like readability. Using predefined constants already available I know if a return value signals success or failure at a glance in the source. Using the Windows predefined constants avoids the dreaded "magic number" syndrome.

Ultimately the predefined constants are just numbers, after the preprocessor "mangles" the source code.

I could have used my own defined error codes, I've done it before with an enum, but why bother if there are error codes already defined in included headers.

The OP hasn't bothered to return and acknowledge what's being discussed and debated, so I believe I'll go on to greener pastures as well. It's been fun, it's been instructive, but now things have reached the point of diminishing returns.

(Elvis Leaves The Thread) :Þ
Topic archived. No new replies allowed.