#if 0
Form8 -- Convenient GUI Debugging With A Console Window And CRT printf()
I usually open a log file for GUI debugging purposes, but sometimes just printing to a console screen can be very convenient.
I've especially used this technique when developing custom controls or ActiveX Controls, where I'd open a log output file for
control output and a console screen for debugging the client/host. That way I could keep track of exactly what was happening
simultaneously in both interacting binaries.
This is easy to do by simply allocating a console window in a GUI app, then outputting to it with WriteConsole(). But using
printf() is simply more convenient and direct, in my opinion. But the catch is, CRT printf() doesn't work in GUI apps. At
the time of my writing this (11/28/2021) further and more detailed information on this topic can be found here in this very
old article....
https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/105305Quoting from this article....
When a GUI application is started with the "start" command, the three standard OS handles STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
and STD_ERROR_HANDLE are all "zeroed out" by the console initialization routines. These three handles are replaced by valid
values when the GUI application calls AllocConsole(). Therefore, once this is done, calling GetStdHandle() will always return
valid handle values. The problem is that the CRT has already completed initialization before your application gets a chance to
call AllocConsole(); the three low I/O handles 0, 1, and 2 have already been set up to use the original zeroed out OS handles,
so all CRT I/O is sent to invalid OS handles and CRT output does not appear in the console. Use the workaround described above
to eliminate this problem.
The following code implements the workarounds described in that article. In WinMain() I call GUI_Console_IO() - the function
I created (just below) to set things up. If it succeeds one can read/write the console opened by AllocateConsole(). The
program consists simply of a main GUI window with a single command button on it. When one clicks the button a text string is
output to the console stating my weight in pounds. I used a double variable and the %f printf format specifier. The program
additionally outputs entrances and exits from the various message/event handling functions. Here is what an output run looks
like (copied from the console screen) when I start the app from a shortcut, click twice on the button to write to console, then
X out of the app from the GUI component of it....
Entering WinMain()
Entering fnWndProc_OnCreate()
Wea.hWnd = 0x00000000000D0620
Leaving fnWndProc_OnCreate()
hWnd = 0x00000000000D0620
Entering fnWndProc_OnCommand()
My Name Is Fred And I Weight 167.5 Pounds.
Leaving fnWndProc_OnCommand()
Entering fnWndProc_OnCommand()
My Name Is Fred And I Weight 167.5 Pounds.
Leaving fnWndProc_OnCommand()
Entering fnWndProc_OnClose()
Wea.hWnd = 0x00000000000D0620
Entering fnWndProc_OnDestroy()
Wea.hWnd = 0x00000000000D0620
Leaving fnWndProc_OnDestroy()
Leaving fnWndProc_OnClose()
Leaving WinMain()
Press Any Key To Continue....
Note that when one x's out, DestroyWindow() has already executed, and code execution is at the end of WinMain(), and the message
loop has ended. But the console window is still 'hanging' open with a CRT getchar() call, a blinking cursor, and a last printf()
output line of....
Press Any Key To Continue....
Further note that in a GUI app of this type where one allocates a console window, the console window will have the typical
minimize, maximize, and close buttons up in the title bar. If one clicks the close (X) button, it not only closes/destroys the
console window - it takes out the whole process - console window and GUI window. This is by design, and for console windows
is not a problem. But it is a problem when shutdown code in the GUI window fails to run (release allocated memory, close
resources, etc.). So you'll note in GUI_Console_IO() there is code to deactivate the close button of the console window.
Note that this is a TCLib only version. Due to the sort of irregular nature of all this, I'll have to make a somewhat altered
non-TCLib version I'll call Form8a.cpp, and I'll post that after tis one.
#endif
// Form8.cpp
// cl Form8.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
// Size: 7,168 Bytes; Uses TCLib
#include <windows.h>
#include "Form8.h"
#include "stdio.h"
bool GUI_Console_IO(FILE*& hFileIn, FILE*& hFileOut)
{
HMENU hSysMenu = NULL; // For Re-Initializing CRT Console IO In GUI Apps. With Windows, CRT Console IO File
HANDLE hStdOut = NULL; // Descriptors Are Zeroed Out At GUI Initialization. That's why C Runtime console i/o
HANDLE hStdIn = NULL; // Routines Won't Work. If You Don't Believe Me, Try Creating A GUI App, Call
int hCrtOut = 0; // AllocateConsole(), Then Try Using CRT printf(...). Windows API WriteConsole() Will
int hCrtIn = 0; // Work, But CRT printf(...) Won't. This App Shows How To Fix That Using TCLib.lib.
if(!AllocConsole()) return false; // Create Console
hStdIn = GetStdHandle(STD_INPUT_HANDLE); // Get Standard Input Handle
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); // Get Standard Output Handle
// Open stdin
hCrtIn = _open_osfhandle((intptr_t)hStdIn,O_TEXT); // Associates a C run-time file descriptor with an existing operating system Api file handle
hFileIn = _fdopen(hCrtIn,"r"); // Associates a stream with a file that was previously opened for low-level I/O.
_iob[0]=*hFileIn; // _iob Is An Array of FILE structs containing stdin, stdout, and stderr
// Open stdout
hCrtOut = _open_osfhandle((intptr_t)hStdOut,O_TEXT); // Associates a C run-time file descriptor with an existing operating system Api file handle
hFileOut = _fdopen(hCrtOut,"w"); // Associates a stream with a file that was previously opened for low-level I/O.
_iob[1]=*hFileOut; // pFile[1] is stdout FILE object. This Call Re-Initializes It.
// Disable Close In Title Bar Of Console Window // Otherwise, clicking the close button on the console window will terminate the whole
hSysMenu = GetSystemMenu(GetConsoleWindow(),0); // process - GUI and all! Not the best thing to happen.
DeleteMenu(hSysMenu,6,MF_BYPOSITION);
return true;
}
LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
HWND hButton = NULL;
printf(" Entering fnWndProc_OnCreate()\n");
printf(" Wea.hWnd = 0x%p\n",Wea.hWnd);
Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
hButton=CreateWindow("button","Write To Console",WS_CHILD|WS_VISIBLE,90,50,200,30,Wea.hWnd,(HMENU)IDC_BUTTON1,Wea.hIns,0);
printf(" Leaving fnWndProc_OnCreate()\n\n");
return 0;
}
LRESULT CALLBACK fnWndProc_OnCommand(WndEventArgs& Wea)
{
double dblWeight = 167.5;
printf(" Entering fnWndProc_OnCommand()\n");
if(LOWORD(Wea.wParam)==IDC_BUTTON1)
printf(" My Name Is Fred And I Weight %5.1f Pounds.\n",dblWeight);
printf(" Leaving fnWndProc_OnCommand()\n\n");
return 0;
}
LRESULT CALLBACK fnWndProc_OnClose(WndEventArgs& Wea)
{
printf(" Entering fnWndProc_OnClose()\n");
printf(" Wea.hWnd = 0x%p\n",Wea.hWnd);
DestroyWindow(Wea.hWnd);
printf(" Leaving fnWndProc_OnClose()\n");
return 0;
}
LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
printf(" Entering fnWndProc_OnDestroy()\n");
printf(" Wea.hWnd = 0x%p\n",Wea.hWnd);
PostQuitMessage(0);
printf(" Leaving fnWndProc_OnDestroy()\n");
return 0;
}
LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(EventHandler); i++)
{
if(EventHandler[i].iMsg==msg)
{
Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
char szClassName[]="Form8";
FILE* hFileOut = NULL;
FILE* hFileIn = NULL;
WNDCLASSEX wc;
MSG messages;
HWND hWnd;
if(GUI_Console_IO(hFileIn,hFileOut))
{
printf("Entering WinMain()\n");
wc.lpszClassName = szClassName, wc.lpfnWndProc = fnWndProc;
wc.cbSize = sizeof(WNDCLASSEX), wc.style = 0;
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION), wc.hInstance = hIns;
wc.hIconSm = NULL, wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW, wc.cbWndExtra = 0;
wc.lpszMenuName = NULL; wc.cbClsExtra = 0;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,775,600,400,190,HWND_DESKTOP,0,hIns,0);
printf(" hWnd = 0x%p\n\n",hWnd);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
printf("Leaving WinMain()\n\nPress Any Key To Continue....");
getchar();
fclose(hFileIn);
fclose(hFileOut);
FreeConsole();
}
return (int)messages.wParam;
}
Here is #include file Form8.h...
//Form8.h
#ifndef Form8_h
#define Form8_h
#define O_TEXT 0x4000 // From Fcntl.h
#define IDC_BUTTON1 1500
#define dim(x) (sizeof(x) / sizeof(x[0]))
extern "C" int _fltused = 1;
struct WndEventArgs
{
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
};
LRESULT CALLBACK fnWndProc_OnCreate (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnCommand (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnClose (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy (WndEventArgs& Wea);
struct EVENTHANDLER
{
unsigned int iMsg;
LRESULT (*fnPtr)(WndEventArgs&);
};
const EVENTHANDLER EventHandler[]=
{
{WM_CREATE, fnWndProc_OnCreate},
{WM_COMMAND, fnWndProc_OnCommand},
{WM_CLOSE, fnWndProc_OnClose},
{WM_DESTROY, fnWndProc_OnDestroy}
};
#endif
I believe this is my standard stdio.h which I've posted elsewhere, but I'll post it again as it is small. And I'll reiterate, you can't run this code
with Microsoft's <stdio.h> or <cstdio>...
// stdio.h
#ifndef stdio_h
#define stdio_h
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
struct FILE
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
extern FILE* _iob;
extern char (__cdecl* getchar)();
extern FILE* (__cdecl* fopen)(const char* pszFile, const char* pszMode);
extern FILE* (__cdecl* _wfopen)(const wchar_t* pszFile, const wchar_t* pszMode);
extern int (__cdecl* _open_osfhandle)(intptr_t osfhandle, int flags );
extern FILE* (__cdecl* _fdopen)(int FileDescriptor, const char* mode);
extern int (__cdecl* fseek)(FILE* stream, long offset, int origin);
extern int (__cdecl* feof)(FILE* stream);
extern int (__cdecl* printf)(const char* pFormat, ...);
extern int (__cdecl* wprintf)(const wchar_t* pFormat, ...);
extern int (__cdecl* fprintf)(FILE* fp, const char* format, ...);
extern int (__cdecl* fwprintf)(FILE* fp, const wchar_t* format, ...);
extern int (__cdecl* fscanf)(FILE* fp, const char* pFormat, ...);
extern int (__cdecl* fwscanf)(FILE* fp, const wchar_t* pFormat, ...);
extern int (__cdecl* sprintf)(char* buffer, const char* format, ...);
extern int (__cdecl* swprintf)(wchar_t* buffer, const wchar_t* format, ...);
extern char* (__cdecl* fgets)(char* pBuffer, int iSize, FILE* fp);
extern wchar_t* (__cdecl* fgetws)(wchar_t* pBuffer, int iSize, FILE* fp);
extern void (__cdecl* rewind)(FILE* fp);
extern int (__cdecl* fclose)(FILE* fp);
#endif