A head for Windows

A well-known UNIX utility called head displays the beginning lines of a file. Let's use a list box to write a similar program for Windows. HEAD, shown in Figure 6-7, lists all files and child subdirectories in the list box. You can choose a file to display by double-clicking on the filename with the mouse or by pressing the Enter key when the filename is selected. You can also change the subdirectory using either of these methods. The program displays up to 2 KB of the beginning of the file in the right side of the client area of HEAD's window.

HEAD.MAK

#--------------------

# HEAD.MAK make file

#--------------------

head.exe : head.obj head.def

link head, /align:16, NUL, /nod slibcew libw, head

rc head.exe

head.obj : head.c

cl -c -Gsw -Ow -W2 -Zp head.c

HEAD.C

/*---------------------------------------------

HEAD.C -- Displays Beginning (Head) of File

(c) Charles Petzold, 1990

---------------------------------------------*/

#include <windows.h>

#include <io.h>

#include <string.h>

#include <direct.h>

#define MAXPATH 100

#define MAXREAD 2048

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

long FAR PASCAL ListProc (HWND, WORD, WORD, LONG) ;

char sReadBuffer [MAXREAD] ;

FARPROC lpfnOldList ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName [] = "Head" ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = COLOR_WINDOW + 1 ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, "File Head",

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, nCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static BOOL bValidFile ;

static char szFile [16] ;

static HWND hwndList, hwndText ;

static OFSTRUCT ofs ;

static RECT rect ;

char szBuffer [MAXPATH + 1] ;

HDC hdc ;

int iHandle, i, iCount ;

PAINTSTRUCT ps ;

TEXTMETRIC tm ;

switch (message)

{

case WM_CREATE :

hdc = GetDC (hwnd) ;

SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

GetTextMetrics (hdc, &tm) ;

ReleaseDC (hwnd, hdc) ;

rect.left = 20 * tm.tmAveCharWidth ;

rect.top = 3 * tm.tmHeight ;

hwndList = CreateWindow ("listbox", NULL,

WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,

tm.tmAveCharWidth, tm.tmHeight * 3,

tm.tmAveCharWidth * 13 +

GetSystemMetrics (SM_CXVSCROLL),

tm.tmHeight * 10,

hwnd, 1,

GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

hwndText = CreateWindow ("static", getcwd (szBuffer, MAXPATH),

WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,

tm.tmAveCharWidth, tm.tmHeight,

tm.tmAveCharWidth * MAXPATH, tm.tmHeight,

hwnd, 2,

GetWindowWord (hwnd, GWW_HINSTANCE), NULL) ;

lpfnOldList = (FARPROC) GetWindowLong (hwndList, GWL_WNDPROC) ;

SetWindowLong (hwndList, GWL_WNDPROC,

(LONG) MakeProcInstance ((FARPROC) ListProc,

GetWindowWord (hwnd, GWW_HINSTANCE))) ;

SendMessage (hwndList, LB_DIR, 0x37, (LONG) (LPSTR) "*.*") ;

return 0 ;

case WM_SIZE :

rect.right = LOWORD (lParam) ;

rect.bottom = HIWORD (lParam) ;

return 0 ;

case WM_SETFOCUS :

SetFocus (hwndList) ;

return 0 ;

case WM_COMMAND :

if (wParam == 1 && HIWORD (lParam) == LBN_DBLCLK)

{

if (LB_ERR == (i = (WORD) SendMessage (hwndList,

LB_GETCURSEL, 0, 0L)))

break ;

SendMessage (hwndList, LB_GETTEXT, i,

(LONG) (char far *) szBuffer) ;

if (-1 != OpenFile (szBuffer, &ofs, OF_EXIST | OF_READ))

{

bValidFile = TRUE ;

strcpy (szFile, szBuffer) ;

getcwd (szBuffer, MAXPATH) ;

if (szBuffer [strlen (szBuffer) - 1] != '\\')

strcat (szBuffer, "\\") ;

SetWindowText (hwndText, strcat (szBuffer, szFile)) ;

}

else

{

bValidFile = FALSE ;

szBuffer [strlen (szBuffer) - 1] = '\0' ;

chdir (szBuffer + 1) ;

getcwd (szBuffer, MAXPATH) ;

SetWindowText (hwndText, szBuffer) ;

SendMessage (hwndList, LB_RESETCONTENT, 0, 0L) ;

SendMessage (hwndList, LB_DIR, 0x37,

(LONG) (LPSTR) "*.*") ;

}

InvalidateRect (hwnd, NULL, TRUE) ;

}

return 0 ;

case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;

SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;

SetBkColor (hdc, GetSysColor (COLOR_WINDOW)) ;

if (bValidFile && -1 != (iHandle =

OpenFile (szFile, &ofs, OF_REOPEN | OF_READ)))

{

i = read (iHandle, sReadBuffer, MAXREAD) ;

close (iHandle) ;

DrawText (hdc, sReadBuffer, i, &rect, DT_WORDBREAK |

DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX) ;

}

else

bValidFile = FALSE ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

} return DefWindowProc (hwnd, message, wParam, lParam) ;

}

long FAR PASCAL ListProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

if (message == WM_KEYDOWN && wParam == VK_RETURN)

SendMessage (GetParent (hwnd), WM_COMMAND, 1,

MAKELONG (hwnd, LBN_DBLCLK)) ;

return CallWindowProc (lpfnOldList, hwnd, message, wParam, lParam) ;

}

HEAD.DEF

;---------------------------------

; HEAD.DEF module definition file

;---------------------------------

NAME HEAD

DESCRIPTION 'File Head Program (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

ListProc

In ENVIRON, when we selected an environment variable—either with a mouse click or with the keyboard—the program displayed an environment string. If we used this select-display approach in HEAD, however, the program would be too slow because it would continually need to open and close files as you moved the selection through the list box. Instead, HEAD requires that the file or subdirectory be double-clicked. This presents a bit of a problem because list box controls have no automatic keyboard interface that corresponds to a mouse double-click. As we know, we shouldn't write Windows programs that require a mouse.

The solution? Window subclassing, of course. The list box subclass function in HEAD is called ListProc. It simply looks for a WM_KEYDOWN message with wParam equal to VK_RETURN and sends a WM_COMMAND message with an LBN_DBLCLK notification code back to the parent. The WM_COMMAND processing in WndProc uses the Windows function OpenFile to check for the selection from the list. If OpenFile returns an error, the selection is not a file, so it's probably a subdirectory. HEAD then uses chdir to change the subdirectory. It sends a LB_RESETCONTENT message to the list box to clear out the contents and a LB_DIR message to fill the list box with files from the new subdirectory.

The WM_PAINT message processing in WndProc opens the file using the Windows OpenFile function. This returns an MS-DOS handle to the file that can be passed to the normal C functions read and close. The contents of the file are displayed using DrawText.