MCIAPP.C

//==========================================================================; 
//
// 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 1993 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// mciapp.c
//
// Description:
// This is a sample application that demonstrates how to use the
// Media Control Interface (MCI) in Windows. This application is
// also useful as an MCI device tester.
//
// History:
// 2/ 6/93 created.
//
//==========================================================================;

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commdlg.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "appport.h"
#include "app.h"
#include "mciapp.h"

#include "debug.h"


//
//
//
BOOL gfExecuting;
BOOL gfAbortExec;


HWND ghwndDevices;

TCHAR gszNone[] = TEXT("(none)");


//--------------------------------------------------------------------------;
//
// BOOL MciAppFileSaveModified
//
// Description:
// This function tests if the current script has been modified, and
// if it has it gives the option of saving the file.
//
// NOTE! This function does *NOT* clear the modified bit for the
// script window. The calling function must do this if necessary.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PTSTR pszFilePath: Pointer to null terminated file path of current
// script. This buffer will receive the new file path if one is chosen.
//
// PTSTR pszFileTitle: Pointer to null terminated file title of
// current script. This buffer will receive the new file title if one
// is chosen.
//
// Return (BOOL):
// Returns TRUE if the calling function should continue--the file was
// either saved or the user does not wish to save it. Returns FALSE
// if the calling function should cancel its operation--the user
// wants to keep the data, but it has not been saved.
//
// History:
// 2/ 6/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppFileSaveModified
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle
)
{
BOOL f;
int n;
HWND hwndScript;

//
// check if the contents of the script window have been modified--if
// they have then ask the user if they want to save the current
// contents...
//
hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
f = Edit_GetModify(hwndScript);
if (f)
{
//
// display an appropriate message box asking for the user's opinion
//
n = AppMsgBoxId(hwnd, MB_YESNOCANCEL | MB_ICONQUESTION| MB_SETFOREGROUND,
IDS_MCI_SCRIPT_CHANGED, (LPSTR)pszFilePath);
switch (n)
{
case IDYES:
f = AppFileSave(hwnd, pszFilePath, pszFileTitle, FALSE);
if (f)
break;

// -- fall through --

case IDCANCEL:
//
// don't continue!
//
return (FALSE);

case IDNO:
break;
}
}

//
// ok to continue...
//
return (TRUE);
} // MciAppFileSaveModified()


//--------------------------------------------------------------------------;
//
// BOOL MciAppFileNew
//
// Description:
// This function simply clears the script window. If a modified script
// will be destroyed, then the user is asked if the script should be
// saved first.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PTSTR pszFilePath: Pointer to null terminated file path of current
// script. This buffer will receive the newly initialized file path.
//
// PTSTR pszFileTitle: Pointer to null terminated file title of
// current script. This buffer will receive the newly initialized file
// title.
//
// Return (BOOL):
// The return value is TRUE if the script window was cleared. It is
// FALSE if the user canceled the operation.
//
// History:
// 2/ 6/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppFileNew
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle
)
{
BOOL f;
HWND hwndScript;

//
// test for a modified script first...
//
f = MciAppFileSaveModified(hwnd, pszFilePath, pszFileTitle);
if (!f)
return (FALSE);

//
// blow away all the text and set the modified bit to 'NOT'
//
hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
SetWindowText(hwndScript, gszNull);
Edit_SetModify(hwndScript, FALSE);

//
// success
//
return (TRUE);
} // MciAppFileNew()


//--------------------------------------------------------------------------;
//
// BOOL MciAppFileOpen
//
// Description:
// This function opens the specified text file and copies the contents
// of the file into the script edit control.
//
// NOTE! This function does NOT check for a modified script! It is
// assumed that the calling function took care of everything before
// calling this function.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PTSTR pszFilePath: Pointer to null terminated file path to open as
// an MCI script. This buffer will be returned with a fully qualified
// path of the file that was opened (if successful).
//
// PTSTR pszFileTitle: Pointer to buffer to receive the file title of
// the newly opened file. This buffer does not need to be initialized.
//
// Return (BOOL):
// The return value is TRUE if the function is successful. It is FALSE
// if an error occurred. If an error does occur, then the contents
// of the script window, pszFilePath and pszFileTitle will remain
// unchanged.
//
// History:
// 2/ 6/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppFileOpen
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle
)
{
#ifdef UNICODE
HANDLE hf;
#else
#define SEEK_SET 0 // flags for _lseek
#define SEEK_CUR 1
#define SEEK_END 2

HFILE hf;
OFSTRUCT of;
#endif
HWND hwndScript;
UINT uFileLen;
LPTSTR psz;
BOOL fReturn;

//
// open the file for reading..
//
#ifdef UNICODE
hf = CreateFile(pszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE == hf)
return (FALSE);
#else
of.cBytes = sizeof(of);
hf = OpenFile(pszFilePath, &of, OF_READ);
if (HFILE_ERROR == hf)
return (FALSE);
#endif

//
// assume the worst
//
fReturn = FALSE;

//
// determine the length in _bytes_ of the file--note that win 16 is
// limited to 32k so big files get truncated... improperly.
//
// !!! need to handle files that are too big better !!!
//
#ifdef WIN32
uFileLen = (UINT)GetFileSize((HANDLE)hf, NULL);
#else
uFileLen = (UINT)_llseek(hf, 0L, SEEK_END);
_llseek(hf, 0L, SEEK_SET);
#endif


//
// now read the contents of the file into a buffer--display an hour
// glass in case the file is large and/or we are reading from a slow
// device... note that the memory allocation may take some time if
// the pager is in meltdown mode.
//
AppHourGlass(TRUE);
for (;;)
{
//
// allocate enough memory to hold the complete image of the file
// plus one character for good measure (the forced null termination
// could fault if don't add one char to size).
//
psz = GlobalAllocPtr(GMEM_MOVEABLE, uFileLen + sizeof(TCHAR));
if (NULL == psz)
break;

//
// read the file and copy the contents into the script window
//
#ifdef UNICODE
fReturn = ReadFile(hf, psz, uFileLen, (LPDWORD)&uFileLen, NULL);
if (!fReturn)
break;
#else
uFileLen = _lread(hf, psz, uFileLen);
if ((UINT)-1 == uFileLen)
break;
#endif

//
// make sure the text gets null terminated, then shove it into
// the script window.
//
psz[uFileLen / sizeof(TCHAR)] = '\0';

hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
#ifdef UNICODE
//
// determine whether the text is Unicode or ANSI: Unicode files
// all start with a Byte Order Mark (BOM) that will be 0xFEFF in
// the proper Endian for the file.
//
// !!! this application only deals with ANSI and Little Endian
// Unicode files--should handle more gracefully if not...
//
if ((uFileLen >= sizeof(TCHAR)) && (psz[0] == 0xFEFF))
{
//
// set the window text as Unicode--note that we do not send
// the BOM, the edit control doesn't want it...
//
gfuAppOptions |= APP_OPTF_UNICODE;
SetWindowTextW(hwndScript, (LPCWSTR)&psz[1]);
}
else
{
gfuAppOptions &= ~APP_OPTF_UNICODE;
SetWindowTextA(hwndScript, (LPCSTR)psz);
}
#else
SetWindowText(hwndScript, (LPCTSTR)psz);
#endif

//
// now return the fully qualified path and title for the file
//!!!
#ifndef UNICODE
lstrcpy(pszFilePath, of.szPathName);
#endif
if (NULL != pszFileTitle)
{
AppGetFileTitle(pszFilePath, pszFileTitle);
}

fReturn = TRUE;
break;
}
AppHourGlass(FALSE);


//
// free memory (if allocated) and close the file. return the result
// of our attempt...
//
if (psz)
GlobalFreePtr(psz);

#ifdef UNICODE
CloseHandle(hf);
#else
_lclose(hf);
#endif


//
// !!! before returning, we really should try to display a error
// message... memory error, etc..
//
return (fReturn);
} // MciAppFileOpen()


//--------------------------------------------------------------------------;
//
// BOOL MciAppFileSave
//
// Description:
// This function saves the current script to the specified file.
//
// NOTE! This function does NOT bring up a save file chooser dialog
// if the file path is invalid. The calling function is responsible
// for making sure the file path is valid before calling this function.
//
// This function also does NOT modify the 'modified' bit of the script
// window. This is up to the calling function.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PCTSTR pszFilePath: Pointer to null terminated file path to save
// the script to.
//
// Return (BOOL):
// The return value is TRUE if the function is successful. It is FALSE
// if an error occurred. If an error does occur, then the contents
// of the script window was not saved.
//
// History:
// 2/ 6/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppFileSave
(
HWND hwnd,
PCTSTR pszFilePath
)
{
#ifdef UNICODE
HANDLE hf;
#else
HFILE hf;
OFSTRUCT of;
#endif
HWND hwndScript;
UINT uFileLen;
DWORD cbBytes;
LPTSTR psz;
BOOL fReturn;

//
// create the new file--if it already exists, this will open and init
// it to zero length.
//
#ifdef UNICODE
hf = CreateFile(pszFilePath, GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, 0, 0);
if (INVALID_HANDLE_VALUE == hf)
return (FALSE);
#else
of.cBytes = sizeof(of);
hf = OpenFile(pszFilePath, &of, OF_CREATE);
if (HFILE_ERROR == hf)
return (FALSE);
#endif

//
// assume the worst
//
fReturn = FALSE;

//
// get the length in bytes of the script--we add 1 to the result
// because GetWindowTextLength returns the length in bytes NOT including
// the null terminator.
//
// !!! UNICODE !!!
//
hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
uFileLen = (UINT)GetWindowTextLength(hwndScript) + 1;

//!!! nFileLen = nFileLen*sizeof(TCHAR); We write ASCII

//
// allocate a buffer to hold the script text, get the text, and write
// it out. display an hour glass in case the file is large and/or we
// are writing to a slow device...
//
AppHourGlass(TRUE);
for (;;)
{
//
// allocate enough memory to hold the complete image of the script
//
psz = GlobalAllocPtr(GMEM_MOVEABLE, uFileLen + sizeof(TCHAR));
if (NULL == psz)
break;

//
// read the contents of the script window and write it to the
// new file
//
#ifdef UNICODE
// !!!! Save ASCII file
GetWindowTextA(hwndScript, (LPSTR)psz, uFileLen);
WriteFile(hf, psz, uFileLen, &cbBytes, NULL);
#else
GetWindowText(hwndScript, (LPTSTR)psz, uFileLen);
cbBytes = (DWORD)_lwrite(hf, (LPSTR)psz, uFileLen);
#endif

//
// succeed
//
fReturn = TRUE;
break;
}
AppHourGlass(FALSE);


//
// free memory (if allocated) and close the file. return the result
// of our attempt...
//
if (psz)
GlobalFreePtr(psz);

#ifdef UNICODE
CloseHandle(hf);
#else
_lclose(hf);
#endif


//
// !!! before returning, we really should try to display an error
// message... memory error, etc..
//
return (fReturn);
} // MciAppFileSave()


//==========================================================================;
//
// MCI Device List stuff
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// UINT MciAppGetNumDevices
//
// Description:
// This function sends a command to MCI querying it as to how many
// devices are currently open in the system. This number is the
// return value.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (UINT):
// Returns the number of currently open MCI devices in the system.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

UINT FNGLOBAL MciAppGetNumDevices
(
HWND hwnd
)
{
MCI_SYSINFO_PARMS msip;
MCIERROR mciError;
DWORD dwDevices;

//
// set things up so that MCI puts the number of open devices directly
// into dwDevices
//
msip.lpstrReturn = (LPVOID)&dwDevices;
msip.dwRetSize = sizeof(dwDevices);

//
// ask MCI how many open devices are in the system--if this errors
// then return 0...
//
mciError = mciSendCommand(MCI_ALL_DEVICE_ID,
MCI_SYSINFO,
MCI_SYSINFO_OPEN | MCI_SYSINFO_QUANTITY,
(DWORD)(LPVOID)&msip);

if (MMSYSERR_NOERROR == mciError)
return ((UINT)dwDevices);

return (0);
} // MciAppGetNumDevices()


//--------------------------------------------------------------------------;
//
// BOOL MciAppDeviceListUpdate
//
// Description:
// This function updates the MCI Device List window if it is displayed.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// BOOL fForceUpdate: Forces the list to be updated even if number
// of devices has not changed.
//
// Return (BOOL):
// Returns TRUE if the device list needed to be updated. FALSE if
// the list was fine...
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL MciAppDeviceListUpdate
(
HWND hwnd,
HWND hwndDevices,
BOOL fForceUpdate
)
{
static TCHAR szFormatDevice[] = TEXT("%2d. '%s'");
static UINT uLastNumDevices;

TCHAR ach[APP_MAX_STRING_RC_CHARS];
TCHAR szDevName[APP_MAX_STRING_RC_CHARS];
MCI_SYSINFO_PARMS msip;
MCIERROR mciError;
HWND hwndList;
UINT uNumDevs;
UINT u;

//
// if the devices window is not displayed, then fail..
//
if (NULL == hwndDevices)
return (FALSE);

uNumDevs = MciAppGetNumDevices(hwnd);

//
// if not being forced to update list, then make a quick check to
// see if we should update the list...
//
if (!fForceUpdate && (uNumDevs == uLastNumDevices))
return (FALSE);

//
// really update the device list...
//
uLastNumDevices = uNumDevs;

//
// initialize the devices listbox...
//
hwndList = GetDlgItem(hwndDevices, IDD_MCIDEVS_LIST_OPEN);
ListBox_ResetContent(hwndList);

SetWindowRedraw(hwndList, FALSE);

//
// get the name of each open device in the system and add it to the
// device list box...
//
for (u = 1; u <= uNumDevs; ++u)
{
msip.dwNumber = u;
msip.lpstrReturn = (LPVOID)&szDevName;
msip.dwRetSize = SIZEOF(szDevName);

//
// get the name--if an error is encountered, then skip to the
// next device...
//
mciError = mciSendCommand(MCI_ALL_DEVICE_ID,
MCI_SYSINFO,
MCI_SYSINFO_OPEN | MCI_SYSINFO_NAME,
(DWORD)(LPVOID)&msip);

if (MMSYSERR_NOERROR != mciError)
continue;

//
// add the device name to the listbox..
//
wsprintf(ach, szFormatDevice, u, (LPSTR)szDevName);
ListBox_AddString(hwndList, ach);
}

if (0 != uNumDevs)
ListBox_SetCurSel(hwndList, 0);

SetWindowRedraw(hwndList, TRUE);
InvalidateRect(hwndList, NULL, TRUE);

//
// set the info button to the correct state: enabled if there are
// devices open--disabled if no devices are open.
//
EnableWindow(GetDlgItem(hwndDevices, IDD_MCIDEVS_BTN_INFO), 0 != uNumDevs);

return (TRUE);
} // MciAppDeviceListUpdate()


//--------------------------------------------------------------------------;
//
// BOOL MciAppCloseAllDevices
//
// Description:
// This function sends the MCI command "close all" and then updates
// the displayed info for the application.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (BOOL):
// Always returns TRUE.
//
// History:
// 2/ 8/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppCloseAllDevices
(
HWND hwnd
)
{
static TCHAR szCloseAll[] = TEXT("close all");

//
// close all open devices and update the device list if it is being
// displayed...
//
mciSendString(szCloseAll, NULL, 0, NULL);
MciAppDeviceListUpdate(hwnd, ghwndDevices, FALSE);

return (TRUE);
} // MciAppCloseAllDevices()


//--------------------------------------------------------------------------;
//
// BOOL MciAppDeviceDlgProc
//
// Description:
// Callback function for the dialog box which displays a list of the
// currently opened MCI devices.
//
// 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.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNEXPORT MciAppDeviceDlgProc
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
RECT rcApp;
RECT rc;
HFONT hfont;
HWND hwndParent;
UINT u;
int n;

switch (uMsg)
{
case WM_INITDIALOG:
//
// move the window so it sits in the upper right corner of the
// main window by default...
//
GetWindowRect(GetParent(hwnd), &rcApp);
GetWindowRect(hwnd, &rc);

n = (int)(rc.right - rc.left);
rc.left = rcApp.right - n - 30;
MoveWindow(hwnd, (int)rc.left, (int)rc.top,
n, (int)(rc.bottom - rc.top), FALSE);

//
//
//
hfont = GetStockFont(SYSTEM_FIXED_FONT);
SetWindowFont(GetDlgItem(hwnd, IDD_MCIDEVS_LIST_OPEN), hfont, FALSE);

//
// update the information displayed in the listbox
//
MciAppDeviceListUpdate(GetParent(hwnd), hwnd, TRUE);

return (TRUE);

case WM_COMMAND:
u = GET_WM_COMMAND_ID(wParam, lParam);
switch (u)
{
case IDCANCEL:
case IDOK:
//
// return button id of the one that was pressed...
//
hwndParent = GetParent(hwnd);
EndDialog(hwnd, u);

ghwndDevices = NULL;
gfuAppOptions &= ~APP_OPTF_DEVICELIST;
SetFocus(GetDlgItem(hwndParent, IDD_APP_EDIT_SCRIPT));
break;

case IDD_MCIDEVS_BTN_INFO:
n = ListBox_GetCurSel(GetDlgItem(hwnd, IDD_MCIDEVS_LIST_OPEN));
if (LB_ERR == n)
break;

break;
}
break;
}

return (FALSE);
} // MciAppDeviceDlgProc()


//--------------------------------------------------------------------------;
//
// BOOL MciAppDeviceList
//
// Description:
// This function either displays or destroys the MCI device list
// window.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// BOOL fActivate: TRUE if the device list window should keep
// the activation. FALSE if the current active window should remain
// active.
//
// Return (BOOL):
// The return value is TRUE if the device list is displayed. It is
// FALSE if the device list has been canceled/closed.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppDeviceList
(
HWND hwnd,
BOOL fActivate
)
{
HWND hwndFocus;

//
// should we display or destroy it?
//
if (0 != (gfuAppOptions & APP_OPTF_DEVICELIST))
{
if (NULL != ghwndDevices)
return (TRUE);

hwndFocus = GetFocus();

ghwndDevices = CreateDialog(ghinst, DLG_MCIDEVS, hwnd, (DLGPROC)MciAppDeviceDlgProc);
if (NULL == ghwndDevices)
{
gfuAppOptions &= ~APP_OPTF_DEVICELIST;
return (FALSE);
}

//
// note that the window will 'flash' because we used CreateDialog
// when creating the device list window. we keep the flash to
// a minimum by creating the device window hidden and then showing
// it... if you want to get rid of the flash completely, then
// use CreateWindow and write a lot more code.
//
if (fActivate)
{
ShowWindow(ghwndDevices, SW_SHOW);
}
else
{
SetActiveWindow(hwnd);
SetFocus(hwndFocus);
ShowWindow(ghwndDevices, SW_SHOWNA);
}

return (TRUE);
}
else
{
if (NULL != ghwndDevices)
{
EndDialog(ghwndDevices, IDOK);
ghwndDevices = NULL;
}

return (FALSE);
}
} // MciAppDeviceList()


//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// BOOL MciAppUpdateOptions
//
// Description:
// This function updates the options status window to reflect the
// current status of the options.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (BOOL):
// Always returns TRUE.
//
// History:
// 2/ 8/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppUpdateOptions
(
HWND hwnd
)
{
#ifdef UNICODE
static TCHAR szFormatOptions[] = TEXT("%c%c%c%c");
#else
static TCHAR szFormatOptions[] = TEXT("%c%c%c");
#endif

TCHAR ach[APP_MAX_STRING_RC_CHARS];

//
// format an appropriate string for the options
//
wsprintf(ach, szFormatOptions,
#ifdef UNICODE
(0 != (gfuAppOptions & APP_OPTF_UNICODE)) ? 'U' : '-',
#endif
(0 != (gfuAppOptions & APP_OPTF_EDITONLY)) ? 'E' : '-',
(0 != (gfuAppOptions & APP_OPTF_YIELDEXEC)) ? 'Y' : '-',
(0 != (gfuAppOptions & APP_OPTF_DEBUGLOG)) ? 'L' : '-');

SetWindowText(GetDlgItem(hwnd, IDD_APP_TEXT_OPTIONS), ach);

return (TRUE);
} // MciAppUpdateOptions()


//--------------------------------------------------------------------------;
//
// BOOL MciAppResetStatus
//
// Description:
// This function resets all of the status windows (status, notify,
// and output). This is used when a new script is created/opened.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (BOOL):
// Always returns TRUE.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppResetStatus
(
HWND hwnd
)
{
//
// nuke all status text...
//
SetWindowText(GetDlgItem(hwnd, IDD_APP_TEXT_OUTPUT), gszNone);
SetWindowText(GetDlgItem(hwnd, IDD_APP_TEXT_STATUS), gszNull);
SetWindowText(GetDlgItem(hwnd, IDD_APP_TEXT_NOTIFY), gszNull);

MciAppUpdateOptions(hwnd);

return (TRUE);
} // MciAppResetStatus()


//--------------------------------------------------------------------------;
//
// void MciAppDebugLog
//
// 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
// MCI drivers.
//
// Arguments:
// PCTSTR pszFormat: Pointer to any valid format for wsprintf.
//
// Return (void):
// None.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

void FNCGLOBAL MciAppDebugLog
(
PCTSTR pszFormat,
...
)
{
va_list va;
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];


//

//  !!! UNICODE !!! 
//
//
if (0 != (gfuAppOptions & APP_OPTF_DEBUGLOG))
{
//
// format and display the string in a message box...
//
va_start(va, pszFormat);
wvsprintf(ach, pszFormat, va);
va_end(va);

OutputDebugString(gszAppName);
OutputDebugString(TEXT(": "));
OutputDebugString(ach);
OutputDebugString(TEXT("\r\n"));
}
} // MciAppDebugLog()


//--------------------------------------------------------------------------;
//
// BOOL MciAppHandleNotify
//
// Description:
// This function handles displaying the notification message from
// commands sent with the 'notify' option.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// UINT fuNotify: Notification flags (wParam) from MM_MCINOTIFY message.
//
// UINT uId: Device id sending notification.
//
// Return (BOOL):
// Always returns TRUE.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppHandleNotify
(
HWND hwnd,
UINT fuNotify,
UINT uId
)
{
static TCHAR szFormatNotify[] = TEXT("Notify(%u,%u): %s");
static TCHAR szDBFormatNotify[] = TEXT(" MCI Notify: Id=%u, Flag(%u)='%s'");

HWND hwndNotify;
TCHAR ach[APP_MAX_STRING_RC_CHARS];
UINT uIds;

//
//
//
//
switch (fuNotify)
{
case MCI_NOTIFY_SUCCESSFUL:
uIds = IDS_MCI_NOTIFY_SUCCESSFUL;
break;

case MCI_NOTIFY_SUPERSEDED:
uIds = IDS_MCI_NOTIFY_SUPERSEDED;
break;

case MCI_NOTIFY_ABORTED:
uIds = IDS_MCI_NOTIFY_ABORTED;
break;

case MCI_NOTIFY_FAILURE:
uIds = IDS_MCI_NOTIFY_FAILURE;
break;

default:
uIds = IDS_MCI_NOTIFY_UNKNOWN;
break;
}

LoadString(ghinst, uIds, ach, SIZEOF(ach));
hwndNotify = GetDlgItem(hwnd, IDD_APP_TEXT_NOTIFY);
AppSetWindowText(hwndNotify, szFormatNotify, uId, fuNotify, (LPSTR)ach);

//
// !!! UNICODE !!!
//
MciAppDebugLog(szDBFormatNotify, uId, fuNotify, (LPSTR)ach);

return (TRUE);
} // MciAppHandleNotify()


//--------------------------------------------------------------------------;
//
// BOOL MciAppErrorDlgProc
//
// Description:
// Callback function for the dialog box which occurs during the
// execution of an error in a loop of script commands. It displays
// Abort, Continue and Ignore buttons.
//
// 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.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNEXPORT MciAppErrorDlgProc
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
UINT u;

switch (uMsg)
{
case WM_INITDIALOG:
return (TRUE);

case WM_COMMAND:
u = GET_WM_COMMAND_ID(wParam, lParam);
switch (u)
{
case IDABORT:
case IDOK:
case IDIGNORE:
//
// return button id of the one that was pressed...
//
EndDialog(hwnd, u);
break;
}
break;
}

return (FALSE);
} // MciAppErrorDlgProc()


//--------------------------------------------------------------------------;
//
// MCIERROR MciAppSendString
//
// Description:
// Sends the specified string command to an MCI device via the MCI
// string interface. Any return strings from MCI devices are displayed
// in the 'Output' text box. Error values are displayed in the status
// bar.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PCTSTR pszCommand: Pointer to the string command to be executed.
//
// Return (MCIERROR):
// The return value is the result of the mciSendString() function.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

MCIERROR FNGLOBAL MciAppSendString
(
HWND hwnd,
PCTSTR pszCommand
)
{
static TCHAR szFormatDBSendString[] = TEXT("mciSendString('%s')");
static TCHAR szFormatOutput[] = TEXT("'%s'");
static TCHAR szFormatDBOutput[] = TEXT(" MCI Output: '%s'");
static TCHAR szFormatError[] = TEXT("MCI Error: [%lu], %s");
static TCHAR szFormatDBError[] = TEXT(" MCI Error : [%lu], '%s'");

TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
HWND hwndOutput;
HWND hwndStatus;
MCIERROR mciError;

//
// reset the notify window text..
//
SetWindowText(GetDlgItem(hwnd, IDD_APP_TEXT_NOTIFY), gszNull);


//
// send the string command to MCI--capture the return value and have
// the notification code go to the main window (MM_MCINOTIFY)...
//
// !!! UNICODE !!!
//
MciAppDebugLog(szFormatDBSendString, (LPSTR)pszCommand);
mciError = mciSendString(pszCommand, ach, SIZEOF(ach), hwnd);

//
// if the command errored, then beep the _speaker_ (-1). don't want
// to use wave devices because many mci scripts use them..
//
if (MMSYSERR_NOERROR != mciError)
MessageBeep((UINT)-1);

MciAppDebugLog(szFormatDBOutput, (LPSTR)ach);

//
// put the text message returned by MCI into the 'Output' box
//
hwndOutput = GetDlgItem(hwnd, IDD_APP_TEXT_OUTPUT);
if ('\0' == ach[0])
SetWindowText(hwndOutput, gszNone);
else
AppSetWindowText(hwndOutput, szFormatOutput, (LPSTR)ach);


//
// decode the error number returned by MCI and display the string in
// the status bar...
//
// !!! UNICODE !!!
//
mciGetErrorString(mciError, ach, SIZEOF(ach));
MciAppDebugLog(szFormatDBError, mciError, (LPSTR)ach);

hwndStatus = GetDlgItem(hwnd, IDD_APP_TEXT_STATUS);
AppSetWindowText(hwndStatus, szFormatError, mciError, (LPSTR)ach);


//
// update our list of currently open devices and stuff...
//
MciAppDeviceListUpdate(hwnd, ghwndDevices, FALSE);

return (mciError);
} // MciAppSendString()


//--------------------------------------------------------------------------;
//
// BOOL MciAppGetLine
//
// Description:
// Retrieves the contents of line (uLine) from the script window.
// The string is placed in the pszLineBuffer. All leading and trailing
// spaces (and comments) are removed.
//
// Arguments:
// HWND hwndScript: Handle to script window (multiline edit control)
// to extract nLine from.
//
// int nLine: Line index (zero based) to retrieve.
//
// PTSTR pszBuffer: Pointer to string buffer to receive nLine's
// contents.
//
// UINT cchBuffer: Size of pszBuffer is _characters_.
//
// Return (BOOL):
// The return value is TRUE if nLine was extracted from the script
// window. The return value is FALSE if nLine is out of range for
// the current contents of the script window.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL MciAppGetLine
(
HWND hwndScript,
int nLine,
PTSTR pszBuffer,
UINT cchBuffer
)
{
#define IS_SPACE(c) (' ' == (c) || '\t' == (c))
#define IS_EOL(c) ('\n' == (c) || '\r' == (c))
#define IS_WHITE(c) (IS_SPACE(c) || IS_EOL(c))

int nNumLines;

//
// find out how many lines are in the script window--if it is out of
// range then fail....
//
nNumLines = Edit_GetLineCount(hwndScript);
if ((nLine < 0) || (nLine >= nNumLines))
{
pszBuffer[0] = '\0';
return (FALSE);
}

//
// read the line requested into the string buffer..
//
cchBuffer = Edit_GetLine(hwndScript, nLine, pszBuffer, cchBuffer);


//
// strip trailing spaces
//
while ((cchBuffer > 0) && IS_WHITE(pszBuffer[cchBuffer - 1]))
{
cchBuffer--;
}

pszBuffer[cchBuffer] = '\0';

return (TRUE);
} // MciAppGetLine()


//--------------------------------------------------------------------------;
//
// BOOL MciAppInternalCommand
//
// Description:
//
//
// Arguments:
//
//
// Return (BOOL):
//
//
// History:
// 2/20/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL MciAppInternalCommand
(
HWND hwnd,
PTSTR pszCommandLine,
BOOL fYield
)
{
static TCHAR szWhiteToken[] = " \t\n";
static TCHAR szTokenSleep[] = "sleep";
static TCHAR szTokenPause[] = "pause";

static TCHAR szFormatError[] = TEXT("INTERNAL Error: Command (%s), %s.");
static TCHAR szFormatDBError[] = TEXT(" ICmd Error: Command (%s), %s.");

static TCHAR szSleepComplete[] = TEXT("Sleep complete.");
static TCHAR szSleepDBComplete[] = TEXT(" Sleeping: sleep complete.");

static TCHAR szErrorBadCmd[] = TEXT("unrecognized command");
static TCHAR szErrorBadParam[] = TEXT("invalid parameter");
static TCHAR szErrorParamRqd[] = TEXT("parameter(s) required");

static TCHAR szFormatSleeping[] = TEXT("Sleeping for %lu %s...");
static TCHAR szSeconds[] = TEXT("seconds");
static TCHAR szMilliseconds[] = TEXT("milliseconds");
static TCHAR szMinutes[] = TEXT("minutes");
static TCHAR szHours[] = TEXT("hours");

PTSTR pchToken;
PTSTR pchCommand;
HWND hwndStatus;
long l;
DWORD dwSleep;
PTSTR szSleepUnit;

//
// parse the internal command line...
//
pchToken = strtok(pszCommandLine, szWhiteToken);
if (NULL == pchToken)
return (TRUE);

hwndStatus = GetDlgItem(hwnd, IDD_APP_TEXT_STATUS);
pchCommand = pchToken;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
//
// sleep x [ms | s | m | h] (default is milliseconds)
// pause x [ms | s | m | h] (default is milliseconds)
//
//
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;

if ((0 == lstrcmpi(pchCommand, szTokenSleep)) ||
(0 == lstrcmpi(pchCommand, szTokenPause)))
{
pchToken = strtok(NULL, szWhiteToken);
if (NULL == pchToken)
{
MessageBeep((UINT)-1);
AppSetWindowText(hwndStatus, szFormatError,
(LPSTR)pchCommand, (LPSTR)szErrorParamRqd);
MciAppDebugLog(szFormatDBError, (LPSTR)pchCommand,
(LPSTR)szErrorParamRqd);
return (TRUE);
}

//
// assume the first token after sleep/pause command is a
// number--do not allow zero or negative numbers!
//
l = atol(pchToken);
if (0 >= l)
{
MessageBeep((UINT)-1);
AppSetWindowText(hwndStatus, szFormatError,
(LPSTR)pchCommand, (LPSTR)szErrorBadParam);
MciAppDebugLog(szFormatDBError, (LPSTR)pchCommand,
(LPSTR)szErrorBadParam);
return (TRUE);
}

//
// now see what unit
//
dwSleep = (DWORD)l;
szSleepUnit = szMilliseconds;
pchToken = strtok(NULL, szWhiteToken);
if (NULL != pchToken)
{
switch (pchToken[0])
{
case 's':
case 'S':
dwSleep *= 1000;
szSleepUnit = szSeconds;
break;

case 'm':
case 'M':
//
// if unit marker is not 'ms' then assume minutes
//
if ('s' != pchToken[1])
{
dwSleep *= 1000L * 60;
szSleepUnit = szMinutes;
}
break;

case 'h':
case 'H':
dwSleep *= 1000L * 60 * 60;
szSleepUnit = szHours;
break;
}
}

//
//
//
AppSetWindowText(hwndStatus, szFormatSleeping, l, (LPSTR)szSleepUnit);
MciAppDebugLog(szFormatSleeping, l, (LPSTR)szSleepUnit);

if (!fYield)
UpdateWindow(hwndStatus);

//
// !!! broken !!!
//
//
#ifdef WIN32
Sleep(dwSleep);
#else
{
DWORD dwStart;

if (!fYield)
AppHourGlass(TRUE);

dwStart = timeGetTime();
while ((timeGetTime() - dwStart) < dwSleep)
{
if (fYield)
{
AppYield(hwnd, FALSE);
if (gfAbortExec)
break;
}
else
{
if (GetAsyncKeyState(VK_ESCAPE) < 0)
break;
}
}

if (!fYield)
AppHourGlass(FALSE);
}
#endif

AppSetWindowText(hwndStatus, szSleepComplete);
MciAppDebugLog(szSleepDBComplete);

if (!fYield)
UpdateWindow(hwndStatus);

return (TRUE);
}


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
//
// unrecognized command...
//
//
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;

MessageBeep((UINT)-1);
AppSetWindowText(hwndStatus, szFormatError,
(LPSTR)pchCommand, (LPSTR)szErrorBadCmd);
MciAppDebugLog(szFormatDBError, (LPSTR)pchCommand, (LPSTR)szErrorBadCmd);

return (TRUE);
} // MciAppInternalCommand()


//--------------------------------------------------------------------------;
//
// MCIERROR MciAppSingleStep
//
// Description:
// Executes one line from the script window.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (MCIERROR):
// The return value is the MCIERROR return value from mciSendString.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

MCIERROR FNGLOBAL MciAppSingleStep
(
HWND hwnd
)
{
TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
HWND hwndScript;
int nLine;
int nLineStart;
int nSelStart;
int nSelEnd;
MCIERROR mciError;
BOOL fDidOne;

//
// initialize a bunch of stuff...
//
hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
mciError = MMSYSERR_NOERROR;
fDidOne = FALSE;
nLineStart = Edit_LineFromChar(hwndScript, -1);
nLine = nLineStart;

//
// step through the script starting with the current line until we
// execute one non-comment/blank line...
//
for ( ; MciAppGetLine(hwndScript, nLine, ach, SIZEOF(ach)); nLine++)
{
//
// select the current line that is going to be executed....
//
nSelStart = Edit_LineIndex(hwndScript, nLine);
nSelEnd = Edit_LineIndex(hwndScript, nLine + 1);
if (nSelEnd < nSelStart)
nSelEnd = 0x7FFF;
else if (nSelEnd > nSelStart)
nSelEnd -= 1;

Edit_SetSel(hwndScript, nSelStart, nSelEnd);

//
// anything worth doing something with?
//
if (('\0' == ach[0]) || (';' == ach[0]))
continue;

//
// if first character is '!' then process rest of line as an
// internale MCI App command...
//
if ('!' == ach[0])
{
MciAppInternalCommand(hwnd, &ach[1], FALSE);
continue;
}

//
// if we have actually executed a line, then break out... we do
// this so the above code will have selected the next line for
// single stepping.
//
if (fDidOne)
break;


//
// send the command on the current line to MCI via the string
// interface.
//
mciError = MciAppSendString(hwnd, ach);
fDidOne = TRUE;
}

SetFocus(hwndScript);
return (mciError);
} // MciAppSingleStep()


//--------------------------------------------------------------------------;
//
// MCIERROR MciAppExecute
//
// Description:
// Executes the MCI command which is currently selected in the script
// window. If fSingleStep is TRUE, then only this one line will be
// executed. Otherwise, every line from the currently selected line to
// the last line in the script window will be executed sequentially.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// BOOL fYield: TRUE if execution should yield between MCI commands.
//
// Return (MCIERROR):
// The return value is the last MCIERROR return value from executing.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

MCIERROR FNGLOBAL MciAppExecute
(
HWND hwnd,
BOOL fYield
)
{
static TCHAR szFormatRuncount[] = TEXT("%u");

TCHAR ach[APP_MAX_STRING_ERROR_CHARS];
HWND hwndScript;
HWND hwndRuncount;
UINT uRunCount;
UINT u;
UINT uLinesExecuted;
int n;
int nLine;
int nLineStart;
int nSelStart;
int nSelEnd;
MCIERROR mciError;
BOOL fIgnoreErrors;


hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);
hwndRuncount = GetDlgItem(hwnd, IDD_APP_EDIT_RUNCOUNT);

uRunCount = GetDlgItemInt(hwnd, IDD_APP_EDIT_RUNCOUNT, NULL, TRUE);

uLinesExecuted = 0;
mciError = MMSYSERR_NOERROR;
fIgnoreErrors = FALSE;

gfExecuting = TRUE;
gfAbortExec = FALSE;


//
// if we are yielding, then disable a bunch of controls...
//
// if we are not yielding during then bring up an hour glass to show
// we be busy... don't bother with disabling controls--they can't
// be used
//
if (fYield)
{
static TCHAR szButtonStop[] = TEXT("&Stop");

SetWindowText(GetDlgItem(hwnd, IDD_APP_BTN_STEP), szButtonStop);

EnableWindow(GetDlgItem(hwnd, IDD_APP_BTN_GO), FALSE);
EnableWindow(GetDlgItem(hwnd, IDD_APP_BTN_RUN), FALSE);
EnableWindow(GetDlgItem(hwnd, IDD_APP_EDIT_RUNCOUNT), FALSE);
}
else
{
AppHourGlass(TRUE);
}


//
// go through every line in the script window from the currently
// selected line to the last line...
//
nLineStart = Edit_LineFromChar(hwndScript, -1);
for (u = uRunCount; u; u--)
{
nLine = nLineStart;
for ( ; MciAppGetLine(hwndScript, nLine, ach, SIZEOF(ach)); nLine++)
{
//
// select the current line that is going to be executed....
//
nSelStart = Edit_LineIndex(hwndScript, nLine);
nSelEnd = Edit_LineIndex(hwndScript, nLine + 1);
if (nSelEnd < nSelStart)
nSelEnd = 0x7FFF;
else if (nSelEnd > nSelStart)
nSelEnd -= 1;

Edit_SetSel(hwndScript, nSelStart, nSelEnd);


//
// check for abort...
//
if (gfAbortExec)
{
n = AppMsgBoxId(hwnd, MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL,
IDS_EXEC_ABORT);
if (IDYES == n)
goto MciApp_Execute_Exit;

gfAbortExec = FALSE;
}


//
// anything worth doing something with?
//
if (('\0' == ach[0]) || (';' == ach[0]))
continue;

//
// if first character is '!' then process rest of line as an
// internale MCI App command...
//
if ('!' == ach[0])
{
MciAppInternalCommand(hwnd, &ach[1], fYield);
continue;
}

//
// send the command on the current line to MCI via the string
// interface.
//
mciError = MciAppSendString(hwnd, ach);
if ((MMSYSERR_NOERROR != mciError) && !fIgnoreErrors)
{
n = DialogBox(ghinst, DLG_MCIERR, hwnd, (DLGPROC)MciAppErrorDlgProc);
switch (n)
{
case IDABORT:
goto MciApp_Execute_Exit;

case IDIGNORE:
fIgnoreErrors = TRUE;
break;
}
}

uLinesExecuted++;

//
// yield like a good little app..
//
if (fYield)
AppYield(hwnd, FALSE);
else
{
UpdateWindow(GetDlgItem(hwnd, IDD_APP_TEXT_STATUS));
UpdateWindow(GetDlgItem(hwnd, IDD_APP_TEXT_OUTPUT));
}
}

//
// SetDlgItemInt() won't update the field unless we yield
// so set window text instead... in case not yielding.
//
SetDlgItemInt(hwnd, IDD_APP_EDIT_RUNCOUNT, u, TRUE);
if (fYield)
AppYield(hwnd, FALSE);
else
UpdateWindow(GetDlgItem(hwnd, IDD_APP_EDIT_RUNCOUNT));
}


MciApp_Execute_Exit:

gfExecuting = FALSE;
if (fYield)
{
static TCHAR szButtonStep[] = TEXT("&Step");

SetWindowText(GetDlgItem(hwnd, IDD_APP_BTN_STEP), szButtonStep);

EnableWindow(GetDlgItem(hwnd, IDD_APP_BTN_GO), TRUE);
EnableWindow(GetDlgItem(hwnd, IDD_APP_BTN_RUN), TRUE);
EnableWindow(GetDlgItem(hwnd, IDD_APP_EDIT_RUNCOUNT), TRUE);
}
else
{
AppHourGlass(FALSE);
}

SetDlgItemInt(hwnd, IDD_APP_EDIT_RUNCOUNT, uRunCount, TRUE);
SetFocus(hwndScript);

return (mciError);
} // MciAppExecute()


//--------------------------------------------------------------------------;
//
// BOOL MciAppEnterLine
//
// Description:
// This function handles entering a new line into the script window.
// This may involve executing the new line if Options.Edit Only is
// not selected.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// BOOL fEditOnly: TRUE if in data entry mode (won't single step the
// current line before inserting CR/LF). FALSE if the current line
// should be executed and a CR/LF inserted at the _END_ of the
// current line.
//
// Return (BOOL):
// The return value is always TRUE.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppEnterLine
(
HWND hwnd,
BOOL fEditOnly
)
{
static TCHAR szEOL[] = TEXT("\r\n");

HWND hwndScript;
int n;

hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);

//
// if Options.Edit Only is checked or the user pressed Alt+Enter,
// then act like a simple edit control...
//
if (fEditOnly)
{
Edit_ReplaceSel(hwndScript, szEOL);
return (TRUE);
}

//
// get current line (containing caret)--if there is a
// selection, the line number of the line containing the
// _beginning_ of the selection is retrieved.
//
SetFocus(hwndScript);
n = Edit_LineFromChar(hwndScript, -1);
MciAppSingleStep(hwnd);
n = Edit_LineIndex(hwndScript, n);
Edit_SetSel(hwndScript, n, n);

//
// now force CR/LF at the _end_ of the current line
//
FORWARD_WM_KEYDOWN(hwndScript, VK_END, 1, 0, SendMessage);
FORWARD_WM_KEYUP(hwndScript, VK_END, 1, 0, SendMessage);
Edit_ReplaceSel(hwndScript, szEOL);

return (TRUE);
} // MciAppEnterLine()


//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// BOOL MciAppDispatchMessage
//
// Description:
//
//
// Arguments:
//
//
// Return (BOOL):
//
//
// History:
// 2/ 8/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppDispatchMessage
(
HWND hwnd,
PMSG pmsg
)
{
static HWND hwndScript;
static HACCEL haccl;

UINT u;

//
// if this is the first time through, cache some stuff...
//
if (NULL == hwndScript)
hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);

if (NULL == haccl)
haccl = LoadAccelerators(ghinst, ACCEL_APP);


//
// peek at the message being sent. if it is one of the special
// things for the script window, then deal with it...
//
// !!! this is somewhat bogus !!!
//
if ((WM_KEYDOWN == pmsg->message) || (WM_SYSKEYDOWN == pmsg->message))
{
if (GetActiveWindow() != hwnd)
goto MciApp_Dispatch_Continue;

switch (pmsg->wParam)
{
case VK_RETURN:
if (GetFocus() != hwndScript)
break;

//
// force a line to be 'executed' and stuff... if Alt+Enter
// was used, then reverse the logic of 'Edit Only' by
// setting uCode to 0 (from menu)
//
u = (GetKeyState(VK_MENU) < 0) ? 0 : 1;
FORWARD_WM_COMMAND(hwnd, IDOK, hwndScript, u, PostMessage);
return (TRUE);

case VK_TAB:
if (WM_SYSKEYDOWN == pmsg->message)
break;

if (GetKeyState(VK_CONTROL) < 0)
{
if (GetFocus() == hwndScript)
{
SetFocus(GetDlgItem(hwnd, IDD_APP_BTN_STEP));
return (TRUE);
}

SetFocus(GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT));
return (TRUE);
}
else if (GetFocus() == hwndScript)
{
static TCHAR szTab[] = TEXT("\t");
Edit_ReplaceSel(hwndScript, szTab);
return (TRUE);
}
break;
}
}


MciApp_Dispatch_Continue:

//
// update the device list if it needs to be updated--also
// do the floating dialog box message thing...
//
if (NULL != ghwndDevices)
{
MciAppDeviceListUpdate(hwnd, ghwndDevices, FALSE);

if (IsDialogMessage(ghwndDevices, pmsg))
return (TRUE);
}


//
// take care of accelerators and dialog box style things...
//
if (TranslateAccelerator(hwnd, haccl, pmsg) ||
IsDialogMessage(hwnd, pmsg))

{ 
return (TRUE);
}

TranslateMessage(pmsg);
DispatchMessage(pmsg);

return (TRUE);
} // MciAppDispatchMessage()


//==========================================================================;
//
// Startup and shutdown code...
//
//
//==========================================================================;

TCHAR gszKeyOptions[] = TEXT("Options");
TCHAR gszFormatOptions[] = TEXT("%u");

TCHAR gszKeyWindow[] = TEXT("Window");
TCHAR gszKeyFont[] = TEXT("Font");


//--------------------------------------------------------------------------;
//
// BOOL MciAppChooseFont
//
// Description:
// This function lets the user choose a new font for the script window.
// After a new font is chosen, the font structure is stored to the
// .ini file so it can be restored on the next run of this application.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// Return (BOOL):
// The return value is TRUE if a new font was chosen. It is FALSE if
// the user canceled the operation.
//
// History:
// 2/ 7/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppChooseFont
(
HWND hwnd
)
{
LOGFONT lf;
HWND hwndScript;
HFONT hfont;
HFONT hfontNew;

hwndScript = GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT);

//
// get the current script font and pass it to the choose font dialog
//
hfont = GetWindowFont(hwndScript);

hfontNew = AppChooseFont(hwnd, hfont, &lf);
if (NULL == hfontNew)
return (FALSE);

//
// select the new font into the script window and delete the old one
//
SetWindowFont(hwndScript, hfontNew, TRUE);
DeleteFont(hfont);


//
// save the complete description of the chosen font so there can be
// no strangness in the font mapping next run. this is overkill, but
// it works...
//
AppProfileWriteBytes(gszKeyFont, (LPBYTE)&lf, sizeof(lf));

return (TRUE);
} // MciAppChooseFont()


//--------------------------------------------------------------------------;
//
// BOOL MciAppSettingsRestore
//
// Description:
// This function restores state information for the application. This
// function is called just after the main window is created (it has
// not been ShowWindow()'d). This function will generate the call
// to ShowWindow before returning.
//
// Arguments:
// HWND hwnd: Handle to main window that has just been created but
// not shown.
//
// int nCmdShow: The state that the application window should show as.
//
// Return (BOOL):
// The return value is always TRUE.
//
// History:
// 2/15/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL MciAppSettingsRestore
(
HWND hwnd,
int nCmdShow
)
{
static TCHAR szSecExtensions[] = TEXT("Extensions");
static TCHAR szKeyMCI[] = TEXT("mci");
static TCHAR szValAssocMCI[] = TEXT("mciapp.exe ^.mci");
static TCHAR szKeyMCS[] = TEXT("mcs");
static TCHAR szValAssocMCS[] = TEXT("mciapp.exe ^.mcs");

TCHAR ach[APP_MAX_STRING_RC_CHARS];
WINDOWPLACEMENT wp;
HFONT hfont;
LOGFONT lf;
UINT u;
RECT rc;
RECT rcWindow;
POINT pt;
int n;
BOOL f;


//
// restore the previous Options state...
//
gfuAppOptions = GetProfileInt(gszAppSection, gszKeyOptions, gfuAppOptions);


//
// we want to make sure that the association for .MCI and .MCS files is
// to run MCIAPP.EXE. this way any app that ShellExecute's a .MCI or
// .MCS file will run this app...
//
// in WIN.INI:
//
// [Extensions]
// mci = mciapp.exe ^.mci
// mcs = mciapp.exe ^.mcs
//
u = (UINT)GetProfileString(szSecExtensions, szKeyMCI, gszNull, ach, SIZEOF(ach));
if ((0 == u) || lstrcmpi(ach, szValAssocMCI))
{
WriteProfileString(szSecExtensions, szKeyMCI, szValAssocMCI);
}

u = (UINT)GetProfileString(szSecExtensions, szKeyMCS, gszNull, ach, SIZEOF(ach));
if ((0 == u) || lstrcmpi(ach, szValAssocMCS))
{
WriteProfileString(szSecExtensions, szKeyMCS, szValAssocMCS);
}

//
// restore the user's preferred font.
//
f = AppProfileReadBytes(gszKeyFont, (LPBYTE)&lf, sizeof(lf));
if (f)
{
hfont = CreateFontIndirect(&lf);
if (NULL != hfont)
{
SetWindowFont(GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT), hfont, FALSE);
}
}


//
// grab the stored window position and size from the .ini file...
// there must be four arguments stored or the entry is considered
// invalid.
//
f = AppProfileReadBytes(gszKeyWindow, (LPBYTE)&rcWindow, sizeof(rcWindow));
if (f)
{
//
// to make sure the user can always get at the window, check to
// see if the midpoint of the caption is visible--if it is not,
// then default to the default position used when creating the
// window.
//
n = (rcWindow.right - rcWindow.left) / 2;
pt.x = (n + rcWindow.left);

n = GetSystemMetrics(SM_CYCAPTION) / 2 + GetSystemMetrics(SM_CXFRAME);
pt.y = (n + rcWindow.top);

GetWindowRect(GetDesktopWindow(), &rc);
if (PtInRect(&rc, pt))
{
//
// fill out the window placement structure--default the
// maximized and minimized states to default placement by
// getting its current placement.
//
wp.length = sizeof(wp);
GetWindowPlacement(hwnd, &wp);

wp.showCmd = nCmdShow;
wp.rcNormalPosition = rcWindow;

SetWindowPlacement(hwnd, &wp);
return (TRUE);
}
}

//
// show defaulted and succeed
//
ShowWindow(hwnd, nCmdShow);
return (TRUE);
} // MciAppSettingsRestore()


//--------------------------------------------------------------------------;
//
// BOOL MciAppSettingsSave
//
// Description:
// This function saves the current state information for the application.
// It is called just before the main window is closed (destroyed); or
// as Windows is exiting (query end session).
//
// Note that this function should not destroy any resources--it can
// be called at any time to save a snapshot of the application state.
//
// Arguments:
// HWND hwnd: Handle to main window that will be destroyed shortly.
//
// Return (BOOL):
// The return value is always TRUE.
//
// History:
// 2/15/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppSettingsSave
(
HWND hwnd
)
{
TCHAR ach[APP_MAX_STRING_RC_CHARS];
WINDOWPLACEMENT wp;
PRECT prc;
BOOL f;

//
// save the current option settings--note that we ALWAYS turn off the
// debug logging option so the app doesn't try to OutputDebugString
// unexpectedly during the next session...
//
gfuAppOptions &= ~APP_OPTF_DEBUGLOG;
if (GetProfileInt(gszAppSection, gszKeyOptions, 0) != gfuAppOptions)
{
wsprintf(ach, gszFormatOptions, gfuAppOptions);
WriteProfileString(gszAppSection, gszKeyOptions, ach);
}

//
// save the current window placement--only store the size and location
// of the restored window. maximized and minimized states should
// remain defaulted on the next invocation of this application.
//
wp.length = sizeof(wp);
f = GetWindowPlacement(hwnd, &wp);
if (f)
{
prc = &wp.rcNormalPosition;

DPF(0, "WindowPlacement: show=%d, minX=%d, minY=%d, maxX=%d, maxY=%d",
wp.showCmd, wp.ptMinPosition.x, wp.ptMinPosition.y,
wp.ptMaxPosition.x, wp.ptMaxPosition.y);

DPF(0, " normX=%d, normY=%d, normW=%d, normH=%d",
prc->left, prc->top, prc->right, prc->bottom);

//
// save the _bounding rectangle_ of the restored window state...
//
AppProfileWriteBytes(gszKeyWindow, (LPBYTE)prc, sizeof(*prc));
}


//
// succeed
//
return (TRUE);
} // MciAppSettingsSave()


//--------------------------------------------------------------------------;
//
// BOOL MciAppShutdown
//
// Description:
// This function is called to gracefully shut down the application.
// If the application should not be closed, a FALSE value is returned.
// This function is called for WM_CLOSE and WM_QUERYENDSESSION
// messages...
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PTSTR pszFilePath: Pointer to current file path of script. If the
// file needs to be saved, then this buffer will receive the new
// file path if one is chosen.
//
// PTSTR pszFilePath: Pointer to current file title of script. If the
// file needs to be saved, then this buffer will receive the new
// file title if one is chosen.
//
// Return (BOOL):
// Returns TRUE if the application can proceed with close. Returns
// FALSE if the application should NOT be closed.
//
// History:
// 2/ 9/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppShutdown
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle
)
{
int n;
UINT u;
BOOL f;

//
// if we are currently executing a script (Run or Go), things would
// get very messy if we tried to close the application. so disallow
// it!
//
// !!! this is rude--bring up dialog with the problem statement !!!
//
if (gfExecuting)
{
MessageBeep((UINT)-1);
return (FALSE);
}

//
// check if the script has been modified without saving. if the user
// cancels the operation, then we will NOT close the application.
//
f = MciAppFileSaveModified(hwnd, pszFilePath, pszFileTitle);
if (!f)
return (FALSE);

//
// if devices are still open, ask user if we should close them down
// before exiting. the only reason we don't do this by default is
// for application cleanup testing in mmsystem... i suppose it is
// a nifty reminder for scripts that don't close devices also.
//
u = MciAppGetNumDevices(hwnd);
if (0 != u)
{
n = AppMsgBoxId(hwnd, MB_YESNOCANCEL | MB_ICONEXCLAMATION | MB_TASKMODAL
| MB_SETFOREGROUND, IDS_WARN_OPEN_DEVICES, u);
switch (n)
{
case IDYES:
MciAppCloseAllDevices(hwnd);
break;

case IDNO:
break;

case IDCANCEL:
return (FALSE);
}
}


//
// save any settings that should be saved on app termination...
//
MciAppSettingsSave(hwnd);

//
// allow closing of application...
//
return (TRUE);
} // MciAppShutdown()


//--------------------------------------------------------------------------;
//
// BOOL MciAppInit
//
// Description:
//
//
// Arguments:
//
//
// Return (BOOL):
//
//
// History:
// 2/15/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppInit
(
HWND hwnd,
PTSTR pszFilePath,
PTSTR pszFileTitle,
LPTSTR pszCmdLine,
int nCmdShow
)
{
BOOL f;

//
//
//
MciAppSettingsRestore(hwnd, nCmdShow);

//
// strip the command line..
//
if (NULL != pszCmdLine)
{
while (('\0' != *pszCmdLine) && (' ' == *pszCmdLine))
pszCmdLine++;
}

//
// if there is a command line, assume it is a filename for a script
// and try to open it. otherwise, just initialize the script window.
//
if ((NULL != pszCmdLine) && ('\0' != *pszCmdLine))
{
//
// attempt to open the specified file..
//
lstrcpy(pszFilePath, pszCmdLine);
f = MciAppFileOpen(hwnd, pszFilePath, pszFileTitle);
if (f)
{
AppTitle(hwnd, pszFileTitle);
SetFocus(GetDlgItem(hwnd, IDD_APP_EDIT_SCRIPT));
}
else
{
//
// opening the command line file was untriumphant..
//
AppMsgBoxId(hwnd, MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL,
IDS_ERROR_OPEN_FAILED, (LPSTR)pszFilePath);


pszFilePath[0] = '\0';
pszFileTitle[0] = '\0';
AppFileNew(hwnd, pszFilePath, pszFileTitle);
}
}
else
{
AppFileNew(hwnd, pszFilePath, pszFileTitle);
}


//
//
//
//
MciAppDeviceList(hwnd, FALSE);

return (TRUE);
} // MciAppInit()


//--------------------------------------------------------------------------;
//
// BOOL MciAppExit
//
// Description:
//
//
// Arguments:
//
//
// Return (BOOL):
//
//
// History:
// 2/15/93 created.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL MciAppExit
(
void
)
{
//
// clean up any resources and stuff we have allocated...
//

return (TRUE);
} // MciAppExit()