FILER.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/******************************Module*Header*******************************\
*
* Module Name: Filer.c
*
*
* Filer: SDK sample
* + Simple File Management program with GUI front end.
* Demonstrates Win32 File I/O API and various User algorithms.
*
* Dependencies:
*
* (#defines)
* (#includes)
* -Enumdrv.h
* -Walk.h
* -Drvproc.h
* -Filer.h
*
\**************************************************************************/
#define STRICT
#include <windows.h>
#include <stdlib.h>
#include <stdarg.h>
#include "globals.h"
#include "enumdrv.h"
#include "drvproc.h"
#include "filer.h"

//
// Global Handles
//
HANDLE ghModule; // Process Instance handle

HWND ghwndMain; // Main window handle
HWND ghwndDrives; // Drives Toolbar window handle
HWND ghwndFunction; // Function Toolbar window handle
HWND ghwndCommand; // Command Line window handle
HWND ghwndDrv1,
ghwndDrv2;
HWND ghActiveChild = NULL; // Handle to active drive child window
HWND ghFocusWnd; // Handle to last listbox with focus


HANDLE ghHeap; // Application heap handle
HFONT ghFont;
HANDLE ghDrvThread = NULL;

HMENU ghMenu; // App Menu variables

//
// Global Data Items
//
BOOL gfDrvWndOrient = SIDE_BY_SIDE, // relative Drv child pos.
gfKeepCommandWin = FALSE;

DRVCHILDINFO gDrvChild1Info,
gDrvChild2Info;

LPDINFO glpDrives = NULL; // Root of Available Drives linked list

CRITICAL_SECTION gDrvCS; // Drive list critical section var.
CRITICAL_SECTION gHeapCS; // Global heap critical section.

TCHAR gszCommandLine[DIRECTORY_STRING_SIZE * 2];
TCHAR gszExtensions[NUM_EXTENSION_STRINGS][EXTENSION_LENGTH];

VKINFO gVKArray[NUM_VERSION_INFO_KEYS]; // .EXE version info array.
// See GetVersion() in DRVPROC.C

/***************************************************************************\
* WinMain
*
*
* History:
* 04-17-91
* Created.
\***************************************************************************/
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HANDLE hAccel;

//-- check to make sure we are running on Windows NT
if( GetVersion() & 0x80000000 ) { // 0x80000000 == Windows 3.1
MessageBox( NULL,
TEXT("Sorry, Filer must run under Windows NT. Filer will now terminate."),
TEXT("Error: Windows NT Required"), MB_OK );
return( 0 );
}

ghModule = hInstance;

if (!InitializeApp()) {
ErrorMsg(TEXT("Filer: InitializeApp failure!"));
return 0;
}
ShowWindow(ghwndMain, nCmdShow);

if (!(hAccel = LoadAccelerators (ghModule, MAKEINTRESOURCE(ACCEL_ID))))
ErrorMsg(TEXT("Filer: Load Accel failure!"));


while (GetMessage(&msg, NULL, 0, 0)) {
if( !TranslateAccelerator(ghwndMain, hAccel, &msg) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return 1;

UNREFERENCED_PARAMETER(lpCmdLine);
UNREFERENCED_PARAMETER(hPrevInstance);
}


/***************************************************************************\
* InitializeApp
*
* History:
* 5/5/92
* Created
\***************************************************************************/

BOOL InitializeApp(void)
{
WNDCLASS wc;

wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghModule;
wc.hIcon = LoadIcon(ghModule, MAKEINTRESOURCE(UI_FILERICON));
wc.hCursor = LoadCursor(ghModule, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);
wc.lpszMenuName = TEXT("FilerMenu");
wc.lpszClassName = TEXT("FilerClass");

if (!RegisterClass(&wc))
return(FALSE);

wc.lpfnWndProc = DrvWndProc;
wc.hIcon = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("DrvClass");

if (!RegisterClass(&wc))
return(FALSE);

wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = DriveBarProc;
wc.hbrBackground = (HBRUSH)(COLOR_BTNSHADOW);
wc.lpszClassName = TEXT("DriveBarClass");

if (!RegisterClass(&wc))
return(FALSE);

wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = TextWndProc;
wc.hbrBackground = (HBRUSH)(COLOR_INACTIVECAPTION);
wc.lpszClassName = TEXT("TextClass");

if (!RegisterClass(&wc))
return(FALSE);

ghMenu = LoadMenu(ghModule, TEXT("FilerMenu"));

ghwndMain = CreateWindow(TEXT("FilerClass"),
TEXT("Filer"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
MAIN_WIDTH,
MAIN_HEIGHT,
HWND_DESKTOP,
ghMenu,
ghModule,
NULL);

if (ghwndMain == NULL)
return(FALSE);

return(TRUE);
}


/***************************************************************************\
* MainWndProc
*
* History:
* 05-01-92 Created.
\***************************************************************************/

LRESULT WINAPI MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {

case WM_CREATE:{
LOGFONT lf;
HDC hDC;
HGDIOBJ hOldFont;
TEXTMETRIC tm;

DWORD dwThreadID;

//
// Initialize drive list and Set Directory critical sections.
//
InitializeCriticalSection(&gDrvCS);
InitializeCriticalSection(&gHeapCS);

ghDrvThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)EnumDrives,
(LPVOID)&glpDrives,
0, &dwThreadID);

//
// Create the application's heap
//
ghHeap = HeapCreate( 0, (DWORD)sizeof(DRVCHILDINFO), 0);
if( ghHeap == NULL )
ErrorMsg(TEXT("Main Create: Failed in Creating Heap"));

//
// Compute default application font by creating a bold version
// of the system default icon font.
//
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf),
(PVOID) &lf, FALSE);

hDC = GetDC(hwnd);

// this is the height for 8 point size font in pixels.
// (1 point = 1/72 in.)
// Japanese fonts have different characteristics and don't look good in bold
if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
lf.lfHeight = 10 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;
else {
lf.lfHeight = 8 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;
lf.lfWeight = BOLD_FONT;
}

ghFont = CreateFontIndirect(&lf);
hOldFont = SelectObject(hDC, ghFont);
GetTextMetrics(hDC, &tm);

if(hOldFont)
SelectObject(hDC, hOldFont);

ReleaseDC(hwnd, hDC);

//
// Create Drive windows
//
gDrvChild1Info.hParent = hwnd;
gDrvChild2Info.hParent = hwnd;

ghwndDrv1 = CreateWindow(TEXT("DrvClass"), NULL,
WS_CHILD |
WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) 1, ghModule,
(LPVOID)&gDrvChild1Info);

ghActiveChild = ghwndDrv1;

//
// Set initial focus to Drive Child 1's Directory listbox.
//
ghFocusWnd = ((LPCINFO)GetWindowLong(ghwndDrv1, GWL_USERDATA))->hDirLB;

ghwndDrv2 = CreateWindow(TEXT("DrvClass"), NULL,
WS_CHILD |
WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) 2, ghModule,
(LPVOID)&gDrvChild2Info);

//
// Create DriveBar, FunctionBar and Command windows
//
ghwndDrives = CreateWindow(TEXT("DriveBarClass"), NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, 0, 0,
hwnd, (HMENU) 3, ghModule,
(LPVOID)NULL);

ghwndFunction = CreateDialog(ghModule,
TEXT("FunctionBar"),
hwnd,
(DLGPROC)FunctionBarProc);

ghwndCommand = CreateWindow(TEXT("EDIT"), NULL,
ES_AUTOHSCROLL | ES_LEFT | WS_BORDER |
ES_NOHIDESEL | WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd,
(HMENU) COMMAND_ID,
ghModule,
NULL);

//
// Compute height of command window from font; store in window info.
// Set command window to default font.
//
SetWindowLong( ghwndCommand, GWL_USERDATA,
tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6);
SendMessage(ghwndCommand, WM_SETFONT, (WPARAM)ghFont, (LPARAM)FALSE);

//
// Load String table entries
//
LoadString( ghModule, STR_EXE, &gszExtensions[0][0], EXTENSION_LENGTH);
LoadString( ghModule, STR_COM, &gszExtensions[1][0], EXTENSION_LENGTH);
LoadString( ghModule, STR_CMD, &gszExtensions[2][0], EXTENSION_LENGTH);
LoadString( ghModule, STR_BAT, &gszExtensions[3][0], EXTENSION_LENGTH);

UpdateDrivesMenu(ghMenu, ghDrvThread);

return(1);
}

case WM_COMMAND:{

//
// The menu Identifiers for the drives are (potentially)
// MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
// same case, so we will put the Menu ID in lParam, and
// MM_DRIVE_NUM in LOWORD(wParam).
//
if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
(LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
lParam = LOWORD(wParam);
wParam = MM_DRIVE_NUM;
}

switch (LOWORD(wParam)) {
//
// If a drive is selected from the Drives menu, or clicked
// on the drives toolbar, the currently active child will
// switch to this drive. Message 'unconverted' (see top of
// WM_COMMAND case), and sent to DriveBarProc
//
case MM_DRIVE_NUM:{

SendMessage(ghwndDrives, WM_COMMAND,
(WPARAM)lParam, (LPARAM)NULL);
return(1);
}

//
// Passes these WM_COMMAND messages to the appropriate active child
// window proc for processing
//
case MM_TAB:
case MM_ESCAPE:
case MM_OPEN:
case MM_COPY:
case MM_DELETE:
case MM_MOVE:
case MM_RENAME:
case MM_MKDIR:
case MM_EXPAND:
case MM_VERSION:{

SendMessage(ghActiveChild, WM_COMMAND, wParam, lParam);
return(1);
}

case MM_EXIT:{
SendMessage(ghwndMain, WM_CLOSE, wParam, lParam);
return(1);
}

//
// Creates the drive enumeration thread to re-enumerate the
// available drives in the main menu. Also sends a refresh
// to the active drive child, and repaints the window.
//
case MM_REFRESH: {
DWORD dwThreadID;

//
// Initialize/Refresh Drives linked list
//

if( WaitForSingleObject(ghDrvThread, 0) != WAIT_TIMEOUT ){

//
// Close previous Drive Thread handle before creating new handle.
//
CloseHandle( ghDrvThread );

ghDrvThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)EnumDrives,
(LPVOID)&glpDrives,
0, &dwThreadID);

//
// Refresh active child, drive toolbar, and drives menu
//
SendMessage(ghActiveChild, WM_COMMAND, wParam, lParam);
SendMessage(ghwndDrives, WM_COMMAND, wParam, lParam);
UpdateDrivesMenu(ghMenu, ghDrvThread);

//
// Mark all for repaint
//
InvalidateRect(hwnd,NULL,TRUE);

}
else
MessageBeep(MB_ICONASTERISK);

return(1);
}

//
// Swaps the directory and file list boxes of the active drv child.
//
case MM_SWAP:{
LPCINFO lpCInfo;
RECT rect;

lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

//
// Switch the flag which indicates which side the Directory
// LB is on. This is used by the WM_SIZE case of DrvWndProc.
//
lpCInfo->fDirLeft = !lpCInfo->fDirLeft;

//
// Send size message with current size to active child,
// in order to redraw the listboxes.
//
if( !GetClientRect( ghActiveChild, &rect ) )
return(0);

SendMessage( ghActiveChild, WM_SIZE, SIZENORMAL,
MAKELONG( rect.right - rect.left,
rect.bottom - rect.top) );
return(1);
}

case MM_KEEPCMD:{

gfKeepCommandWin = !gfKeepCommandWin;

if( gfKeepCommandWin )
CheckMenuItem( ghMenu, MM_KEEPCMD,
MF_BYCOMMAND | MF_CHECKED);
else
CheckMenuItem( ghMenu, MM_KEEPCMD,
MF_BYCOMMAND | MF_UNCHECKED);
}
break;

//
// Toggles the relative Drive Child orientaion between
// Over/under and side/side. gfDrvWndOrient is a flag checked
// by WM_SIZE to size Drv children
//
case MM_ORIENT:{
RECT rect;

if( gfDrvWndOrient == OVER_UNDER )
gfDrvWndOrient = SIDE_BY_SIDE;
else
gfDrvWndOrient = OVER_UNDER;

//
// Send size message with current size to self (main window),
// in order to redraw the Drv children.
//
if( !GetClientRect( hwnd, &rect ) )
return(0);

SendMessage( hwnd, WM_SIZE, SIZENORMAL,
MAKELONG( rect.right - rect.left,
rect.bottom - rect.top) );

InvalidateRect(ghwndDrv1,NULL,TRUE);
InvalidateRect(ghwndDrv2,NULL,TRUE);

return(1);
}

//
// Toggles the active drive child. Sent from menu.
// This behaves the same as a WM_MOUSEACTIVATE in one of the
// Drive children. The PostMessage is so the current Active
// child will not process the MM_TOGGLE message until after it
// is no longer active.
//
case MM_ACTIVEDRV:{
LPCINFO lpCInfo;

PostMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
(LPARAM)NULL);

if( ghActiveChild == ghwndDrv1 )
ghActiveChild = ghwndDrv2;
else
ghActiveChild = ghwndDrv1;

SendMessage(ghActiveChild, WM_COMMAND, (WPARAM)MM_TOGGLE,
(LPARAM)NULL);

// change drive button
lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);
SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
(LPARAM)lpCInfo->lpDriveInfo);
return(1);
}

//
// Sent by the Command line Edit control.
//
case COMMAND_ID:{

if( HIWORD(wParam) == EN_SETFOCUS )
ghFocusWnd = ghwndCommand;
}
break;

//
// Launches the About DialogBox.
//
case MM_ABOUT:{
TCHAR lpBuffer[128];

if (DialogBox(ghModule, TEXT("AboutBox"), ghwndMain, (DLGPROC)AboutProc) == -1) {
LoadString(ghModule, IDS_ABOUTDLGERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}
return(1);
}

default:
return( DefWindowProc(hwnd, message, wParam, lParam) );
}
return(1);
}
//
// Whenever the window is resized, its children have to be
// resized accordingly. The GetWindowLong values are the height
// of the windows queried by this function, and are set in the
// WM_CREATE cases of their respective WNDPROCs.
//
case WM_SIZE:{
int DrvWndHeight;

//
// Always put the command window at the bottom of the frame window
//
MoveWindow(ghwndCommand,
0,
HIWORD(lParam) - GetWindowLong(ghwndCommand, GWL_USERDATA),
LOWORD(lParam),
GetWindowLong(ghwndCommand, GWL_USERDATA),
TRUE);
//
// Always put the drives toolbar at the top of the frame window
//
MoveWindow(ghwndDrives,
0,
0,
LOWORD(lParam),
GetWindowLong(ghwndDrives, GWL_USERDATA),
TRUE);

//
// Always put the Function window just below the drives toolbar.
//
MoveWindow(ghwndFunction,
0,
GetWindowLong(ghwndDrives, GWL_USERDATA),
LOWORD(lParam),
GetWindowLong(ghwndFunction, GWL_USERDATA),
TRUE);

//
// Always size the Drive Children between the Drives and Command
// windows. The width is set so that borders overlap.
//

if( gfDrvWndOrient == OVER_UNDER ){

DrvWndHeight = ( HIWORD(lParam) -
GetWindowLong(ghwndDrives, GWL_USERDATA) -
GetWindowLong(ghwndFunction, GWL_USERDATA) -
GetWindowLong(ghwndCommand, GWL_USERDATA) ) / 2;

MoveWindow(ghwndDrv1,
-1,
GetWindowLong(ghwndDrives, GWL_USERDATA)+
GetWindowLong(ghwndFunction, GWL_USERDATA),
LOWORD(lParam) + 2,
DrvWndHeight,
TRUE);

MoveWindow(ghwndDrv2,
-1,
GetWindowLong(ghwndDrives, GWL_USERDATA)+
GetWindowLong(ghwndFunction, GWL_USERDATA) +
DrvWndHeight,
LOWORD(lParam) + 2,
DrvWndHeight,
TRUE);
}
else{

DrvWndHeight = HIWORD(lParam) -
GetWindowLong(ghwndDrives, GWL_USERDATA) -
GetWindowLong(ghwndFunction, GWL_USERDATA) -
GetWindowLong(ghwndCommand, GWL_USERDATA);

MoveWindow(ghwndDrv1,
-1,
GetWindowLong(ghwndDrives, GWL_USERDATA)+
GetWindowLong(ghwndFunction, GWL_USERDATA),
LOWORD(lParam)/2 + 1,
DrvWndHeight,
TRUE);

MoveWindow(ghwndDrv2,
LOWORD(lParam)/2,
GetWindowLong(ghwndDrives, GWL_USERDATA)+
GetWindowLong(ghwndFunction, GWL_USERDATA),
LOWORD(lParam)/2 + 1,
DrvWndHeight,
TRUE);
}

return(1);
}

case WM_DESTROY: {
//
// Close last drive thread handle, the global heap and it's corresponding critical section,
// the created font, and the Drive list critical section.
//
CloseHandle( ghDrvThread );

EnterCriticalSection(&gHeapCS);
HeapDestroy(ghHeap);
LeaveCriticalSection(&gHeapCS);

DeleteObject(ghFont);

DeleteCriticalSection(&gDrvCS);
DeleteCriticalSection(&gHeapCS);

PostQuitMessage(0);
return(1);
}

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


/***************************************************************************\
* AboutProc
*
* About dialog proc.
*
* History:
* 05-13-92 Created.
\***************************************************************************/

LRESULT WINAPI AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:{
return TRUE;
}

case WM_COMMAND:{
if (wParam == IDOK)
EndDialog(hDlg, wParam);
break;
}
}

return FALSE;

UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(hDlg);
}


/***************************************************************************\
*
* DriveBarProc()
*
* Drive Toolbar procedure for displaying available drive Icons.
* A bitmap button is displayed corresponding to the drive type of the
* given drive, with the drive letter alongside.
* ghwndDrives is the global handle assoc. w/ this window procedure.
*
*
* History:
* 6/9/92
* Created.
*
\***************************************************************************/

LRESULT WINAPI DriveBarProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hDrvBmp[NUM_BITMAPS];
static HBRUSH hBrush; // background brush
static int nDrvEntryWidth; // width of button/letter entry
static int yVal; // y value in toolbar for top left of bmp
static LPBINFO lpDrvButtonRoot;
static int nActiveDrvIndex;
TCHAR lpBuffer[128];

switch (message)
{
case WM_CREATE:{
HDC hDC;
HGDIOBJ hOldFont;
TEXTMETRIC tm;
LONG lHeight;


lpDrvButtonRoot = NULL;

//
// Load drive button bitmaps.
//
for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
hDrvBmp[yVal] = LoadBitmap( ghModule,
MAKEINTRESOURCE(UB_BMP_MARKER + yVal) );

//
// Sets background color of Toolbar non-modal dialog children.
//
hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));

hDC = GetDC(hwnd);

hOldFont = SelectObject(hDC, ghFont);
GetTextMetrics(hDC, &tm);

// base the height of the window on size of text
// Different display height is optimal if on a Japanese language system
//
if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 8;
else
lHeight = tm.tmHeight + GetSystemMetrics(SM_CYBORDER) + 6;

//
// saved the window height, drive button entry width
// and button y starting value for later reference
//
SetWindowLong(hwnd, GWL_USERDATA, lHeight);

//
// Width of one button entry = spacing, button, sm. space,
// drive letter, spacing.
//
nDrvEntryWidth = DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
DRIVE_LETTER_SPACING + tm.tmAveCharWidth +
DRIVE_BITMAP_SPACING;

//
// Center bitmaps (by height) in drive toolbar.
//
yVal = (lHeight - DRIVE_BITMAP_HEIGHT)/2;

SelectObject(hDC, hOldFont);
ReleaseDC(hwnd, hDC);

SendMessage(hwnd, WM_COMMAND, (WPARAM)MM_REFRESH, (LPARAM)NULL);

break;
}

case WM_COMMAND:{
//
// The button Identifiers for the drives are (potentially)
// MM_DRIVE_NUM + 0 thru MM_DRIVE_NUM + 25. They all go to the
// same case, so we will put the Menu ID in lParam, and
// MM_DRIVE_NUM in LOWORD(wParam).
//
if( (LOWORD(wParam) - MM_DRIVE_NUM) <= 25 &&
(LOWORD(wParam) - MM_DRIVE_NUM) >= 0 ){
lParam = LOWORD(wParam);
wParam = MM_DRIVE_NUM;
}

switch( LOWORD(wParam) ){
case MM_REFRESH:{

LPDINFO lpWalk;
LPBINFO lpBWalk, lpBHold;
LPCINFO lpCInfo;
int xVal = 0;
int nCount = MM_DRIVE_NUM;
TCHAR lpBuffer[128];

lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

//
// Wait for Drive Thread to complete, if necessary.
//
WaitForSingleObject(ghDrvThread, INFINITE);
EnterCriticalSection(&gDrvCS);

//
// Traverse DRVINFO linked list, creating drive buttons and
// allocating corresp. structures as necessary.
//
lpWalk = glpDrives;
lpBWalk = lpDrvButtonRoot;

while( lpWalk != NULL ){
if( lpBWalk == NULL ){ //If at the end of the button list

// Allocate a LPBINFO (button) structure
EnterCriticalSection(&gHeapCS);
lpBWalk = (LPBINFO)HeapAlloc(ghHeap, 0, (DWORD)sizeof(BINFO) );
LeaveCriticalSection(&gHeapCS);

// Create a button window
lpBWalk->hButton = (HANDLE)CreateWindow(TEXT("BUTTON"),
lpWalk->DriveName,
WS_CHILD | WS_VISIBLE |
BS_OWNERDRAW,
xVal + DRIVE_BITMAP_SPACING,
yVal,
DRIVE_BITMAP_WIDTH,
DRIVE_BITMAP_HEIGHT,
hwnd,
(HMENU)nCount,
ghModule,

NULL); 

// Insert structure into list
if( lpDrvButtonRoot == NULL)
lpDrvButtonRoot = lpBHold = lpBWalk;
else{
lpBHold->next = lpBWalk;
lpBWalk->next = NULL;
}

}

// An LPBINFO (button) structure exists: now initialize

// Set Title of Button (Drive Letter)
SetWindowText(lpBWalk->hButton, lpWalk->DriveName);

// Set Child Window ID for Button
SetMenu( lpBWalk->hButton, (HMENU)nCount);

// Determine button up/down status
if( lpCInfo->lpDriveInfo == lpWalk ){
nActiveDrvIndex = nCount;
lpBWalk->fButtonDown = TRUE;
}
else
lpBWalk->fButtonDown = FALSE;

// Set a pointer to the corresponding drive in Drive list
lpBWalk->lpDrive = lpWalk;

nCount++;
xVal += nDrvEntryWidth;
lpBHold = lpBWalk;
lpBWalk = lpBWalk->next;

lpWalk = lpWalk->next;
}

LeaveCriticalSection(&gDrvCS);

//
// Free any remaining button windows.
//
while( lpBWalk != NULL ){
// NULL out new end of list
lpBHold->next = NULL;

// Assign pointer to doomed node
lpBHold = lpBWalk;
lpBWalk = lpBWalk->next;

// Free doomed node resources
if( !DestroyWindow(lpBHold->hButton) ) {
LoadString(ghModule, IDS_DRVBUTTNERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}

EnterCriticalSection(&gHeapCS);
HeapFree(ghHeap, 0, (LPVOID)lpBHold);
LeaveCriticalSection(&gHeapCS);
}

SendMessage(hwnd, WM_PAINT, (WPARAM)NULL, (LPARAM)NULL);
break;
}


//
// switches the drive button to the newly active drv child's
// current drive. Called by WM_MOUSEACTIVATE in DrvWndProc,
// as well as ChangeDrive.
// lParam contains the drive linked list pointer of the active
// drv child's LPCINFO struct.
//
case MM_ACTIVEDRV:{
LPBINFO lpBWalk = lpDrvButtonRoot;
int nCount = 0;

//
// 'unpush' old active button
//
for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
lpBWalk = lpBWalk->next;
lpBWalk->fButtonDown = FALSE;

InvalidateRect(lpBWalk->hButton, NULL, FALSE);

//
// change active drive to new before redrawing old.
// 'push' new active button
//
lpBWalk = lpDrvButtonRoot;
nCount = MM_DRIVE_NUM;
while( lpBWalk->lpDrive != (LPDINFO)lParam){
lpBWalk = lpBWalk->next;
nCount++;
}

nActiveDrvIndex = nCount;

lpBWalk->fButtonDown = TRUE;

InvalidateRect(lpBWalk->hButton, NULL, FALSE);

break;
}

//
// Changes drive of active child. ButtonID in lParam.
//
case MM_DRIVE_NUM:{

LPBINFO lpBWalk = lpDrvButtonRoot;
int nCount = 0;
TCHAR szDrvBuff[DIRECTORY_STRING_SIZE];

//
// if drive chosen is already current drive, leave.
//
if( nActiveDrvIndex == (int)lParam )
break;

//
// unpush' old active button
//
for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
lpBWalk = lpBWalk->next;
lpBWalk->fButtonDown = FALSE;

//
// change active drive to new before redrawing old.
//
nActiveDrvIndex = (int)lParam;

InvalidateRect(lpBWalk->hButton, NULL, FALSE);

//
// 'push' new active button
//
lpBWalk = lpDrvButtonRoot;

for( nCount = MM_DRIVE_NUM; nCount < nActiveDrvIndex; nCount++)
lpBWalk = lpBWalk->next;
lpBWalk->fButtonDown = TRUE;

InvalidateRect(lpBWalk->hButton, NULL, FALSE);

GetWindowText(lpBWalk->hButton, szDrvBuff,
DIRECTORY_STRING_SIZE);

if( !ChangeDrive(szDrvBuff, (DWORD)nActiveDrvIndex) ){
LoadString(ghModule, IDS_CHNGDRVERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(0);
}

break;
}
}
return(1);
}

//
// Sent by all created buttons for initialization purposes.
//
case WM_MEASUREITEM:{
LPMEASUREITEMSTRUCT lpMIS;

lpMIS = (LPMEASUREITEMSTRUCT)lParam;

lpMIS->CtlType = ODT_BUTTON;
lpMIS->CtlID = (UINT)wParam;
lpMIS->itemWidth = DRIVE_BITMAP_WIDTH;
lpMIS->itemHeight = DRIVE_BITMAP_HEIGHT;

return(1);
}

//
// Sent by owner draw drive buttons when needing redrawing.
//
case WM_DRAWITEM:{
LPBINFO lpBWalk = lpDrvButtonRoot;
int nCount;
int nBmpIndex;
HDC hDC;
HDC hCompatDC;
HGDIOBJ hOldBitmap;
TCHAR szDrvBuff[DIRECTORY_STRING_SIZE];
LPDRAWITEMSTRUCT lpDIS;

lpDIS = (LPDRAWITEMSTRUCT)lParam;

for( nCount = MM_DRIVE_NUM; nCount < (int)wParam; nCount++)
lpBWalk = lpBWalk->next;

//
// If not the current selected button, handle button stuff.
//
if( (int)wParam != nActiveDrvIndex ){
//
// mousebutton is down...
//
if( lpDIS->itemAction & ODA_SELECT ){
//
// left button region, 'unpush' button
//
if( lpDIS->itemState == (UINT)ODS_FOCUS )
lpBWalk->fButtonDown = FALSE;
//
// clicked on a button, draw 'pushed' button
//
if( lpDIS->itemState == (UINT)(ODS_SELECTED | ODS_FOCUS))
lpBWalk->fButtonDown = TRUE;
}
}

//
// draw current state of button.
//
GetWindowText(lpDIS->hwndItem, szDrvBuff,
DIRECTORY_STRING_SIZE);

szDrvBuff[1] = TEXT('\0');

hCompatDC = CreateCompatibleDC(lpDIS->hDC);
hOldBitmap = CreateCompatibleBitmap(hCompatDC,
DRIVE_BITMAP_WIDTH,
DRIVE_BITMAP_HEIGHT);

nBmpIndex = GetDriveBitmap(lpBWalk);

SelectObject( hCompatDC, hDrvBmp[nBmpIndex] );

if( !hOldBitmap ) {
LoadString(ghModule, IDS_SELOBJERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}
if( !BitBlt(lpDIS->hDC, lpDIS->rcItem.left, lpDIS->rcItem.top,
DRIVE_BITMAP_WIDTH,
DRIVE_BITMAP_HEIGHT,
hCompatDC, 0, 0, SRCCOPY) ) {
LoadString(ghModule, IDS_BITBLTERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}

SelectObject( hCompatDC, hOldBitmap);
DeleteDC(hCompatDC);

hDC = GetDC(hwnd);
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT) );
SetBkColor(hDC, GetSysColor(COLOR_BTNSHADOW) );

// Allow for a different aesthetic for Japanese systems
if (GetUserDefaultLangID() == LANG_JAPANESE)
TextOut(hDC,
((int)(wParam - MM_DRIVE_NUM) * nDrvEntryWidth) +
DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
DRIVE_LETTER_SPACING,
GetSystemMetrics(SM_CYBORDER)/2,
szDrvBuff, 1);
else
TextOut(hDC,
((int)(wParam - MM_DRIVE_NUM) * nDrvEntryWidth) +
DRIVE_BITMAP_SPACING + DRIVE_BITMAP_WIDTH +
DRIVE_LETTER_SPACING,
(GetSystemMetrics(SM_CYBORDER) + 6)/2,
szDrvBuff, 1);

SetBkMode(hDC, OPAQUE);

ReleaseDC(hwnd, hDC);

break;
}


case WM_PAINT:{
HDC hCompatDC;
RECT rc;
PAINTSTRUCT ps;

//
// Paint btnshadow background.
//
GetClientRect(hwnd, &rc);

BeginPaint(hwnd, &ps);

hCompatDC = CreateCompatibleDC(ps.hdc);
FillRect(ps.hdc, &rc, hBrush);

EndPaint(hwnd, &ps);

return(TRUE);
}

case WM_DESTROY:{
DeleteObject(hBrush);

for(yVal = 0; yVal < NUM_BITMAPS; yVal++)
DeleteObject(hDrvBmp[yVal]);

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


/***************************************************************************\
*
* GetDriveBitmap()
*
* Determines the appropriate index into the drive button bitmap array
* (hDrvBmp[]), given a pointer to a drive info structure (LPDINFO)
*
* lpWalk - pointer to LPDINFO structure.
* lpCurrentDrv - pointer to current drive of active child.
*
*
* History:
* 6/16/92
* Created.
*
\***************************************************************************/
int GetDriveBitmap(LPBINFO lpBWalk)
{
int nBmpIndex;

EnterCriticalSection(&gDrvCS);

switch( lpBWalk->lpDrive->DriveType ){
case DRIVE_REMOVABLE:{
nBmpIndex = UB_FLOPPY1 - UB_BMP_MARKER;
break;
}

case DRIVE_REMOTE:{
nBmpIndex = UB_REMOTE1 - UB_BMP_MARKER;
break;
}

case DRIVE_CDROM:{
nBmpIndex = UB_CD1 - UB_BMP_MARKER;
break;
}

case DRIVE_FIXED:
default:{
nBmpIndex = UB_FIXED1 - UB_BMP_MARKER;
break;
}
}

LeaveCriticalSection(&gDrvCS);

if( lpBWalk->fButtonDown == TRUE )
nBmpIndex++;

return(nBmpIndex);
}


/***************************************************************************\
*
* ChangeDrive()
*
* Changes the current drive of the active child. Called by the MM_DRIVE_NUM
* cases in MainWndProc and DriveBarProc. This is caused by choosing a
* Drive menu item or selecting a drive button from the drive toolbar.
*
* lpszDriveName - points to a buffer containing the name of the drive
* DriveID - points to the ID of the Menu item or button, which
* corresponds to the index into the drives linked list
* of the new drive.
*
* History:
* 6/20/92
* Created.
*
\***************************************************************************/
BOOL ChangeDrive(LPTSTR lpszDriveName, DWORD DriveIndex)
{
LPCINFO lpCInfo;
LPDINFO lpWalk;
DWORD dwLoop;
UINT nDriveType;
TCHAR lpBuffer[128];

//
// Retrieve active child handle.
//
if( (ghActiveChild != ghwndDrv1) &&
(ghActiveChild != ghwndDrv2) ){
LoadString(ghModule, IDS_DRVNOTACTVE, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(0);
}

//
// Retrieving the child window's DRVCHILDINFO data
//
lpCInfo = (LPCINFO)GetWindowLong(ghActiveChild, GWL_USERDATA);

//
// Enter Drive list critical section
//
EnterCriticalSection(&gDrvCS);

//
// if removable drive, check for existing media.
//
nDriveType = GetDriveType(lpszDriveName);
if( nDriveType == DRIVE_REMOVABLE ||
nDriveType == DRIVE_CDROM ){
dwLoop = (DWORD)IDOK;

LoadString(ghModule, IDS_INSRTMEDIA, lpBuffer, sizeof(lpBuffer));

while( !CheckRM(lpszDriveName) && (dwLoop == (DWORD)IDOK) ){
dwLoop = (DWORD)MessageBox(ghwndMain, lpBuffer, lpszDriveName, MB_OKCANCEL);
}

if( dwLoop == (DWORD)IDCANCEL ){
SendMessage(ghwndDrives, WM_COMMAND, MM_ACTIVEDRV,
(LPARAM)lpCInfo->lpDriveInfo);
LeaveCriticalSection(&gDrvCS);
return(0);
}
}

//
// set lpDriveInfo member to associated drive struct.
//
lpWalk = glpDrives;
for( dwLoop = 0; dwLoop < DriveIndex - MM_DRIVE_NUM;
dwLoop++)
lpWalk = lpWalk->next;

lpCInfo->lpDriveInfo = lpWalk;

lstrcpy(lpCInfo->CaptionBarText, lpWalk->DriveName);

LeaveCriticalSection(&gDrvCS);

//
// This will terminate any currently running drive thread.
//
SendMessage(ghActiveChild, WM_COMMAND, MM_ESCAPE, (LPARAM)0);
lpCInfo->fEscape = FALSE;

//
// enact the drive change.
//
PostMessage(ghActiveChild, WM_COMMAND, MM_REFRESH, (LPARAM)0);

return(1);
}


/***************************************************************************\
*
* FunctionBarProc
*
* ToolBar Window procedure for displaying File I/O functions.
* ghwndFunction is the global handle assoc. w/ this Dlg procedure.
*
* History:
* 6/8/92
* Created.
*
\***************************************************************************/
LRESULT WINAPI FunctionBarProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBRUSH hBrush;

switch (message){
case WM_INITDIALOG:{
HWND hButton;
RECT rc;

hButton = GetDlgItem(hDlg, MM_COPY);

GetWindowRect(hButton, &rc);

SetWindowLong(hDlg, GWL_USERDATA, rc.bottom - rc.top);

//
// Sets background color of Toolbar non-modal dialog children.
//
hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));

return(FALSE);
}

case WM_PAINT:{
RECT rc;
PAINTSTRUCT ps;

//
// Paint btnshadow background.
//
GetClientRect(hDlg, &rc);
InvalidateRect(hDlg, &rc, FALSE);

BeginPaint(hDlg, &ps);

FillRect(ps.hdc, &rc, hBrush);

EndPaint(hDlg, &ps);

return(TRUE);
}

//
// Passes button messages ( = file I/O function messages )
// to active Drv child.
//
case WM_COMMAND:{
switch(wParam){
case MM_COPY:
case MM_MOVE:
case MM_DELETE:
case MM_RENAME:
case MM_MKDIR:{
SendMessage(ghActiveChild, message, wParam, lParam);
return(TRUE);
}
}
}

case WM_DESTROY:{
DeleteObject(hBrush);
break;
}
}

return(FALSE);
}


/***************************************************************************\
*
* RunCommandItem()
*
*
* History:
* 5/26/93
* Created.
*
\***************************************************************************/
BOOL RunCommandItem(LPCINFO lpCInfo)
{
TCHAR szCmdLine[DIRECTORY_STRING_SIZE * 2] = TEXT("cmd ");
LPTSTR lpszHold;
TCHAR lpBuffer[128];

STARTUPINFO si;
PROCESS_INFORMATION pi;

//
// Add the CMD.EXE parameter to keep or kill cmd sessions when done.
//
if( gfKeepCommandWin )
lstrcat( szCmdLine, TEXT("/k ") );
else
lstrcat( szCmdLine, TEXT("/c ") );

//
// Add command line edit control text.
//
lpszHold = TStrChr(szCmdLine, TEXT('\0'));

if( SendMessage( ghwndCommand, WM_GETTEXT,
DIRECTORY_STRING_SIZE,
(LPARAM)lpszHold) == LB_ERR ){
LoadString(ghModule, IDS_SRCSTRNGERR, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(0);
}

//
// Attempt to spawn command shell with entry as parameter.
//
si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpDesktop = NULL;
si.lpTitle = NULL;
si.dwFlags = 0;
si.cbReserved2 = 0;
si.lpReserved2 = NULL;

SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

if( !CreateProcess(NULL, (LPTSTR)szCmdLine, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
NULL, lpCInfo->CaptionBarText, &si, &pi) ){
LoadString(ghModule, IDS_CANTSPAWN, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(0);
}

CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );

SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL);

return(1);
}


/***************************************************************************\
*
* UpdateDrivesMenu()
*
* Adds current drives from the glpDrives linked list to the TEXT('Drives') menu
*
* Input: hDrivesMenu - handle to TEXT('Drives') Menu
* hThread - used to wait for drives thread to terminate
*
* History:
* 5/14/92
* Created.
*
\***************************************************************************/
BOOL UpdateDrivesMenu(HMENU hMenu, HANDLE hThread)
{
HMENU hDrivesMenu;
int NumMenuItems;
DWORD dwLoop;
LPDINFO lpWalk;
TCHAR lpBuffer[128];

//
// Remove list of drive menu items from Drive menu, if any.
//
hDrivesMenu = GetSubMenu( hMenu, DRIVE_MENU_NUM);
if( !hDrivesMenu ){
LoadString(ghModule, IDS_MENUERR1, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(FALSE);
}

if( (NumMenuItems = GetMenuItemCount(hDrivesMenu)) == -1) {
LoadString(ghModule, IDS_MENUERR2, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}

//
// Delete previous menu items.
//
for( dwLoop = 0; dwLoop < (DWORD)NumMenuItems; dwLoop++)
if( !DeleteMenu( hDrivesMenu, 0,
MF_BYPOSITION) ){
LoadString(ghModule, IDS_MENUERR3, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
return(FALSE);
}

//
// Wait for Enumdrv Thread to terminate, and
// enter drive list critical section
//
WaitForSingleObject(hThread, INFINITE);
EnterCriticalSection(&gDrvCS);

//
// Fill drive menu from glpDrives linked list
//
NumMenuItems = 0;
lpWalk = glpDrives;

while(lpWalk != NULL){
if( !InsertMenu( hDrivesMenu, NumMenuItems, MF_STRING |
MF_BYPOSITION | MF_ENABLED, MM_DRIVE_NUM + NumMenuItems,
lpWalk->DriveName)) {
LoadString(ghModule, IDS_MENUERR4, lpBuffer, sizeof(lpBuffer));
ErrorMsg(lpBuffer);
}
NumMenuItems++;
lpWalk = lpWalk->next;
}

LeaveCriticalSection(&gDrvCS);

return(TRUE);
}


/***************************************************************************\
*
* ErrorMsg()
*
* Displays a Message Box with a given error message.
*
* History:
* 5/28/92
* Created.
*
\***************************************************************************/
void ErrorMsg(LPTSTR szMsg)
{
TCHAR szHold[DIRECTORY_STRING_SIZE + 1];

lstrcpy( szHold, szMsg );
lstrcat( szHold, TEXT("\n") );

OutputDebugString(szHold);
}