/*----------------------------
PoePoem1.rc resource script
rc /r /v PoePoem1.rc
WindRes -i PoePoem1.rc -o PoePoem1_res.obj -v
----------------------------*/
#include "PoePoem1.h"
PoePoem1 ICON PoePoem1.ico
AnnabelLee TEXT PoePoem1.txt
STRINGTABLE
{
IDS_APPNAME, "PoePoem1"
IDS_CAPTION, """Annabel Lee"" by Edgar Allen Poe"
IDS_POEMRES, "AnnabelLee"
}
/*-----------------------
PoePoem1.h header file
-----------------------*/
#define IDS_APPNAME 0
#define IDS_CAPTION 1
#define IDS_POEMRES 2
// Main.h
#ifndef Main_h
#define Main_h
#define dim(x) (sizeof(x) / sizeof(x[0]))
#define TYPEFACE_NAME L"Times New Roman"
#define FONT_SIZE 12
struct WndEventArgs
{
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
};
LRESULT CALLBACK fnWndProc_OnCreate (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnSize (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnVScroll (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnHScroll (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnMouseWheel (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnPaint (WndEventArgs& Wea);
LRESULT CALLBACK fnWndProc_OnDestroy (WndEventArgs& Wea);
struct EVENTHANDLER
{
unsigned int iMsg;
LRESULT (CALLBACK* fnPtr)(WndEventArgs&);
};
const EVENTHANDLER EventHandler[]=
{
{WM_CREATE, fnWndProc_OnCreate},
{WM_SIZE, fnWndProc_OnSize},
{WM_VSCROLL, fnWndProc_OnVScroll},
{WM_HSCROLL, fnWndProc_OnHScroll},
{WM_MOUSEWHEEL, fnWndProc_OnMouseWheel},
{WM_PAINT, fnWndProc_OnPaint},
{WM_DESTROY, fnWndProc_OnDestroy}
};
struct ScrollData
{
wchar_t** pPtrs;
int iNumLines;
int cxChar;
int cxCaps;
int cyChar;
int cxClient;
int cyClient;
int iMaxWidth;
};
#endif
//#define TCLib // PoePoem1.cpp
#ifndef UNICODE // cl PoePoem1.cpp /O1 /Os /MT user32.lib gdi32.lib PoePoem1.res // 99,328 Bytes;
#define UNICODE // cl PoePoem1.cpp /O1 /Os /GR- /GS- TCLib.lib user32.lib gdi32.lib PoePoem1.res // 10,752 Bytes
#endif // g++ PoePoem1.cpp PoePoem1_res.obj -o PoePoem1_gcc.exe -mwindows -m64 -s -Os // 25,600 Bytes
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#ifdef TCLib
#include "string.h"
#include "stdio.h"
#include "memory.h"
#else
#include <string.h>
#include <stdio.h>
#include <wchar.h>
#endif
#include "Main.h"
#include "PoePoem1.h"
LRESULT CALLBACK fnWndProc_OnCreate(WndEventArgs& Wea)
{
ScrollData* pScrDta = NULL;
char* pText = NULL;
wchar_t* pWText = NULL;
wchar_t* p = NULL;
HGLOBAL hResource = NULL;
HANDLE hHeap = NULL;
HRSRC hFoundRes = NULL;
HDC hdc = NULL;
HFONT hFont = NULL;
size_t iStrLen = 0;
char szPoemRes[16];
wchar_t szBuffer[128];
TEXTMETRIC tm;
Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance; // Get HINSTANCE From CREATESTRUCT Object
LoadStringA(Wea.hIns,IDS_POEMRES,szPoemRes,sizeof(szPoemRes)); // Get Name Of Custom TEXT Resource "AnnabelLee" (It's Asci)
hFoundRes = FindResourceA(Wea.hIns,szPoemRes,"TEXT"); // Find Custom Resource In Binary Executable
hResource = LoadResource(Wea.hIns,hFoundRes); // Get HANDLE To Resource, So We Can Get Pointer To It.
pText=(char*)LockResource(hResource); // Get Pointer To First Byte Of Custom Resource
hHeap=GetProcessHeap(); // Will Need To Allocate Memory For A ScrollData Object
pScrDta=(ScrollData*)HeapAlloc // (See Main.h) So As To Eliminate Charles Petzold's
( // Static And Global C Variables. We'll Store The
hHeap,HEAP_ZERO_MEMORY,sizeof(ScrollData) // Allocated Object As Part Of The App's WNDCLASSEX
); // cbWndExtra Bytes, Which Is Kind Of A C Ism For A
if(!pScrDta) // C++ Class Member Variable. If -1 Is Returned From
return -1; // WM_CREATE Handler Code, The CreateWindowEx() Call
SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pScrDta); // In WinMain() Will Fail, And App Will Terminate.
hdc = GetDC(Wea.hWnd); // To Get Scroll Bars And Scrolling Working, One Needs
hFont=CreateFont // To Know, For Example, The Size (Height) Of A Char-
( // acter In Relation To The Size Of The Client Area Of
-1*(FONT_SIZE*GetDeviceCaps(hdc,LOGPIXELSY))/72, // The Window Where Text Is To Be Displayed And Scrolled.
0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY, // So We Need To Create A Font That The App Will Use,
0,TYPEFACE_NAME // And Get It's Metrics Using GetTextMetrics(). We'll
); // Store This Info In The ScrollData Persisted Object,
if(!hFont) // And Let It Up To The WM_SIZE Handler Code To Deal
return -1; // With The Screen Size Stuff. We Gotta Be Careful To
HFONT hTmp=(HFONT)SelectObject(hdc,hFont); // Delete The Dynamically Allocated Resource.
GetTextMetrics(hdc, &tm);
pScrDta->cxChar = tm.tmAveCharWidth;
pScrDta->cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * pScrDta->cxChar / 2;
pScrDta->cyChar = tm.tmHeight + tm.tmExternalLeading;
DeleteObject(SelectObject(hdc,hTmp));
ReleaseDC(Wea.hWnd, hdc);
iStrLen=MultiByteToWideChar(CP_ACP,0,pText,-1,NULL,0); // We Need To Convert Asci "Annabel Lee" Poem Read From
pWText=(wchar_t*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iStrLen*2); // Executable Resource To Wide Character For GUI Output
MultiByteToWideChar(CP_ACP,0,pText,-1,pWText,iStrLen); // In WM_PAINT Handler Code. We'll Use MultiByteToWideChar().
p=pWText; // Run Through Annabel Lee Poem Counting Line Feed Characters Because, For The Scroll Logic This App
while(*p) // Employs Using TextOut() In WM_PAINT Handler Code, We Need To Break The Poem Up Into An Array Of Lines.
{ // We'll Persist The Count Obtained Here In Our ScrollData Object, Which Becomes Part Of The
if(*p==13) // WNDCLASSEX::cbWndExtra Bytes. This Eliminates Charles Petzold's statics and globals.
pScrDta->iNumLines++;
p++;
}
pScrDta->pPtrs=(wchar_t**)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,pScrDta->iNumLines*sizeof(wchar_t**));
if(pScrDta->pPtrs)
{ // This Code Will Break The CR-LF Delimited Poem Lines From The Resources
size_t j; // In The Executable Into An Array Of Character Pointers -- All Contained
p=pWText; // Within The ScrollData Object. Wouldn't Surprise Me If There Was
for(size_t i=0; i<pScrDta->iNumLines; i++) // Something Or Other In The C++ Specific Part Of The Standard Library To
{ // Do This Easier, But Since I'm A Hard Core C Coder Code Like This Doesn't
wmemset(szBuffer,'\0',128); // Bother Me. Especially Since I Can Build This x64 Using My Own Custom C
j=0; // Runtime In 10 K, As Opposed To A Megabyte Sized Executable If Linked
while(true) // Against MS's LibCmt. So What The Code Does Is Run Through The Poem Byte
{ // By Byte Detecting When A NewLine Sequence (CR-LF) Has Delimited A Line.
if(*p==10) // Up Until That Point, Ordinary Printable Characters Are Accumulated In
p++; // szBuffer[], But When A Newline Is Encountered The CR Is Replaced By A
if(*p==13) // NULL Byte Terminating The String In szBuffer. Then We Make A Memory Alloc-
{ // ation For The Delimited Line And Store It In ScrollData.
szBuffer[j]=L'\0';
iStrLen=wcslen(szBuffer);
if(iStrLen>pScrDta->iMaxWidth)
pScrDta->iMaxWidth=iStrLen;
pScrDta->pPtrs[i]=(wchar_t*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,iStrLen*2+2);
wcscpy(pScrDta->pPtrs[i],szBuffer);
p++;
break;
}
szBuffer[j]=*p;
p++,j++;
}
}
pScrDta->iMaxWidth=pScrDta->iMaxWidth*pScrDta->cxChar;
}
return 0;
}
LRESULT CALLBACK fnWndProc_OnSize(WndEventArgs& Wea)
{
ScrollData* pScrDta=NULL;
SCROLLINFO si;
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta)
{
pScrDta->cxClient = LOWORD(Wea.lParam);
pScrDta->cyClient = HIWORD(Wea.lParam);
si.cbSize = sizeof(si) ;
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = pScrDta->iNumLines - 1;
si.nPage = pScrDta->cyClient / pScrDta->cyChar;
SetScrollInfo(Wea.hWnd, SB_VERT, &si, TRUE);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = pScrDta->iMaxWidth / pScrDta->cxChar;
si.nPage = pScrDta->cxClient / pScrDta->cxChar;
SetScrollInfo(Wea.hWnd, SB_HORZ, &si, TRUE);
}
return 0;
}
LRESULT CALLBACK fnWndProc_OnVScroll(WndEventArgs& Wea)
{
ScrollData* pScrDta=NULL;
SCROLLINFO si;
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta)
{
si.cbSize = sizeof(si) ;// Get all the vertial scroll bar information
si.fMask = SIF_ALL ;
GetScrollInfo(Wea.hWnd, SB_VERT, &si);
int iVertPos = si.nPos; // Save the position for comparison later on
switch (LOWORD(Wea.wParam))
{
case SB_TOP:
si.nPos = si.nMin ;
break ;
case SB_BOTTOM:
si.nPos = si.nMax ;
break ;
case SB_LINEUP:
si.nPos -= 1 ;
break ;
case SB_LINEDOWN:
si.nPos += 1 ;
break ;
case SB_PAGEUP:
si.nPos -= si.nPage ;
break ;
case SB_PAGEDOWN:
si.nPos += si.nPage ;
break ;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break ;
default:
break ;
}
si.fMask = SIF_POS ;
SetScrollInfo(Wea.hWnd, SB_VERT, &si, TRUE);
GetScrollInfo(Wea.hWnd, SB_VERT, &si);
if(si.nPos != iVertPos)
{
ScrollWindow(Wea.hWnd, 0, pScrDta->cyChar*(iVertPos-si.nPos), NULL, NULL);
UpdateWindow(Wea.hWnd);
}
}
return 0;
}
LRESULT CALLBACK fnWndProc_OnHScroll(WndEventArgs& Wea)
{
ScrollData* pScrDta=NULL;
SCROLLINFO si;
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta)
{
si.cbSize = sizeof (si);// Get all the horizontal scroll bar information
si.fMask = SIF_ALL;
GetScrollInfo(Wea.hWnd, SB_HORZ, &si) ;// Save the position for comparison later on
int iHorzPos = si.nPos;
switch (LOWORD(Wea.wParam))
{
case SB_LINELEFT:
si.nPos -= 1 ;
break ;
case SB_LINERIGHT:
si.nPos += 1 ;
break ;
case SB_PAGELEFT:
si.nPos -= si.nPage ;
break ;
case SB_PAGERIGHT:
si.nPos += si.nPage ;
break ;
case SB_THUMBTRACK: // case SB_THUMBPOSITION:
si.nPos = si.nTrackPos ;
break ;
default :
break ;
}
si.fMask = SIF_POS;
SetScrollInfo(Wea.hWnd, SB_HORZ, &si, TRUE);
GetScrollInfo(Wea.hWnd, SB_HORZ, &si);
if(si.nPos != iHorzPos)
ScrollWindow(Wea.hWnd, pScrDta->cxChar*(iHorzPos-si.nPos), 0, NULL, NULL);
}
return 0;
}
LRESULT CALLBACK fnWndProc_OnMouseWheel(WndEventArgs& Wea)
{
int zdelta=GET_WHEEL_DELTA_WPARAM(Wea.wParam);
if(zdelta>0)
{
for(int i=0; i<10; i++)
SendMessage(Wea.hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0);
}
else
{
for(int i=0; i<10; i++)
SendMessage(Wea.hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
}
return 0;
}
LRESULT CALLBACK fnWndProc_OnPaint(WndEventArgs& Wea)
{
int x,y,iPaintBeg,iPaintEnd,iVertPos,iHorzPos;
ScrollData* pScrDta=NULL;
HFONT hFont=NULL;
PAINTSTRUCT ps;
SCROLLINFO si;
HDC hdc;
hdc = BeginPaint(Wea.hWnd, &ps);
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta)
{
hFont=CreateFont
(
-1*(FONT_SIZE*GetDeviceCaps(hdc,LOGPIXELSY))/72,0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,0,TYPEFACE_NAME
);
HFONT hTmp=(HFONT)SelectObject(hdc,hFont);
si.cbSize = sizeof (si) ;// Get vertical scroll bar position
si.fMask = SIF_POS ;
GetScrollInfo(Wea.hWnd, SB_VERT, &si), iVertPos = si.nPos;
GetScrollInfo(Wea.hWnd, SB_HORZ, &si), iHorzPos = si.nPos;
if(iVertPos+ps.rcPaint.top/pScrDta->cyChar>0)
iPaintBeg=iVertPos + ps.rcPaint.top / pScrDta->cyChar;
else
iPaintBeg=0;
if(iVertPos + ps.rcPaint.bottom / pScrDta->cyChar < pScrDta->iNumLines - 1)
iPaintEnd=iVertPos + ps.rcPaint.bottom / pScrDta->cyChar;
else
iPaintEnd=pScrDta->iNumLines-1;
for(int i = iPaintBeg; i<= iPaintEnd; i++)
{
x = pScrDta->cxChar * (1 - iHorzPos);
y = pScrDta->cyChar * (i - iVertPos);
TextOut(hdc, x, y, pScrDta->pPtrs[i], (int)wcslen(pScrDta->pPtrs[i]));
}
DeleteObject(SelectObject(hdc,hTmp));
}
EndPaint(Wea.hWnd, &ps);
return 0;
}
LRESULT CALLBACK fnWndProc_OnDestroy(WndEventArgs& Wea)
{
ScrollData* pScrDta = NULL;
HANDLE hHeap = NULL;
hHeap=GetProcessHeap();
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta->pPtrs)
{
for(size_t i=0; i<pScrDta->iNumLines; i++)
HeapFree(hHeap,0,pScrDta->pPtrs[i]);
HeapFree(hHeap,0,pScrDta->pPtrs);
}
HeapFree(hHeap,0,pScrDta);
PostQuitMessage(0);
return 0;
}
LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(size_t i=0; i<dim(EventHandler); i++)
{
if(EventHandler[i].iMsg==msg)
{
Wea.hWnd=hWnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*EventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
char szAppName[64];
char szCaption[64];
wchar_t szWAppName[16];
wchar_t szWCaption[40];
HWND hWnd = NULL;
WNDCLASSEX wc = {0};
MSG msg;
LoadStringA(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));
LoadStringA(hInstance, IDS_CAPTION, szCaption, sizeof(szCaption));
MultiByteToWideChar(CP_UTF8,0,szAppName,-1,szWAppName,16);
MultiByteToWideChar(CP_UTF8,0,szCaption,-1,szWCaption,40);
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = fnWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(void*);
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, szWAppName);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWAppName;
wc.hIconSm = LoadIcon(hInstance, szWAppName);
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szWAppName,szWCaption,WS_OVERLAPPEDWINDOW|WS_VISIBLE,300,150,500,800,NULL,NULL,hInstance,NULL);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
return (int)msg.wParam;
}
I’ve played with this on and off for a couple weeks, and have quite a few versions on my laptops. To use my code to build the apps you may want to change filenames to suit yourself. And you’ll need Charles’ PoePoem.ico and PoePoem.txt or PoePoem.asc files. You’ll need to change file names in a lot of places to get it to all work out, come up with the right icon, etc. Easiest thing to do would be to use my file names, and just rename his PoePoem.ico and PoePoem.txt or PoePoem.asc files.