//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// acmapp.c
//
// Description:
// This is a sample application that demonstrates how to use the
// Audio Compression Manager API's in Windows. This application is
// also useful as an ACM driver test.
//
//==========================================================================;
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commdlg.h>
#include <shellapi.h>
#include <stdarg.h>
#include <memory.h>
#include <mmreg.h>
#include <msacm.h>
#include "appport.h"
#include "acmapp.h"
#include "debug.h"
//
// globals, no less
//
HINSTANCE ghinst;
BOOL gfAcmAvailable;
UINT gfuAppOptions = APP_OPTIONSF_AUTOOPEN;
HFONT ghfontApp;
HACMDRIVERID ghadidNotify;
UINT guWaveInId = (UINT)WAVE_MAPPER;
UINT guWaveOutId = (UINT)WAVE_MAPPER;
TCHAR gszNull[] = TEXT("");
TCHAR gszAppProfile[] = TEXT("acmapp.ini");
TCHAR gszYes[] = TEXT("Yes");
TCHAR gszNo[] = TEXT("No");
TCHAR gszAppName[APP_MAX_APP_NAME_CHARS];
TCHAR gszFileUntitled[APP_MAX_FILE_TITLE_CHARS];
TCHAR gszInitialDirOpen[APP_MAX_FILE_PATH_CHARS];
TCHAR gszInitialDirSave[APP_MAX_FILE_PATH_CHARS];
TCHAR gszLastSaveFile[APP_MAX_FILE_PATH_CHARS];
ACMAPPFILEDESC gaafd;
//==========================================================================;
//
// Application helper functions
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// int AppMsgBox
//
// Description:
// This function displays a message for the application in a standard
// message box.
//
// Note that this function takes any valid argument list that can
// be passed to wsprintf. Because of this, the application must
// remember to cast near string pointers to FAR when built for Win 16.
// You will get a nice GP fault if you do not cast them correctly.
//
// Arguments:
// HWND hwnd: Handle to parent window for message box holding the
// message.
//
// UINT fuStyle: Style flags for MessageBox().
//
// PTSTR pszFormat: Format string used for wvsprintf().
//
// Return (int):
// The return value is the result of MessageBox() function.
//
//--------------------------------------------------------------------------;
int FNCGLOBAL AppMsgBox
(
HWND hwnd,
UINT fuStyle,
PTSTR pszFormat,
...
)
{
va_list va;
TCHAR ach[1024];
int n;
//
// format and display the message..
//
va_start(va, pszFormat);
#ifdef WIN32
wvsprintf(ach, pszFormat, va);
#else
wvsprintf(ach, pszFormat, (LPSTR)va);
#endif
va_end(va);
n = MessageBox(hwnd, ach, gszAppName, fuStyle);
return (n);
} // AppMsgBox()
//--------------------------------------------------------------------------;
//
// int AppMsgBoxId
//
// Description:
// This function displays a message for the application. The message
// text is retrieved from the string resource table using LoadString.
//
// Note that this function takes any valid argument list that can
// be passed to wsprintf. Because of this, the application must
// remember to cast near string pointers to FAR when built for Win 16.
// You will get a nice GP fault if you do not cast them correctly.
//
// Arguments:
// HWND hwnd: Handle to parent window for message box holding the
// message.
//
// UINT fuStyle: Style flags for MessageBox().
//
// UINT uIdsFormat: String resource id to be loaded with LoadString()
// and used a the format string for wvsprintf().
//
// Return (int):
// The return value is the result of MessageBox() if the string
// resource specified by uIdsFormat is valid. The return value is zero
// if the string resource failed to load.
//
//--------------------------------------------------------------------------;
int FNCGLOBAL AppMsgBoxId
(
HWND hwnd,
UINT fuStyle,
UINT uIdsFormat,
...
)
{
va_list va;
TCHAR szFormat[APP_MAX_STRING_RC_CHARS];
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
int n;
n = LoadString(ghinst, uIdsFormat, szFormat, SIZEOF(szFormat));
if (0 != n)
{
//
// format and display the message..
//
va_start(va, uIdsFormat);
#ifdef WIN32
wvsprintf(ach, szFormat, va);
#else
wvsprintf(ach, szFormat, (LPSTR)va);
#endif
va_end(va);
n = MessageBox(hwnd, ach, gszAppName, fuStyle);
}
return (n);
} // AppMsgBoxId()
//--------------------------------------------------------------------------;
//
// void AppHourGlass
//
// Description:
// This function changes the cursor to that of the hour glass or
// back to the previous cursor.
//
// This function can be called recursively.
//
// Arguments:
// BOOL fHourGlass: TRUE if we need the hour glass. FALSE if we need
// the arrow back.
//
// Return (void):
// On return, the cursor will be what was requested.
//
//--------------------------------------------------------------------------;
void FNGLOBAL AppHourGlass
(
BOOL fHourGlass
)
{
static HCURSOR hcur;
static UINT uWaiting = 0;
if (fHourGlass)
{
if (!uWaiting)
{
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
ShowCursor(TRUE);
}
uWaiting++;
}
else
{
--uWaiting;
if (!uWaiting)
{
ShowCursor(FALSE);
SetCursor(hcur);
}
}
} // AppHourGlass()
//--------------------------------------------------------------------------;
//
// BOOL AppYield
//
// Description:
// This function yields by dispatching all messages stacked up in the
// application queue.
//
// Arguments:
// HWND hwnd: Handle to main window of application if not yielding
// for a dialog. Handle to dialog box if yielding for a dialog box.
//
// BOOL fIsDialog: TRUE if being called to yield for a dialog box.
//
// Return (BOOL):
// The return value is always TRUE.
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppYield
(
HWND hwnd,
BOOL fIsDialog
)
{
MSG msg;
if (fIsDialog)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if ((NULL == hwnd) || !IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
else
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (TRUE);
} // AppYield()
//--------------------------------------------------------------------------;
//
// int AppSetWindowText
//
// Description:
// This function formats a string and sets the specified window text
// to the result.
//
// Arguments:
// HWND hwnd: Handle to window to receive the new text.
//
// PTSTR pszFormat: Pointer to any valid format for wsprintf.
//
// Return (int):
// The return value is the number of bytes that the resulting window
// text was.
//
//--------------------------------------------------------------------------;
int FNCGLOBAL AppSetWindowText
(
HWND hwnd,
PTSTR pszFormat,
...
)
{
va_list va;
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
int n;
//
// format and display the string in the window...
//
va_start(va, pszFormat);
#ifdef WIN32
n = wvsprintf(ach, pszFormat, va);
#else
n = wvsprintf(ach, pszFormat, (LPSTR)va);
#endif
va_end(va);
SetWindowText(hwnd, ach);
return (n);
} // AppSetWindowText()
//--------------------------------------------------------------------------;
//
// int AppSetWindowTextId
//
// Description:
// This function formats a string and sets the specified window text
// to the result. The format string is extracted from the string
// table using LoadString() on the uIdsFormat argument.
//
// Arguments:
// HWND hwnd: Handle to window to receive the new text.
//
// UINT uIdsFormat: String resource id to be loaded with LoadString()
// and used a the format string for wvsprintf().
//
// Return (int):
// The return value is the number of bytes that the resulting window
// text was. This value is zero if the LoadString() function fails
// for the uIdsFormat argument.
//
//--------------------------------------------------------------------------;
int FNCGLOBAL AppSetWindowTextId
(
HWND hwnd,
UINT uIdsFormat,
...
)
{
va_list va;
TCHAR szFormat[APP_MAX_STRING_RC_CHARS];
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
int n;
n = LoadString(ghinst, uIdsFormat, szFormat, SIZEOF(szFormat));
if (0 != n)
{
//
// format and display the string in the window...
//
va_start(va, uIdsFormat);
#ifdef WIN32
n = wvsprintf(ach, szFormat, va);
#else
n = wvsprintf(ach, szFormat, (LPSTR)va);
#endif
va_end(va);
SetWindowText(hwnd, ach);
}
return (n);
} // AppSetWindowTextId()
//--------------------------------------------------------------------------;
//
// BOOL AppFormatBigNumber
//
// Description:
//
//
// Arguments:
// LPTSTR pszNumber:
//
// DWORD dw:
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppFormatBigNumber
(
LPTSTR pszNumber,
DWORD dw
)
{
//
// this is ugly...
//
//
if (dw >= 1000000000L)
{
wsprintf(pszNumber, TEXT("%u,%03u,%03u,%03u"),
(WORD)(dw / 1000000000L),
(WORD)((dw % 1000000000L) / 1000000L),
(WORD)((dw % 1000000L) / 1000),
(WORD)(dw % 1000));
}
else if (dw >= 1000000L)
{
wsprintf(pszNumber, TEXT("%u,%03u,%03u"),
(WORD)(dw / 1000000L),
(WORD)((dw % 1000000L) / 1000),
(WORD)(dw % 1000));
}
else if (dw >= 1000)
{
wsprintf(pszNumber, TEXT("%u,%03u"),
(WORD)(dw / 1000),
(WORD)(dw % 1000));
}
else
{
wsprintf(pszNumber, TEXT("%lu"), dw);
}
return (TRUE);
} // AppFormatBigNumber()
//--------------------------------------------------------------------------;
//
// BOOL AppFormatDosDateTime
//
// Description:
//
//
// Arguments:
// LPTSTR pszDateTime:
//
// UINT uDosDate:
//
// UINT uDosTime:
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppFormatDosDateTime
(
LPTSTR pszDateTime,
UINT uDosDate,
UINT uDosTime
)
{
static TCHAR szFormatDateTime[] = TEXT("%.02u/%.02u/%.02u %.02u:%.02u:%.02u");
UINT uDateMonth;
UINT uDateDay;
UINT uDateYear;
UINT uTimeHour;
UINT uTimeMinute;
UINT uTimeSecond;
//
//
//
uTimeHour = uDosTime >> 11;
uTimeMinute = (uDosTime & 0x07E0) >> 5;
uTimeSecond = (uDosTime & 0x001F) << 1;
uDateMonth = (uDosDate & 0x01E0) >> 5;
uDateDay = (uDosDate & 0x001F);
uDateYear = (uDosDate >> 9) + 80;
//
//
//
//
wsprintf(pszDateTime, szFormatDateTime,
uDateMonth,
uDateDay,
uDateYear,
uTimeHour,
uTimeMinute,
uTimeSecond);
return (TRUE);
} // AppFormatDosDateTime()
//--------------------------------------------------------------------------;
//
// void AcmAppDebugLog
//
// Description:
// This function logs information to the debugger if the Debug Log
// option is set. You can then run DBWin (or something similar)
// to redirect the output whereever you want. Very useful for debugging
// ACM drivers.
//
// Arguments:
// PTSTR pszFormat: Pointer to any valid format for wsprintf.
//
// Return (void):
// None.
//
//--------------------------------------------------------------------------;
void FNCGLOBAL AcmAppDebugLog
(
PTSTR pszFormat,
...
)
{
static TCHAR szDebugLogSeparator[] = TEXT("=============================================================================\r\n");
va_list va;
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
//
// !!! UNICODE !!!
//
//
if (0 != (APP_OPTIONSF_DEBUGLOG & gfuAppOptions))
{
if (NULL == pszFormat)
{
OutputDebugString(szDebugLogSeparator);
return;
}
//
// format and display the string in a message box...
//
va_start(va, pszFormat);
#ifdef WIN32
wvsprintf(ach, pszFormat, va);
#else
wvsprintf(ach, pszFormat, (LPSTR)va);
#endif
va_end(va);
OutputDebugString(ach);
}
} // AcmAppDebugLog()
//--------------------------------------------------------------------------;
//
// int MEditPrintF
//
// Description:
// This function is used to print formatted text into a Multiline
// Edit Control as if it were a standard console display. This is
// a very easy way to display small amounts of text information
// that can be scrolled and copied to the clip-board.
//
// Arguments:
// HWND hedit: Handle to a Multiline Edit control.
//
// PTSTR pszFormat: Pointer to any valid format for wsprintf. If
// this argument is NULL, then the Multiline Edit Control is cleared
// of all text.
//
//
// Return (int):
// Returns the number of characters written into the edit control.
//
// Notes:
// The pszFormat string can contain combinations of escapes that
// modify the default behaviour of this function. Escapes are single
// character codes placed at the _beginning_ of the format string.
//
// Current escapes defined are:
//
// ~ : Suppresses the default CR/LF added to the end of the
// printed line. Since the most common use of this function
// is to output a whole line of text with a CR/LF, that is
// the default.
//
// ` : Suppresses logging to the debug terminal (regardless of
// the global debug log options flag).
//
//
//--------------------------------------------------------------------------;
int FNCGLOBAL MEditPrintF
(
HWND hedit,
PTSTR pszFormat,
...
)
{
static TCHAR szCRLF[] = TEXT("\r\n");
va_list va;
TCHAR ach[APP_MAX_STRING_RC_CHARS];
int n;
BOOL fCRLF;
BOOL fDebugLog;
//
// default the escapes
//
fCRLF = TRUE;
fDebugLog = TRUE;
//
// if the pszFormat argument is NULL, then just clear all text in
// the edit control..
//
if (NULL == pszFormat)
{
SetWindowText(hedit, gszNull);
AcmAppDebugLog(NULL);
return (0);
}
//
// format and display the string in the window... first search for
// escapes to modify default behaviour.
//
for (;;)
{
switch (*pszFormat)
{
case '~':
fCRLF = FALSE;
pszFormat++;
continue;
case '`':
fDebugLog = FALSE;
pszFormat++;
continue;
}
break;
}
va_start(va, pszFormat);
#ifdef WIN32
n = wvsprintf(ach, pszFormat, va);
#else
n = wvsprintf(ach, pszFormat, (LPSTR)va);
#endif
va_end(va);
Edit_SetSel(hedit, (WPARAM)-1, (LPARAM)-1);
Edit_ReplaceSel(hedit, ach);
if (fDebugLog)
{
AcmAppDebugLog(ach);
}
if (fCRLF)
{
Edit_SetSel(hedit, (WPARAM)-1, (LPARAM)-1);
Edit_ReplaceSel(hedit, szCRLF);
if (fDebugLog)
{
AcmAppDebugLog(szCRLF);
}
}
return (n);
} // MEditPrintF()
//--------------------------------------------------------------------------;
//
// BOOL AppGetFileTitle
//
// Description:
// This function extracts the file title from a file path and returns
// it in the caller's specified buffer.
//
// Arguments:
// PTSTR pszFilePath: Pointer to null terminated file path.
//
// PTSTR pszFileTitle: Pointer to buffer to receive the file title.
//
// Return (BOOL):
// Always returns TRUE. But should return FALSE if this function
// checked for bogus values, etc.
//
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppGetFileTitle
(
PTSTR pszFilePath,
PTSTR pszFileTitle
)
{
#define IS_SLASH(c) ('/' == (c) || '\\' == (c))
PTSTR pch;
//
// scan to the end of the file path string..
//
for (pch = pszFilePath; '\0' != *pch; pch++)
;
//
// now scan back toward the beginning of the string until a slash (\),
// colon, or start of the string is encountered.
//
while ((pch >= pszFilePath) && !IS_SLASH(*pch) && (':' != *pch))
{
pch--;
}
//
// finally, copy the 'title' into the destination buffer.. skip ahead
// one char since the above loop steps back one too many chars...
//
lstrcpy(pszFileTitle, ++pch);
return (TRUE);
} // AppGetFileTitle()
//--------------------------------------------------------------------------;
//
// BOOL AppGetFileName
//
// Description:
// This function is a wrapper for the Get[Open/Save]FileName commdlg
// chooser dialogs. Based on the fuFlags argument, this function will
// display the appropriate chooser dialog and return the result.
//
// Arguments:
// HWND hwnd: Handle to parent window for chooser dialog.
//
// PTSTR pszFilePath: Pointer to buffer to receive the file path.
//
// PTSTR pszFileTitle: Pointer to buffer to receive the file title.
// This argument may be NULL, in which case no title will be returned.
//
// UINT fuFlags:
//
// Return (BOOL):
// The return value is TRUE if a file was chosen. It is FALSE if the
// user canceled the operation.
//
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppGetFileName
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle,
UINT fuFlags
)
{
#define APP_OFN_FLAGS_SAVE (OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT)
#define APP_OFN_FLAGS_OPEN (OFN_HIDEREADONLY | OFN_FILEMUSTEXIST)
TCHAR szExtDefault[APP_MAX_EXT_DEFAULT_CHARS];
TCHAR szExtFilter[APP_MAX_EXT_FILTER_CHARS];
OPENFILENAME ofn;
BOOL f;
PTCHAR pch;
//
// get the extension filter and default extension for this application
//
LoadString(ghinst, IDS_OFN_EXT_DEF, szExtDefault, SIZEOF(szExtDefault));
LoadString(ghinst, IDS_OFN_EXT_FILTER, szExtFilter, SIZEOF(szExtFilter));
//
// NOTE! building the filter string for the OPENFILENAME structure
// is a bit more difficult when dealing with Unicode and C8's new
// optimizer. it joyfully removes literal '\0' characters from
// strings that are concatted together. if you try making each
// string separate (array of pointers to strings), the compiler
// will dword align them... etc, etc.
//
// if you can think of a better way to build the silly filter string
// for common dialogs and still work in Win 16 and Win 32 [Unicode]
// i'd sure like to hear about it...
//
for (pch = &szExtFilter[0]; '\0' != *pch; pch++)
{
if ('!' == *pch)
*pch = '\0';
}
//
// initialize the OPENFILENAME members
//
memset(&ofn, 0, sizeof(OPENFILENAME));
pszFilePath[0] = '\0';
if (pszFileTitle)
pszFileTitle[0] = '\0';
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = szExtFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0L;
ofn.nFilterIndex = 1L;
ofn.lpstrFile = pszFilePath;
ofn.nMaxFile = APP_MAX_FILE_PATH_CHARS;
ofn.lpstrFileTitle = pszFileTitle;
ofn.nMaxFileTitle = pszFileTitle ? APP_MAX_FILE_TITLE_CHARS : 0;
if (fuFlags & APP_GETFILENAMEF_SAVE)
{
ofn.lpstrInitialDir = gszInitialDirSave;
}
else
{
ofn.lpstrInitialDir = gszInitialDirOpen;
}
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = szExtDefault;
//
// if the fuFlags.APP_GETFILENAMEF_SAVE bit is set, then call
// GetSaveFileName() otherwise call GetOpenFileName(). why commdlg was
// designed with two separate functions for save and open only clark
// knows.
//
if (fuFlags & APP_GETFILENAMEF_SAVE)
{
ofn.Flags = APP_OFN_FLAGS_SAVE;
f = GetSaveFileName(&ofn);
if (f)
{
if (NULL != pszFilePath)
{
lstrcpy(gszInitialDirSave, pszFilePath);
pch = &gszInitialDirSave[lstrlen(gszInitialDirSave) - 1];
for ( ; gszInitialDirSave != pch; pch--)
{
if ('\\' == *pch)
{
*pch = '\0';
break;
}
}
}
}
}
else
{
ofn.Flags = APP_OFN_FLAGS_OPEN;
f = GetOpenFileName(&ofn);
if (f)
{
if (NULL != pszFilePath)
{
lstrcpy(gszInitialDirOpen, pszFilePath);
pch = &gszInitialDirOpen[lstrlen(gszInitialDirOpen) - 1];
for ( ; gszInitialDirOpen != pch; pch--)
{
if ('\\' == *pch)
{
*pch = '\0';
break;
}
}
}
}
}
return (f);
} // AppGetFileName()
//--------------------------------------------------------------------------;
//
// BOOL AppTitle
//
// Description:
// This function formats and sets the title text of the application's
// window.
//
// Arguments:
// HWND hwnd: Handle to application window to set title text for.
//
// PTSTR pszFileTitle: Pointer to file title to display.
//
// Return (BOOL):
// The return value is always TRUE.
//
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppTitle
(
HWND hwnd,
PTSTR pszFileTitle
)
{
static TCHAR szFormatTitle[] = TEXT("%s - %s");
TCHAR ach[APP_MAX_FILE_PATH_CHARS];
//
// format the title text as 'AppName - FileTitle'
//
wsprintf(ach, szFormatTitle, (LPSTR)gszAppName, (LPSTR)pszFileTitle);
SetWindowText(hwnd, ach);
return (TRUE);
} // AppTitle()
//--------------------------------------------------------------------------;
//
// BOOL AppFileNew
//
// Description:
// This function is called to handle the IDM_FILE_NEW message. It is
// responsible for clearing the working area for a new unnamed file.
//
// Arguments:
// HWND hwnd: Handle to application window.
//
// PACMAPPFILEDESC paafd: Pointer to current file descriptor.
//
// Return (BOOL):
// The return value is TRUE if the working area was cleared and is
// ready for new stuff. The return value is FALSE if the user canceled
// the operation.
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppFileNew
(
HWND hwnd,
PACMAPPFILEDESC paafd,
BOOL fCreate
)
{
BOOL f;
if (fCreate)
{
f = AcmAppFileNew(hwnd, paafd);
if (!f)
return (FALSE);
}
else
{
//
// if there is currently a file path, then we have to do some real
// work...
//
if ('\0' != paafd->szFilePath[0])
{
f = AcmAppFileNew(hwnd, paafd);
if (!f)
return (FALSE);
}
//
// blow away the old file path and title; set the window title
// and return success
//
lstrcpy(paafd->szFilePath, gszFileUntitled);
lstrcpy(paafd->szFileTitle, gszFileUntitled);
}
AppTitle(hwnd, paafd->szFileTitle);
AcmAppDisplayFileProperties(hwnd, paafd);
return (TRUE);
} // AppFileNew()
//--------------------------------------------------------------------------;
//
// BOOL AppFileOpen
//
// Description:
// This function handles the IDM_FILE_OPEN message. It is responsible
// for getting a new file name from the user and opening that file
// if possible.
//
// Arguments:
// HWND hwnd: Handle to application window.
//
// PACMAPPFILEDESC paafd: Pointer to current file descriptor.
//
// Return (BOOL):
// The return value is TRUE if a new file was selected and opened.
// It is FALSE if the user canceled the operation.
//
//--------------------------------------------------------------------------;
BOOL FNLOCAL AppFileOpen
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
TCHAR szFilePath[APP_MAX_FILE_PATH_CHARS];
TCHAR szFileTitle[APP_MAX_FILE_TITLE_CHARS];
BOOL f;
//
// first test for a modified file that has not been saved. if the
// return value is FALSE we should cancel the File.Open operation.
//
f = AcmAppFileSaveModified(hwnd, paafd);
if (!f)
return (FALSE);
//
// get the file name of the new file into temporary buffers (so
// if we fail to open it we can back out cleanly).
//
f = AppGetFileName(hwnd, szFilePath, szFileTitle, APP_GETFILENAMEF_OPEN);
if (!f)
return (FALSE);
//!!!
// read the new file...
//
lstrcpy(paafd->szFilePath, szFilePath);
lstrcpy(paafd->szFileTitle, szFileTitle);
f = AcmAppFileOpen(hwnd, paafd);
if (f)
{
//
// set the window title text...
//
AppTitle(hwnd, szFileTitle);
AcmAppDisplayFileProperties(hwnd, paafd);
}
return (f);
} // AppFileOpen()
//--------------------------------------------------------------------------;
//
// BOOL AppFileSave
//
// Description:
// This function handles the IDM_FILE_SAVE[AS] messages. It is
// responsible for saving the current file. If a file name needs
// to be specified then the save file dialog is displayed.
//
// Arguments:
// HWND hwnd: Handle to application window.
//
// PACMAPPFILEDESC paafd: Pointer to current file descriptor.
//
// BOOL fSaveAs: TRUE if the save file chooser should be displayed
// before saving the file. FALSE if should operate like File.Save.
//
// Return (BOOL):
// The return value is TRUE if the file was saved. It is FALSE if the
// user canceled the operation or the file does not need saved.
//
//--------------------------------------------------------------------------;
BOOL FNGLOBAL AppFileSave
(
HWND hwnd,
PACMAPPFILEDESC paafd,
BOOL fSaveAs
)
{
TCHAR szFilePath[APP_MAX_FILE_PATH_CHARS];
TCHAR szFileTitle[APP_MAX_FILE_TITLE_CHARS];
BOOL f;
//
//
//
lstrcpy(szFilePath, paafd->szFilePath);
lstrcpy(szFileTitle, paafd->szFileTitle);
//
// check if we should bring up the save file chooser dialog...
//
if (fSaveAs || (0 == lstrcmp(paafd->szFileTitle, gszFileUntitled)))
{
//
// get the file name for saving the data to into temporary
// buffers (so if we fail to save it we can back out cleanly).
//
f = AppGetFileName(hwnd, szFilePath, szFileTitle, APP_GETFILENAMEF_SAVE);
if (!f)
return (FALSE);
}
//
// save the file...
//
f = AcmAppFileSave(hwnd, paafd, szFilePath, szFileTitle, 0);
if (f)
{
//
// changes have been saved, so clear the modified bit...
//
paafd->fdwState &= ~ACMAPPFILEDESC_STATEF_MODIFIED;
AppTitle(hwnd, paafd->szFileTitle);
AcmAppDisplayFileProperties(hwnd, paafd);
}
return (f);
} // AppFileSave()
//==========================================================================;
//
// Main application window handling code...
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// LRESULT AppInitMenuPopup
//
// Description:
// This function handles the WM_INITMENUPOPUP message. This message
// is sent to the window owning the menu that is going to become
// active. This gives an application the ability to modify the menu
// before it is displayed (disable/add items, etc).
//
// Arguments:
// HWND hwnd: Handle to window that generated the WM_INITMENUPOPUP
// message.
//
// HMENU hmenu: Handle to the menu that is to become active.
//
// int nItem: Specifies the zero-based relative position of the menu
// item that invoked the popup menu.
//
// BOOL fSysMenu: Specifies whether the popup menu is a System menu
// (TRUE) or it is not a System menu (FALSE).
//
// Return (LRESULT):
// Returns zero if the message is processed.
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL AppInitMenuPopup
(
HWND hwnd,
HMENU hmenu,
int nItem,
BOOL fSysMenu
)
{
BOOL f;
int nSelStart;
int nSelEnd;
HWND hedit;
DPF(4, "AppInitMenuPopup(hwnd=%Xh, hmenu=%Xh, nItem=%d, fSysMenu=%d)",
hwnd, hmenu, nItem, fSysMenu);
//
// if the system menu is what got hit, succeed immediately... this
// application has no stuff in the system menu.
//
if (fSysMenu)
return (0L);
//
// initialize the menu that is being 'popped up'
//
switch (nItem)
{
case APP_MENU_ITEM_FILE:
EnableMenuItem(hmenu, IDM_FILE_NEW,
(UINT)(gfAcmAvailable ? MF_ENABLED : MF_GRAYED));
f = (NULL != gaafd.pwfx);
EnableMenuItem(hmenu, IDM_FILE_SNDPLAYSOUND_PLAY,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
EnableMenuItem(hmenu, IDM_FILE_SNDPLAYSOUND_STOP,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
f = (NULL != gaafd.pwfx) && gfAcmAvailable;
EnableMenuItem(hmenu, IDM_FILE_CONVERT,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
EnableMenuItem(hmenu, IDM_FILE_CONVERT_ALL,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
//
// if the file has been modified, then enable the File.Save
// menu
//
f = (0 != (gaafd.fdwState & ACMAPPFILEDESC_STATEF_MODIFIED));
EnableMenuItem(hmenu, IDM_FILE_SAVE,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
// f = (NULL != gaafd.pwfx);
EnableMenuItem(hmenu, IDM_FILE_SAVEAS,
(UINT)(f ? MF_ENABLED : MF_GRAYED));
break;
case APP_MENU_ITEM_EDIT:
//
// check to see if something is selected in the display
// window and enable/disable Edit menu options appropriately
//
hedit = GetDlgItem(hwnd, IDD_ACMAPP_EDIT_DISPLAY);
Edit_GetSelEx(hedit, &nSelStart, &nSelEnd);
f = (nSelStart != nSelEnd);
EnableMenuItem(hmenu, WM_COPY, (UINT)(f ? MF_ENABLED : MF_GRAYED));
break;
case APP_MENU_ITEM_VIEW:
EnableMenuItem(hmenu, IDM_VIEW_ACM_DRIVERS,
(UINT)(gfAcmAvailable ? MF_ENABLED : MF_GRAYED));
break;
case APP_MENU_ITEM_OPTIONS:
f = (0 != waveInGetNumDevs());
EnableMenuItem(hmenu, IDM_OPTIONS_WAVEINDEVICE, (UINT)(f ? MF_ENABLED : MF_GRAYED));
f = (0 != waveOutGetNumDevs());
EnableMenuItem(hmenu, IDM_OPTIONS_WAVEOUTDEVICE, (UINT)(f ? MF_ENABLED : MF_GRAYED));
//
// make sure the options that need a checkmark are checked..
//
f = (0 != (APP_OPTIONSF_AUTOOPEN & gfuAppOptions));
CheckMenuItem(hmenu, IDM_OPTIONS_AUTOOPEN,
(UINT)(f ? MF_CHECKED : MF_UNCHECKED));
f = (0 != (APP_OPTIONSF_DEBUGLOG & gfuAppOptions));
CheckMenuItem(hmenu, IDM_OPTIONS_DEBUGLOG,
(UINT)(f ? MF_CHECKED : MF_UNCHECKED));
break;
}
//
// we processed the message--return 0...
//
return (0L);
} // AppInitMenuPopup()
//--------------------------------------------------------------------------;
//
// LRESULT AppCommand
//
// Description:
// This function handles the WM_COMMAND message.
//
// Arguments:
// HWND hwnd: Handle to window receiving the WM_COMMAND message.
//
// int nId: Control or menu item identifier.
//
// HWND hwndCtl: Handle of control if the message is from a control.
// This argument is NULL if the message was not generated by a control.
//
// UINT uCode: Notification code. This argument is 1 if the message
// was generated by an accelerator. If the message is from a menu,
// this argument is 0.
//
// Return (LRESULT):
// Returns zero if the message is processed.
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL AppCommand
(
HWND hwnd,
int nId,
HWND hwndCtl,
UINT uCode
)
{
BOOL f;
DWORD dw;
UINT uDevId;
switch (nId)
{
case IDM_FILE_NEW:
AppFileNew(hwnd, &gaafd, TRUE);
break;
case IDM_FILE_OPEN:
AppFileOpen(hwnd, &gaafd);
break;
case IDM_FILE_SAVE:
AppFileSave(hwnd, &gaafd, FALSE);
break;
case IDM_FILE_SAVEAS:
AppFileSave(hwnd, &gaafd, TRUE);
break;
case IDM_FILE_SNDPLAYSOUND_PLAY:
if (NULL == gaafd.pwfx)
{
MessageBeep((UINT)-1);
break;
}
AppHourGlass(TRUE);
dw = timeGetTime();
f = sndPlaySound(gaafd.szFilePath, SND_ASYNC | SND_NODEFAULT);
dw = timeGetTime() - dw;
AppHourGlass(FALSE);
if (0 != (APP_OPTIONSF_DEBUGLOG & gfuAppOptions))
{
AcmAppDebugLog(NULL);
AcmAppDebugLog(TEXT("sndPlaySound(%s) took %lu milliseconds to %s.\r\n"),
(LPTSTR)gaafd.szFilePath,
dw,
f ? (LPTSTR)TEXT("succeed") : (LPTSTR)TEXT("fail"));
}
if (!f)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("The file '%s' cannot be played by sndPlaySound."),
(LPSTR)gaafd.szFilePath);
}
break;
case IDM_FILE_SNDPLAYSOUND_STOP:
sndPlaySound(NULL, 0L);
break;
case IDM_FILE_CONVERT:
AcmAppFileConvert(hwnd, &gaafd);
break;
case IDM_FILE_CONVERT_ALL:
AcmAppMultiThreadedConvertAll(hwnd, &gaafd);
break;
case IDM_FILE_ABOUT:
DialogBox(ghinst, DLG_ABOUT, hwnd, AboutDlgProc);
break;
case IDM_FILE_EXIT:
FORWARD_WM_CLOSE(hwnd, SendMessage);
break;
case WM_COPY:
//
// pass on edit messages received to the display window
//
SendMessage(GetDlgItem(hwnd, IDD_ACMAPP_EDIT_DISPLAY), nId, 0, 0L);
break;
case IDM_EDIT_SELECTALL:
Edit_SetSel(GetDlgItem(hwnd, IDD_ACMAPP_EDIT_DISPLAY), 0, -1);
break;
case IDM_UPDATE:
AcmAppFileOpen(hwnd, &gaafd);
AcmAppDisplayFileProperties(hwnd, &gaafd);
break;
case IDM_VIEW_SYSTEMINFO:
DialogBox(ghinst, DLG_AADETAILS, hwnd, AcmAppSystemInfoDlgProc);
break;
case IDM_VIEW_ACM_DRIVERS:
DialogBox(ghinst, DLG_AADRIVERS, hwnd, AcmAppDriversDlgProc);
break;
case IDM_OPTIONS_WAVEINDEVICE:
uDevId = DialogBoxParam(ghinst, DLG_AAWAVEDEVICE, hwnd,
AcmAppWaveDeviceDlgProc,
MAKELONG((WORD)guWaveInId, TRUE));
if (uDevId != guWaveInId)
{
guWaveInId = uDevId;
AcmAppDisplayFileProperties(hwnd, &gaafd);
}
break;
case IDM_OPTIONS_WAVEOUTDEVICE:
uDevId = DialogBoxParam(ghinst, DLG_AAWAVEDEVICE, hwnd,
AcmAppWaveDeviceDlgProc,
MAKELONG((WORD)guWaveOutId, FALSE));
if (uDevId != guWaveOutId)
{
guWaveOutId = uDevId;
AcmAppDisplayFileProperties(hwnd, &gaafd);
}
break;
case IDM_OPTIONS_AUTOOPEN:
gfuAppOptions ^= APP_OPTIONSF_AUTOOPEN;
break;
case IDM_OPTIONS_DEBUGLOG:
gfuAppOptions ^= APP_OPTIONSF_DEBUGLOG;
break;
case IDM_OPTIONS_FONT:
AcmAppChooseFont(hwnd);
break;
case IDM_PLAYRECORD:
if (NULL == gaafd.pwfx)
{
f = AppFileNew(hwnd, &gaafd, TRUE);
if (!f)
break;
if (NULL == gaafd.pwfx)
break;
}
f = DialogBoxParam(ghinst, DLG_AAPLAYRECORD, hwnd,
AcmAppPlayRecord, (LPARAM)(LPVOID)&gaafd);
if (f)
{
AcmAppFileOpen(hwnd, &gaafd);
AcmAppDisplayFileProperties(hwnd, &gaafd);
}
break;
}
return (0L);
} // AppCommand()
//--------------------------------------------------------------------------;
//
// BOOL AcmAppDlgProcDragDropContinue
//
// Description:
//
// Arguments:
// HWND hwnd: Handle to window.
//
// UINT uMsg: Message being sent to the window.
//
// WPARAM wParam: Specific argument to message.
//
// LPARAM lParam: Specific argument to message.
//
// Return (BOOL):
// The return value is specific to the message that was received. For
// the most part, it is FALSE if this dialog procedure does not handle
// a message.
//
//--------------------------------------------------------------------------;
BOOL FNEXPORT AcmAppDlgProcDragDropContinue
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
UINT uId;
switch (uMsg)
{
case WM_INITDIALOG:
AppSetWindowText(hwnd, TEXT("File %u of %u"), LOWORD(lParam), HIWORD(lParam));
return (TRUE);
case WM_COMMAND:
uId = GET_WM_COMMAND_ID(wParam, lParam);
if ((IDOK == uId) || (IDCANCEL == uId))
{
EndDialog(hwnd, uId);
}
break;
}
return (FALSE);
} // AcmAppDlgProcDragDropContinue()
//--------------------------------------------------------------------------;
//
// LRESULT AppDropFiles
//
// Description:
// This function handles the WM_DROPFILES message. This message is
// sent when files are 'dropped' on the window from file manager
// (or other drag/drop servers made by ISV's that figured out the
// undocumented internal workings of the SHELL).
//
// A window must be registered to receive these messages either by
// called DragAcceptFiles() or using CreateWindowEx() with the
// WS_EX_ACCEPTFILES style bit.
//
// Arguments:
// HWND hwnd: Handle to window receiving the message.
//
// HDROP hdrop: Handle to drop structure.
//
// Return (LRESULT):
// Returns 0 if the message is processed.
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL AppDropFiles
(
HWND hwnd,
HDROP hdrop
)
{
TCHAR szFilePath[APP_MAX_FILE_PATH_CHARS];
UINT cFiles;
UINT u;
BOOL f;
int n;
//
// first test for a file that has not been saved. if the return
// value is FALSE we should cancel the drop operation.
//
f = AcmAppFileSaveModified(hwnd, &gaafd);
if (!f)
{
goto App_Drop_Files_Exit;
}
//
// get number of files dropped on our window
//
cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
DPF(4, "AppDropFiles(hwnd=%Xh, hdrop=%Xh)--cFiles=%u", hwnd, hdrop, cFiles);
//
// step through each file and stop on the one the user wants or
// the last file (whichever comes first).
//
for (u = 0; u < cFiles; u++)
{
//
// get the next file name and try to open it--if not a valid
// file, then skip to the next one (if there is one).
//
DragQueryFile(hdrop, u, szFilePath, SIZEOF(szFilePath));
//
// !!! destructive !!!
//
// attempt to open the file
//
lstrcpy(gaafd.szFilePath, szFilePath);
lstrcpy(gaafd.szFileTitle, gszNull);
f = AcmAppFileOpen(hwnd, &gaafd);
if (!f)
{
continue;
}
AppTitle(hwnd, gaafd.szFileTitle);
AcmAppDisplayFileProperties(hwnd, &gaafd);
//
// if this is NOT the last file in the list of files that are
// being dropped on us, then bring up a box asking if we should
// continue or stop where we are..
//
if ((cFiles - 1) != u)
{
n = DialogBoxParam(ghinst,
DLG_AADRAGDROP,
hwnd,
AcmAppDlgProcDragDropContinue,
MAKELPARAM((WORD)(u + 1), (WORD)cFiles));
if (IDCANCEL == n)
break;
}
}
//
// tell the shell to release the memory it allocated for beaming
// the file name(s) over to us... return 0 to show we processed
// the message.
//
App_Drop_Files_Exit:
DragFinish(hdrop);
return (0L);
} // AppDropFiles()
//--------------------------------------------------------------------------;
//
// LRESULT AppSize
//
// Description:
// This function handles the WM_SIZE message for the application's
// window. This message is sent to the application window after the
// size has changed (but before it is painted).
//
// Arguments:
// HWND hwnd: Handle to window that generated the WM_SIZE message.
//
// UINT fuSizeType: Specifies the type of resizing requested. This
// argument is one of the following: SIZE_MAXIMIZED, SIZE_MINIMIZED,
// SIZE_RESTORED, SIZE_MAXHIDE, or SIZE_MAXSHOW.
//
// int nWidth: Width of the new client area for the window.
//
// int nHeight: Height of the new client area for the window.
//
// Return (LRESULT):
// Returns zero if the application processes the message.
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL AppSize
(
HWND hwnd,
UINT fuSizeType,
int nWidth,
int nHeight
)
{
HWND hedit;
RECT rc;
DPF(4, "AppSize(hwnd=%Xh, fuSizeType=%u, nWidth=%d, nHeight=%d)",
hwnd, fuSizeType, nWidth, nHeight);
//
// unless this application is the one being resized then don't waste
// time computing stuff that doesn't matter. this applies to being
// minimized also because this application does not have a custom
// minimized state.
//
if ((SIZE_RESTORED != fuSizeType) && (SIZE_MAXIMIZED != fuSizeType))
return (0L);
//
//
//
GetClientRect(hwnd, &rc);
InflateRect(&rc, 1, 1);
hedit = GetDlgItem(hwnd, IDD_ACMAPP_EDIT_DISPLAY);
SetWindowPos(hedit,
NULL,
rc.left,
rc.top,
rc.right - rc.left,
rc.bottom - rc.top,
SWP_NOZORDER);
//
// we processed the message..
//
return (0L);
} // AppSize()
//--------------------------------------------------------------------------;
//
// LRESULT AcmAppNotify
//
// Description:
//
//
// Arguments:
//
// Return (LRESULT):
//
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL AcmAppNotify
(
HWND hwnd,
WPARAM wParam,
LPARAM lParam
)
{
HWND hwndNext;
DPF(1, "AcmAppNotify: hwnd=%.04Xh, wParam=%.04Xh, lParam2=%.08lXh",
hwnd, wParam, lParam);
//
//
//
hwndNext = GetWindow(hwnd, GW_HWNDFIRST);
while (NULL != hwndNext)
{
if (GetParent(hwndNext) == hwnd)
{
SendMessage(hwndNext, WM_ACMAPP_ACM_NOTIFY, wParam, lParam);
}
hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
}
//
// now force an update to our display in case driver [dis/en]able
// changed what is playable/recordable.
//
AcmAppDisplayFileProperties(hwnd, &gaafd);
//
//
//
return (1L);
} // AcmAppNotify()
//--------------------------------------------------------------------------;
//
// LRESULT AppWndProc
//
// Description:
// This is the main application window procedure.
//
// Arguments:
// HWND hwnd: Handle to window.
//
// UINT uMsg: Message being sent to the window.
//
// WPARAM wParam: Specific argument to message.
//
// LPARAM lParam: Specific argument to message.
//
// Return (LRESULT):
// The return value depends on the message that is being processed.
//
//--------------------------------------------------------------------------;
LRESULT FNEXPORT AppWndProc
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT lr;
switch (uMsg)
{
case WM_CREATE:
lr = HANDLE_WM_CREATE(hwnd, wParam, lParam, AppCreate);
return (lr);
case WM_WININICHANGE:
HANDLE_WM_WININICHANGE(hwnd, wParam, lParam, AppWinIniChange);
return (0L);
case WM_INITMENUPOPUP:
HANDLE_WM_INITMENUPOPUP(hwnd, wParam, lParam, AppInitMenuPopup);
return (0L);
case WM_COMMAND:
lr = HANDLE_WM_COMMAND(hwnd, wParam, lParam, AppCommand);
return (lr);
case WM_DROPFILES:
//
// some windowsx.h files have a screwed up message cracker for
// WM_DROPFILES. because this is a sample app, i don't want
// people having trouble with bogus windowsx.h files, so crack
// the message manually... you should use the message cracker
// if you know your windowsx.h file is good.
//
// lr = HANDLE_WM_DROPFILES(hwnd, wParam, lParam, AppDropFiles);
//
lr = AppDropFiles(hwnd, (HDROP)wParam);
return (lr);
case WM_SIZE:
//
// handle what we want for sizing, and then always call the
// default handler...
//
HANDLE_WM_SIZE(hwnd, wParam, lParam, AppSize);
break;
case WM_QUERYENDSESSION:
lr = HANDLE_WM_QUERYENDSESSION(hwnd, wParam, lParam, AppQueryEndSession);
return (lr);
case WM_ENDSESSION:
HANDLE_WM_ENDSESSION(hwnd, wParam, lParam, AppEndSession);
return (0L);
case WM_CLOSE:
HANDLE_WM_CLOSE(hwnd, wParam, lParam, AppClose);
return (0L);
case WM_DESTROY:
PostQuitMessage(0);
return (0L);
case WM_ACMAPP_ACM_NOTIFY:
AcmAppNotify(hwnd, wParam, lParam);
return (0L);
}
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
} // AppWndProc()
//==========================================================================;
//
// Main entry and message dispatching code
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// int WinMain
//
// Description:
// This function is called by the system as the initial entry point
// for a Windows application.
//
// Arguments:
// HINSTANCE hinst: Identifies the current instance of the
// application.
//
// HINSTANCE hinstPrev: Identifies the previous instance of the
// application (NULL if first instance). For Win 32, this argument
// is _always_ NULL.
//
// LPSTR pszCmdLine: Points to null-terminated unparsed command line.
// This string is strictly ANSI regardless of whether the application
// is built for Unicode. To get the Unicode equivalent call the
// GetCommandLine() function (Win 32 only).
//
// int nCmdShow: How the main window for the application is to be
// shown by default.
//
// Return (int):
// Returns result from WM_QUIT message (in wParam of MSG structure) if
// the application is able to enter its message loop. Returns 0 if
// the application is not able to enter its message loop.
//
//--------------------------------------------------------------------------;
int PASCAL WinMain
(
HINSTANCE hinst,
HINSTANCE hinstPrev,
LPSTR pszCmdLine,
int nCmdShow
)
{
int nResult;
HWND hwnd;
MSG msg;
HACCEL haccl;
DbgInitialize(TRUE);
//
// our documentation states that WinMain is supposed to return 0 if
// we do not enter our message loop--so assume the worst...
//
nResult = 0;
//
// make our instance handle global for convenience..
//
ghinst = hinst;
//
// init some stuff, create window, etc.. note the explicit cast of
// pszCmdLine--this is to mute a warning (and an ugly ifdef) when
// compiling for Unicode. see AppInit() for more details.
//
hwnd = AppInit(hinst, hinstPrev, (LPTSTR)pszCmdLine, nCmdShow);
if (hwnd)
{
haccl = LoadAccelerators(hinst, ACCEL_APP);
//
// dispatch messages
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// do all the special stuff required for this application
// when dispatching messages..
//
if (!TranslateAccelerator(hwnd, haccl, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//
// return result of WM_QUIT message.
//
nResult = (int)msg.wParam;
}
//
// shut things down, clean up, etc.
//
nResult = AppExit(hinst, nResult);
return (nResult);
} // WinMain()