// T12_TCLib64.cpp
// cl T12_TCLib64.cpp /O1 /Os /GS- /FeT12_TCLib64.exe /link TCLib.lib kernel32.lib user32.lib gdi32.lib
#ifndef UNICODE
#define UNICODE // Program shows how to produce real time scrolling output of data
#endif // processing as processing occurs using threads. The 'main' or
#ifndef _UNICODE // GUI thread contains a main program object with a 'Process Data'
#define _UNICODE // and 'Cancel' button. When the user clicks the 'Process Data'
#endif // button a 2nd top level window is opened with a CreateWindowEx()
#include <windows.h> // call and that is the 'output' window which displays program
#include "stdio.h" // outputs. The output window of class "Process" is registered
#include "tchar.h" // in fnMain_OnCreate(), which is the constructor function, so to
#include "T12_TCLib.h" // speak, for the "Main" Window Class, RegisterClassEx()'ed...
int rand(void) // down in WinMain(). The 'Cancel' button on the main start up...
{ // window is for terminating data processing early before it is
next = next * 1103515245 + 12345; // finished. The long data processing job I decided upon to demon-
return (unsigned int)(next>>16) & RAND_MAX; // strate this technique is a follows ...
} // 1) Allocate a multi-million byte array of dash characters;
// 2) Change every 7th space to a 'P';
// 3) Replace every 'P' with a "PU', thereby growing the buffer;
int Rnd(int iMin, int iMax) // 4) Replace every remaining dash with an '8';
{ // 5) Insert a carriage return / line feed every 90 characters,
double dblRange,dblMaxFactor,dblRandomNumber;
dblRange=iMax-iMin;
dblMaxFactor=dblRange/RAND_MAX;
dblRandomNumber=(double)rand();
return iMin+dblMaxFactor*dblRandomNumber;
}
void Format(wchar_t* pBuffer, size_t iNumber) // This algorithm is fully contained within the DoProcessing()
{ // function below. The 1st parameter of DoProcessing() is
wchar_t szBuf1[24]; // iNumber of type size_t. The program calls this function
wchar_t szBuf2[24]; // LINE_BUFFER_COUNT - 2 times. At this time I have
size_t iDigit=0; // LINE_BUFFER_COUNT #defined as 102, so DoProcessing() would
size_t iLen=0; // get called 100 times - each with a different iNumber para-
size_t iCtr=1; // meter. Where DoProcessing() gets called from is the worker
size_t j=0; // thread function ProcessingThread(). The worker thread, as
// well as the output window to which the thread draws output,
memset(szBuf1,0,24*sizeof(wchar_t)); // is created in the button click procedure for the button
memset(szBuf2,0,24*sizeof(wchar_t)); // on the main or start up form/window/dialog described at
#ifdef x64 // first above. That procedure would be fnMain_OnCommand(),
iLen=swprintf(szBuf1,L"%llu",iNumber); // which handles the program's response to 'Process Data' or
#else // 'cancel' button clicks. As an aside, the Format() function
iLen=swprintf(szBuf1,L"%u",iNumber); // just left inserts commas every three places in the iNumber
#endif // parameter of DoProcessing(), e.g., this - 123,456,789,
_wcsrev(szBuf1); // instead of this - 123456789. And the Rnd() function just
for(size_t i=0; i<iLen; i++) // above it returns a random integral value between the iMin
{ // and iMax parameters. In the ProcessingThread() function,
if(iCtr==3) // as just described, there is a for loop which runs from 1
{ // to LINE_BUFFER_COUNT -2, i.e., 1 to 98 if LINE_BUFFER_COUNT
iDigit++; // is set to 100, and Rnd() will be called that many times
szBuf2[j]=szBuf1[i]; // like so ...
if(iDigit<iLen) //
{ // iNumber = Rnd(10000, 40000);
j++; //
szBuf2[j]=L','; // ... generating 100 random numbers between 10 thousand and 40
} // thousand which numbers will be passed on to DoProcessing() as
j++, iCtr=1; // follows within the for loop in ProcessingThread()...
} //
else // DoProcessing(iNumber,iTickCount);
{ //
iDigit++; // And each call will start my 1) through 5) sequence again
szBuf2[j]=szBuf1[i]; // with a new buffer size to do the dashes, P, and PU thing
j++, iCtr++; // as described above. The Print() function just below
} // will draw lines of text to the output window such as "It
} // Took 0.218 Seconds To Complete Processing Job Number 32
_wcsrev(szBuf2); // Involving 16,913,083 Bytes!". All in all, its a real mean
wcscpy(pBuffer,szBuf2); // data processing machine!
}
void ErrorMemFree(wchar_t** pPtrs, int iNum) // This little thingie just left likely will never be called.
{ // Its purpose is to unravel memory allocations up to the
HANDLE hHeap=NULL; // point where a memory allocation failure occurred.
hHeap=GetProcessHeap();
if(hHeap)
{
for(int i=0; i<iNum; i++)
{
if(pPtrs[i])
{
HeapFree(hHeap,0,pPtrs[i]);
pPtrs[i]=NULL;
}
}
HeapFree(hHeap,0,pPtrs);
pPtrs=NULL;
}
}
void Print(HWND hWnd, ScrollData* pScrDta, size_t& iLine, wchar_t* pszStr)
{
size_t iLen,iRequiredBytes,iWidth;
if(iLine<LINE_BUFFER_COUNT)
{
if(pszStr)
{
iLen=wcslen(pszStr);
iWidth=iLen*pScrDta->cxChar;
if(iWidth>(size_t)pScrDta->iMaxWidth)
{
pScrDta->iMaxWidth=(int)iWidth;
SCROLLINFO si;
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(hWnd, SB_HORZ, &si, TRUE);
}
iRequiredBytes=iLen*sizeof(wchar_t)+sizeof(wchar_t);
pScrDta->pPtrs[iLine]=(wchar_t*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,iRequiredBytes);
wcscpy(pScrDta->pPtrs[iLine],pszStr);
if(iLine>=pScrDta->cyClient/pScrDta->cyChar)
{
InvalidateRect(hWnd,NULL,FALSE);
SendMessage(hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
}
else
InvalidateRect(hWnd,NULL,FALSE);
iLine++;
}
else
{
if(iLine>=pScrDta->cyClient/pScrDta->cyChar)
SendMessage(hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0);
iLine++;
}
}
}
void DoProcessing(size_t& iNumber, size_t& iTickCount)
{
int i=0,iCtr=0,j;
wchar_t* s1=NULL;
wchar_t* s2=NULL;
DWORD tick=GetTickCount();
int iLineLength=90;
int iNumPs=(int)iNumber/7+1;
int iPuExtLength=(int)iNumber+iNumPs;
int iNumFullLines=iPuExtLength/iLineLength;
int iMaxMem=iPuExtLength+iNumFullLines*2;
s1=(wchar_t*)GlobalAlloc(GPTR,iMaxMem*sizeof(wchar_t)); //Allocate two buffers big enough to hold the original NUMBER of chars
s2=(wchar_t*)GlobalAlloc(GPTR,iMaxMem*sizeof(wchar_t)); //plus substitution of PUs for Ps and CrLfs after each LINE_LENGTH chunk.
for(i=0; i<iNumber; i++) // 1) Create a string of dashes
s1[i]=L'-';
for(i=0; i<iNumber; i++, iCtr++) // 2) Change every 7th dash to a "P"
{
if(iCtr==7)
{
s1[i]=L'P';
iCtr=0;
}
}
iCtr=0; // 3) Substitute 'PUs' for 'Ps'
for(i=0; i<iNumber; i++)
{
if(wcsncmp(s1+i,L"P",1)==0)
{
wcscpy(s2+iCtr,L"PU");
iCtr+=2;
}
else
{
s2[iCtr]=s1[i];
iCtr++;
}
}
for(i=0; i<iPuExtLength; i++) // 4) Replace every '-' with an 8;
{
if(s2[i]==L'-')
s2[i]=56; //56 is '8'
}
i=0, j=0, iCtr=0; // 5)Put in a CrLf every 90 characters
while(i<iPuExtLength)
{
s1[j]=s2[i];
i++, j++, iCtr++;
if(iCtr==iLineLength)
{
s1[j]=13, j++;
s1[j]=10, j++;
iCtr=0;
}
}
iTickCount=GetTickCount()-tick;
GlobalFree(s1), GlobalFree(s2);
}
DWORD ProcessingThread(LPVOID hProcess)
{
wchar_t szSeconds[8],szJob[128],szJobNum[8],szNumber[32];
size_t iLineCount,iNumber,iTickCount,iLine=0;
double dblSeconds,dblTotal=0.0;
ScrollData* pScrDta=NULL;
size_t iTotalBytes=0;
pScrDta=(ScrollData*)GetWindowLongPtr((HWND)hProcess,0);
if(!pScrDta)
return FALSE;
iLineCount=pScrDta->iNumLines-2;
for(int i=1; i<=iLineCount; i++)
{
if(GetWindowLongPtr((HWND)hProcess,1*sizeof(void*)))
{
iNumber=Rnd(1000000, 25000000);
iTotalBytes=iTotalBytes+iNumber;
DoProcessing(iNumber,iTickCount);
dblSeconds=(double)iTickCount/(double)1000.0;
dblTotal=dblTotal+dblSeconds;
FltToTch(szSeconds, dblSeconds, 8, 3, _T('.'),true);
wcscpy(szJob,(wchar_t*)L"It Took ");
wcscat(szJob,szSeconds);
wcscat(szJob,(wchar_t*)L" Seconds To Complete Processing Job # ");
swprintf(szJobNum,L"%d",i);
wcscat(szJob,szJobNum);
wcscat(szJob,(wchar_t*)L" Involving ");
Format(szNumber,(size_t)iNumber);
wcscat(szJob,szNumber);
wcscat(szJob,(wchar_t*)L" Bytes!");
Print((HWND)hProcess,pScrDta,iLine,szJob);
}
else
return FALSE;
}
SetWindowLongPtr((HWND)hProcess,1*sizeof(void*),(LONG_PTR)FALSE);
szJob[0]=NULL;
Print((HWND)hProcess,pScrDta,iLine,szJob);
Format(szNumber,iTotalBytes);
wcscpy(szJob,(wchar_t*)L"Processing Complete. ");
wcscat(szJob,szNumber);
wcscat(szJob,(wchar_t*)L" Bytes Were Processed In ");
FltToTch(szSeconds, dblTotal, 8, 3, _T('.'),true);
wcscat(szJob,szSeconds);
wcscat(szJob,(wchar_t*)L" Seconds.");
Print((HWND)hProcess,pScrDta,iLine,szJob);
return TRUE;
}
long fnProcess_OnCreate(WndEventArgs& Wea) // Index Offset What's Stored There
{ // =======================================================
CREATESTRUCT* pCreateStruct=NULL; // 0 0 - 7 ScrollData* ( pScrDta)
ScrollData* pScrDta=NULL; // 1 8 - 15 blnContinue (processing in progress)
HANDLE hHeap=NULL; // 2 16 - 23 hMain (Main Wnd HWND)
HFONT hFont=NULL;
TEXTMETRIC tm;
HWND hMain;
HDC hdc;
pCreateStruct=(CREATESTRUCT*)Wea.lParam;
hMain=(HWND)pCreateStruct->lpCreateParams;
SetWindowLongPtr(Wea.hWnd,2*sizeof(void*),(LONG_PTR)hMain);
hHeap=GetProcessHeap();
pScrDta=(ScrollData*)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(ScrollData));
if(!pScrDta)
return -1;
SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pScrDta);
hdc = GetDC(Wea.hWnd);
hFont=CreateFont(-1*(10*GetDeviceCaps(hdc,LOGPIXELSY))/72,0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,0,(wchar_t*)L"Courier New");
if(!hFont)
return -1;
HFONT hTmp=(HFONT)SelectObject(hdc,hFont);
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);
pScrDta->iNumLines=LINE_BUFFER_COUNT;
pScrDta->pPtrs=(wchar_t**)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(wchar_t*) * pScrDta->iNumLines);
if(!pScrDta->pPtrs)
return -1;
return 0;
}
long fnProcess_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;
}
long fnProcess_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;
}
long fnProcess_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;
}
long fnProcess_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*(10*GetDeviceCaps(hdc,LOGPIXELSY))/72,0,0,0,FW_SEMIBOLD,0,0,0,ANSI_CHARSET,0,0,DEFAULT_QUALITY,0,(wchar_t*)L"Courier New");
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++)
{
if(pScrDta->pPtrs[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;
}
long fnProcess_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;
}
long fnProcess_OnSetCursor(WndEventArgs& Wea)
{
if(GetWindowLongPtr(Wea.hWnd,1*sizeof(void*)))
SetCursor(LoadCursor(NULL,IDC_WAIT));
else
DefWindowProc(Wea.hWnd,WM_SETCURSOR,Wea.wParam,Wea.lParam);
return 0;
}
long fnProcess_OnDestroy(WndEventArgs& Wea)
{
HWND hMain=NULL,hBtn=NULL;
ScrollData* pScrDta=NULL;
HANDLE hHeap=NULL;
hMain=(HWND)GetWindowLongPtr(Wea.hWnd,2*sizeof(void*));
hBtn=GetDlgItem(hMain,IDC_BTN_PROCESS_DATA);
EnableWindow(hBtn,TRUE);
SetWindowLongPtr(hMain,0,(LONG_PTR)FALSE);
hHeap=GetProcessHeap();
pScrDta=(ScrollData*)GetWindowLongPtr(Wea.hWnd,0);
if(pScrDta->pPtrs)
{
for(int i=0; i<pScrDta->iNumLines; i++)
{
if(pScrDta->pPtrs[i])
HeapFree(hHeap,0,pScrDta->pPtrs[i]);
}
HeapFree(hHeap,0,pScrDta->pPtrs);
}
return 0;
}
LRESULT CALLBACK fnProcess(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(ProcessEventHandler); i++)
{
if(ProcessEventHandler[i].iMsg==msg)
{
Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*ProcessEventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
long fnMain_OnCreate(WndEventArgs& Wea)
{
wchar_t szClassName[]=L"Process";
WNDCLASSEX wc;
Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
CreateWindowEx(0,L"button",L"Process Data",WS_CHILD|WS_VISIBLE,75,20,125,30,Wea.hWnd,(HMENU)IDC_BTN_PROCESS_DATA,Wea.hIns,0);
CreateWindowEx(0,L"button",L"Cancel",WS_CHILD|WS_VISIBLE,75,70,125,30,Wea.hWnd,(HMENU)IDC_BTN_CANCEL,Wea.hIns,0);
memset(&wc,0,sizeof(wc));
wc.lpszClassName = szClassName; wc.lpfnWndProc = fnProcess;
wc.cbSize = sizeof (WNDCLASSEX); wc.hInstance = Wea.hIns;
wc.hCursor = LoadCursor(NULL,IDC_ARROW), wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.cbWndExtra = 3*sizeof(void*), wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wc);
return 0;
}
long fnMain_OnCommand(WndEventArgs& Wea)
{
wchar_t szClassName[]=L"Process";
HWND hProcess=NULL;
switch(LOWORD(Wea.wParam))
{
case IDC_BTN_PROCESS_DATA:
{
DWORD lpThreadId=0;
hProcess=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,450,100,850,392,0,0,GetModuleHandle(NULL),Wea.hWnd);
if(hProcess)
{
ShowWindow(hProcess,SW_SHOWNORMAL);
SetWindowLongPtr(hProcess,1*sizeof(void*),(LONG_PTR)TRUE);
SetWindowLongPtr(Wea.hWnd,0*sizeof(void*),(LONG_PTR)hProcess);
EnableWindow((HWND)Wea.lParam,FALSE);
HANDLE hThread=CreateThread(NULL,0,ProcessingThread,hProcess,0,&lpThreadId);
}
break;
}
case IDC_BTN_CANCEL:
{
hProcess=FindWindow(szClassName,szClassName);
if(hProcess)
{
SetWindowLongPtr(hProcess,1*sizeof(void*),(LONG_PTR)FALSE);
SetWindowLongPtr(Wea.hWnd,0*sizeof(void*),(LONG_PTR)FALSE);
}
break;
}
}
return 0;
}
long fnMain_OnSetCursor(WndEventArgs& Wea)
{
if(GetWindowLongPtr(Wea.hWnd,0))
SetCursor(LoadCursor(NULL,IDC_WAIT));
else
{
DefWindowProc(Wea.hWnd, WM_SETCURSOR, Wea.wParam, Wea.lParam);
SetCursor(LoadCursor(NULL,IDC_ARROW));
}
return 0;
}
long fnMain_OnDestroy(WndEventArgs& Wea)
{
PostQuitMessage(0);
return 0;
}
LRESULT CALLBACK fnMain(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam) // Main Window Window Procedure
{
WndEventArgs Wea;
for(unsigned int i=0; i<dim(MainEventHandler); i++)
{
if(MainEventHandler[i].iMsg==msg)
{
Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
return (*MainEventHandler[i].fnPtr)(Wea);
}
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
int WINAPI _tWinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
wchar_t szClassName[]=L"Main";
WNDCLASSEX wc;
MSG messages;
HWND hWnd;
memset(&wc,0,sizeof(wc));
wc.lpszClassName=szClassName; wc.lpfnWndProc=fnMain;
wc.cbSize=sizeof (WNDCLASSEX); wc.hInstance=hIns;
wc.hCursor=LoadCursor(NULL,IDC_ARROW), wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;
wc.cbWndExtra=1*sizeof(void*);
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,275,625,280,160,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
And here is the header...
// T12_TCLib.h
#ifndef Main_h
#define Main_h
#define dim(x) (sizeof(x) / sizeof(x[0])) // Used in for loops of Window Procedures
#define LINE_BUFFER_COUNT 102 // How many lines to set up for scrolling
#define IDC_BTN_PROCESS_DATA 1500 // Control Id For Button to process data
#define IDC_BTN_CANCEL 1505 // Control Id for Cancel Button
#define RND_MAX 32767
struct WndEventArgs // This struct amalgamates the parameters of the
{ // Window Procedure together into one object. .NET
HWND hWnd; // does this to, e.g., 'PaintEventArgs'.
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
};
long fnMain_OnCreate (WndEventArgs& Wea); // These function proto-types just left are event
long fnMain_OnCommand (WndEventArgs& Wea); // or message handling procedures for the two Window
long fnMain_OnSetCursor (WndEventArgs& Wea); // Classes RegisterClassEx()'ed in this App, i.e.,
long fnMain_OnDestroy (WndEventArgs& Wea); // "Main" and "Process". Just below an EVENTHANDLER
long fnProcess_OnCreate (WndEventArgs& Wea); // object is defined, which contains two members.
long fnProcess_OnSize (WndEventArgs& Wea); // The first member, iMsg, is a #define from the
long fnProcess_OnVScroll (WndEventArgs& Wea); // various Window's header files, e.g., WM_CREATE
long fnProcess_OnHScroll (WndEventArgs& Wea); // is equal to 1; WM_COMMAND is equal to 273. The
long fnProcess_OnMouseWheel (WndEventArgs& Wea); // other member of EVENTHANDLER, fnPtr, is the run-
long fnProcess_OnSetCursor (WndEventArgs& Wea); // time address of the event/message handling proc-
long fnProcess_OnPaint (WndEventArgs& Wea); // edure associated with the respective or associated
long fnProcess_OnDestroy (WndEventArgs& Wea); // message held in iMsg. An array of these objects
struct EVENTHANDLER // are created to hold each event/event procedure
{ // address pair for each of the two Window Classes
unsigned int iMsg; // Registered. For example, MainEventHandler[] has
long (*fnPtr)(WndEventArgs&); // four elements 0 through 3.
}; // MainEventHandler[0].iMsg=1=WM_CREATE, and
const EVENTHANDLER MainEventHandler[]= // MainEventHandler[0].fnPtr will hold the runtime
{ // address of fnMain_OnCreate(). A for loop is used
{WM_CREATE, fnMain_OnCreate}, // in fnMain - the Window Procedure for the "Main"
{WM_COMMAND, fnMain_OnCommand}, // Window Class, to iterate through each array element
{WM_SETCURSOR, fnMain_OnSetCursor}, // trying to make a match between the MSG parameter
{WM_DESTROY, fnMain_OnDestroy} // of the Window Procedure, and the iMsg member of
}; // the EVENTHANDLER object. If a match is made, the
const EVENTHANDLER ProcessEventHandler[]= // associated function is called through its address
{ // held in the fnPtr member. Pretty, d*** slick!
{WM_CREATE, fnProcess_OnCreate}, // I wish I'd have been smart enough to figure some-
{WM_SIZE, fnProcess_OnSize}, // thing like this out for myself, but I learned it
{WM_VSCROLL, fnProcess_OnVScroll}, // from Douglas Boling who writes books for Microsoft
{WM_HSCROLL, fnProcess_OnHScroll}, // Press. And he gives credit for it to Ray Duncan.
{WM_MOUSEWHEEL, fnProcess_OnMouseWheel}, // So what you are really seeing here is an alter-
{WM_SETCURSOR, fnProcess_OnSetCursor}, // native to the switch construct usually used in
{WM_PAINT, fnProcess_OnPaint}, // Window Procedures to map incoming messages to the
{WM_DESTROY, fnProcess_OnDestroy} // code which handles each respective message. Its
}; // a much more elegant solution to the problem.
struct ScrollData // Further note how the dim macro is used in the for
{ // loop to determine the number of elements in each
wchar_t** pPtrs; // array.
int iNumLines;
int cxChar;
int cxCaps;
int cyChar;
int cxClient;
int cyClient;
int iMaxWidth;
};
extern "C" int _fltused = 1;
static unsigned int next = 1;
#endif
continued...