1818 lines
53 KiB
C
1818 lines
53 KiB
C
/*******************************Module*Header*********************************\
|
|
* Module Name: mcisys.c
|
|
*
|
|
* Media Control Architecture System Functions
|
|
*
|
|
* Created: 2/28/90
|
|
* Author: DLL (DavidLe)
|
|
*
|
|
* History:
|
|
*
|
|
* Copyright (c) 1990 Microsoft Corporation
|
|
*
|
|
\******************************************************************************/
|
|
|
|
#include <windows.h>
|
|
|
|
#define MMNOMIDI
|
|
#define MMNOWAVE
|
|
#define MMNOSOUND
|
|
#define MMNOTIMER
|
|
#define MMNOJOY
|
|
#define MMNOSEQ
|
|
#include "mmsystem.h"
|
|
#define NOMIDIDEV
|
|
#define NOWAVEDEV
|
|
#define NOTIMERDEV
|
|
#define NOJOYDEV
|
|
#define NOSEQDEV
|
|
#define NOTASKDEV
|
|
#include "mmddk.h"
|
|
#include "mmsysi.h"
|
|
#include "thunks.h"
|
|
|
|
#ifndef STATICFN
|
|
#define STATICFN
|
|
#endif
|
|
|
|
extern char far szOpen[]; // in MCI.C
|
|
|
|
static SZCODE szNull[] = "";
|
|
static SZCODE szMciExtensions[] = "mci extensions";
|
|
|
|
#define MCI_EXTENSIONS szMciExtensions
|
|
#define MCI_PROFILE_STRING_LENGTH 255
|
|
|
|
//!!#define TOLOWER(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : c)
|
|
|
|
// The device list is initialized on the first call to mciSendCommand or
|
|
// to mciSendString
|
|
BOOL MCI_bDeviceListInitialized;
|
|
|
|
// The next device ID to use for a new device
|
|
UINT MCI_wNextDeviceID = 1;
|
|
|
|
// The list of MCI devices. This list grows and shrinks as needed.
|
|
// The first offset MCI_lpDeviceList[0] is a placeholder and is unused
|
|
// because device 0 is defined as no device.
|
|
LPMCI_DEVICE_NODE FAR * MCI_lpDeviceList;
|
|
|
|
// The current size of the list of MCI devices
|
|
UINT MCI_wDeviceListSize;
|
|
|
|
// The internal mci heap used by mciAlloc and mciFree
|
|
HGLOBAL hMciHeap;
|
|
|
|
// File containing MCI device profile strings
|
|
extern char far szSystemIni[]; // in INIT.C
|
|
|
|
// Name of the section contining MCI device profile strings
|
|
static SZCODE szMCISectionName[] = "mci";
|
|
|
|
static SZCODE szAllDeviceName[] = "all";
|
|
|
|
static SZCODE szUnsignedFormat[] = "%u";
|
|
|
|
static void PASCAL NEAR mciFreeDevice(LPMCI_DEVICE_NODE nodeWorking);
|
|
|
|
BOOL NEAR PASCAL CouldBe16bitDrv(UINT wDeviceID)
|
|
{
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID) return TRUE;
|
|
|
|
if (MCI_VALID_DEVICE_ID(wDeviceID)) {
|
|
if (MCI_lpDeviceList[wDeviceID]->dwMCIFlags & MCINODE_16BIT_DRIVER) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL NEAR PASCAL Is16bitDrv(UINT wDeviceID)
|
|
{
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID) return FALSE;
|
|
|
|
if (MCI_VALID_DEVICE_ID(wDeviceID)) {
|
|
if (MCI_lpDeviceList[wDeviceID]->dwMCIFlags & MCINODE_16BIT_DRIVER) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Initialize device list
|
|
// Called once by mciSendString or mciSendCommand
|
|
// Returns TRUE on success
|
|
BOOL NEAR PASCAL mciInitDeviceList(void)
|
|
{
|
|
|
|
if ((hMciHeap = HeapCreate(0)) == 0)
|
|
{
|
|
DOUT("Mci heap create failed!\r\n");
|
|
return FALSE;
|
|
}
|
|
if ((MCI_lpDeviceList = mciAlloc (sizeof (LPMCI_DEVICE_NODE) *
|
|
(MCI_INIT_DEVICE_LIST_SIZE + 1))) != NULL)
|
|
{
|
|
MCI_wDeviceListSize = MCI_INIT_DEVICE_LIST_SIZE;
|
|
MCI_bDeviceListInitialized = TRUE;
|
|
return TRUE;
|
|
} else
|
|
{
|
|
DOUT ("MCIInit: could not allocate master MCI device list\r\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @doc EXTERNAL MCI
|
|
* @api UINT | mciGetDeviceIDFromElementID | This function
|
|
* retrieves the MCI device ID corresponding to and element ID
|
|
*
|
|
* @parm DWORD | dwElementID | The element ID
|
|
*
|
|
* @parm LPCSTR | lpstrType | The type name this element ID belongs to
|
|
*
|
|
* @rdesc Returns the device ID assigned when it was opened and used in the
|
|
* <f mciSendCommand> function. Returns zero if the device name was not known,
|
|
* if the device was not open, or if there was not enough memory to complete
|
|
* the operation or if lpstrType is NULL.
|
|
*
|
|
*/
|
|
UINT WINAPI mciGetDeviceIDFromElementID (
|
|
DWORD dwElementID,
|
|
LPCSTR lpstrType)
|
|
{
|
|
UINT wID;
|
|
LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
|
|
char strTemp[MCI_MAX_DEVICE_TYPE_LENGTH];
|
|
|
|
if (lpstrType == NULL)
|
|
return 0;
|
|
|
|
wID = (UINT)mciMessage( THUNK_MCI_GETDEVIDFROMELEMID, dwElementID,
|
|
(DWORD)lpstrType, 0L, 0L );
|
|
if ( wID == 0 ) {
|
|
|
|
nodeCounter = &MCI_lpDeviceList[1];
|
|
|
|
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
|
|
{
|
|
nodeWorking = *nodeCounter++;
|
|
|
|
if (nodeWorking == NULL)
|
|
continue;
|
|
|
|
if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID &&
|
|
nodeWorking->dwElementID == dwElementID)
|
|
|
|
if (LoadString (ghInst, nodeWorking->wDeviceType, strTemp,
|
|
sizeof(strTemp)) != 0
|
|
&& lstrcmpi ((LPSTR)strTemp, lpstrType) == 0) {
|
|
|
|
return (wID);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
return wID;
|
|
}
|
|
|
|
// Retrieves the device ID corresponding to the name of an opened device
|
|
// matching the given task
|
|
// This fn only looks for 16-bit devices
|
|
// See mciGetDeviceIDInternalEx that looks for all of them
|
|
UINT NEAR PASCAL mciGetDeviceIDInternal (
|
|
LPCSTR lpstrName,
|
|
HTASK hTask)
|
|
{
|
|
UINT wID;
|
|
LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
|
|
|
|
if (lstrcmpi (lpstrName, szAllDeviceName) == 0)
|
|
return MCI_ALL_DEVICE_ID;
|
|
|
|
if (MCI_lpDeviceList == NULL)
|
|
return 0;
|
|
|
|
// Loop through the MCI device list
|
|
nodeCounter = &MCI_lpDeviceList[1];
|
|
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
|
|
{
|
|
nodeWorking = *nodeCounter++;
|
|
|
|
if (nodeWorking == NULL)
|
|
continue;
|
|
|
|
// If this device does not have a name then skip it
|
|
if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID)
|
|
continue;
|
|
|
|
// If the names match
|
|
if (lstrcmpi(nodeWorking->lpstrName, lpstrName) == 0)
|
|
|
|
// If the device belongs to the indicated task
|
|
if (nodeWorking->hOpeningTask == hTask)
|
|
// Return this device ID
|
|
return wID;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @doc EXTERNAL MCI
|
|
* @api UINT | mciGetDeviceID | This function retrieves the device
|
|
* ID corresponding to the name of an open MCI device.
|
|
*
|
|
* @parm LPCSTR | lpstrName | Specifies the device name used to open the
|
|
* MCI device.
|
|
*
|
|
* @rdesc Returns the device ID assigned when the device was opened.
|
|
* Returns zero if the device name isn't known,
|
|
* if the device isn't open, or if there was insufficient memory to complete
|
|
* the operation. Each compound device element has a unique device ID.
|
|
* The ID of the "all" device is MCI_ALL_DEVICE_ID.
|
|
*
|
|
* @xref MCI_OPEN
|
|
*
|
|
*/
|
|
UINT WINAPI mciGetDeviceID (
|
|
LPCSTR lpstrName)
|
|
{
|
|
UINT wDevID;
|
|
|
|
/*
|
|
** Try the 32 bit side first
|
|
*/
|
|
wDevID = (UINT)mciMessage( THUNK_MCI_GETDEVICEID, (DWORD)lpstrName,
|
|
0L, 0L, 0L );
|
|
if ( wDevID == 0 ) {
|
|
|
|
/*
|
|
** The 32 bit call failed so let the 16 bit side have a go.
|
|
*/
|
|
wDevID = mciGetDeviceIDInternal (lpstrName, GetCurrentTask());
|
|
|
|
}
|
|
|
|
return wDevID;
|
|
}
|
|
|
|
//
|
|
// This function is same as mciGetDeviceID but it won't call GetCurrentTask
|
|
// Used when mci needs to verify the dev alias had not been allocated yet
|
|
//
|
|
//
|
|
|
|
UINT NEAR PASCAL mciGetDeviceIDInternalEx(
|
|
LPCSTR lpstrName,
|
|
HTASK hTask)
|
|
{
|
|
UINT uiDevID;
|
|
|
|
uiDevID = (UINT)mciMessage( THUNK_MCI_GETDEVICEID, (DWORD)lpstrName,
|
|
0L, 0L, 0L );
|
|
if (0 == uiDevID) {
|
|
|
|
uiDevID = mciGetDeviceIDInternal(lpstrName, hTask);
|
|
}
|
|
|
|
return uiDevID;
|
|
}
|
|
|
|
|
|
/*
|
|
* @doc EXTERNAL MCI
|
|
* @api HTASK | mciGetCreatorTask | This function retrieves the creator task
|
|
* corresponding with the device ID passed.
|
|
*
|
|
* @parm UINT | wDeviceID | Specifies the device ID whose creator task is to
|
|
* be returned.
|
|
*
|
|
* @rdesc Returns the creator task responsible for opening the device, else
|
|
* NULL if the device ID passed is invalid.
|
|
*
|
|
*/
|
|
HTASK WINAPI mciGetCreatorTask (
|
|
UINT wDeviceID)
|
|
{
|
|
/*
|
|
** Is this a 16 bit device ID
|
|
*/
|
|
if (Is16bitDrv(wDeviceID)) {
|
|
|
|
return MCI_lpDeviceList[wDeviceID]->hCreatorTask;
|
|
}
|
|
|
|
/*
|
|
** No, so pass it on to the 32 bit code.
|
|
*/
|
|
|
|
return (HTASK)mciMessage( THUNK_MCI_GETCREATORTASK, (DWORD)wDeviceID,
|
|
0L, 0L, 0L );
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func BOOL | mciDeviceMatch | Match the first string with the second.
|
|
* Any single trailing digit on the first string is ignored. Each string
|
|
* must have at least one character
|
|
*
|
|
* @parm LPCSTR | lpstrDeviceName | The device name, possibly
|
|
* with trailing digits but no blanks.
|
|
*
|
|
* @parm LPCSTR | lpstrDeviceType | The device type with no trailing digits
|
|
* or blanks
|
|
*
|
|
* @rdesc TRUE if the strings match the above test, FALSE otherwise
|
|
*
|
|
*/
|
|
STATICFN BOOL PASCAL NEAR
|
|
mciDeviceMatch(
|
|
LPCSTR lpstrDeviceName,
|
|
LPCSTR lpstrDeviceType
|
|
)
|
|
{
|
|
BOOL bAtLeastOne;
|
|
|
|
for (bAtLeastOne = FALSE;;)
|
|
if (!*lpstrDeviceType)
|
|
break;
|
|
else if (!*lpstrDeviceName || ((BYTE)(WORD)(DWORD)AnsiLower((LPSTR)(DWORD)(WORD)(*lpstrDeviceName++)) != (BYTE)(WORD)(DWORD)AnsiLower((LPSTR)(DWORD)(WORD)(*lpstrDeviceType++))))
|
|
return FALSE;
|
|
else
|
|
bAtLeastOne = TRUE;
|
|
if (!bAtLeastOne)
|
|
return FALSE;
|
|
for (; *lpstrDeviceName; lpstrDeviceName++)
|
|
if ((*lpstrDeviceName < '0') || (*lpstrDeviceName > '9'))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | mciLookUpType | Look up the type given a type name
|
|
*
|
|
* @parm LPCSTR | lpstrTypeName | The type name to look up. Trailing
|
|
* digits are ignored.
|
|
*
|
|
* @rdesc The MCI type number (MCI_DEVTYPE_<x>) or 0 if not found
|
|
*
|
|
!! * @comm Converts the input string to lower case as a side effect
|
|
*
|
|
*/
|
|
UINT PASCAL NEAR mciLookUpType (
|
|
LPCSTR lpstrTypeName)
|
|
{
|
|
UINT wType;
|
|
char strType[MCI_MAX_DEVICE_TYPE_LENGTH];
|
|
|
|
//!! mciToLower (lpstrTypeName);
|
|
|
|
for (wType = MCI_DEVTYPE_FIRST; wType <= MCI_DEVTYPE_LAST; ++wType)
|
|
{
|
|
if (LoadString (ghInst, wType, strType, sizeof(strType)) == 0)
|
|
{
|
|
DOUT ("mciLookUpType: could not load string for type\r\n");
|
|
continue;
|
|
}
|
|
|
|
if (mciDeviceMatch (lpstrTypeName, strType))
|
|
return wType;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func DWORD | mciSysinfo | Get system information about a device
|
|
*
|
|
* @parm UINT | wDeviceID | Device ID, may be 0
|
|
*
|
|
* @parm DWORD | dwFlags | SYSINFO flags
|
|
*
|
|
* @parm LPMCI_SYSINFO_PARMS | lpSysinfo | SYSINFO parameters
|
|
*
|
|
* @rdesc 0 if successful, otherwise error code
|
|
*
|
|
*/
|
|
DWORD PASCAL NEAR mciSysinfo (
|
|
UINT wDeviceID,
|
|
DWORD dwFlags,
|
|
LPMCI_SYSINFO_PARMS lpSysinfo)
|
|
{
|
|
UINT wCounted;
|
|
char strBuffer[MCI_PROFILE_STRING_LENGTH];
|
|
LPSTR lpstrBuffer = (LPSTR)strBuffer, lpstrStart;
|
|
|
|
if (dwFlags & MCI_SYSINFO_NAME && lpSysinfo->dwNumber == 0)
|
|
return MCIERR_OUTOFRANGE;
|
|
|
|
if (lpSysinfo->lpstrReturn == NULL || lpSysinfo->dwRetSize == 0)
|
|
return MCIERR_PARAM_OVERFLOW;
|
|
|
|
if (dwFlags & MCI_SYSINFO_NAME && dwFlags & MCI_SYSINFO_QUANTITY)
|
|
return MCIERR_FLAGS_NOT_COMPATIBLE;
|
|
|
|
if (dwFlags & MCI_SYSINFO_INSTALLNAME)
|
|
{
|
|
LPMCI_DEVICE_NODE nodeWorking;
|
|
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID)
|
|
return MCIERR_CANNOT_USE_ALL;
|
|
if (!MCI_VALID_DEVICE_ID(wDeviceID))
|
|
return MCIERR_INVALID_DEVICE_NAME;
|
|
|
|
nodeWorking = MCI_lpDeviceList[wDeviceID];
|
|
if ((DWORD)lstrlen (nodeWorking->lpstrInstallName) >= lpSysinfo->dwRetSize)
|
|
return MCIERR_PARAM_OVERFLOW;
|
|
lstrcpy (lpSysinfo->lpstrReturn, nodeWorking->lpstrInstallName);
|
|
return 0;
|
|
} else if (!(dwFlags & MCI_SYSINFO_OPEN))
|
|
{
|
|
if (wDeviceID != MCI_ALL_DEVICE_ID && lpSysinfo->wDeviceType == 0)
|
|
return MCIERR_DEVICE_TYPE_REQUIRED;
|
|
|
|
if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
|
|
return MCIERR_MISSING_PARAMETER;
|
|
GetPrivateProfileString (szMCISectionName, NULL, szNull,
|
|
lpstrBuffer, MCI_PROFILE_STRING_LENGTH,
|
|
szSystemIni);
|
|
wCounted = 0;
|
|
while (TRUE)
|
|
{
|
|
if (dwFlags & MCI_SYSINFO_QUANTITY)
|
|
{
|
|
if (*lpstrBuffer == '\0')
|
|
{
|
|
*(LPDWORD)lpSysinfo->lpstrReturn = (DWORD)wCounted;
|
|
return MCI_INTEGER_RETURNED;
|
|
}
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID ||
|
|
mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
|
|
++wCounted;
|
|
// Skip past the terminating '\0'
|
|
while (*lpstrBuffer != '\0')
|
|
*lpstrBuffer++;
|
|
lpstrBuffer++;
|
|
} else if (dwFlags & MCI_SYSINFO_NAME)
|
|
{
|
|
if (wCounted == (UINT)lpSysinfo->dwNumber)
|
|
{
|
|
lstrcpy (lpSysinfo->lpstrReturn, lpstrStart);
|
|
return 0L;
|
|
} else if (*lpstrBuffer == '\0')
|
|
return MCIERR_OUTOFRANGE;
|
|
else
|
|
{
|
|
lpstrStart = lpstrBuffer;
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID ||
|
|
mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
|
|
++wCounted;
|
|
// Skip past the terminating '\0'
|
|
while (*lpstrBuffer != '\0')
|
|
*lpstrBuffer++;
|
|
lpstrBuffer++;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
// Process MCI_SYSINFO_OPEN cases
|
|
{
|
|
UINT wID;
|
|
HTASK hCurrentTask = GetCurrentTask();
|
|
LPMCI_DEVICE_NODE Node;
|
|
|
|
if (wDeviceID != MCI_ALL_DEVICE_ID &&
|
|
lpSysinfo->wDeviceType == 0)
|
|
return MCIERR_DEVICE_TYPE_REQUIRED;
|
|
|
|
if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
|
|
return MCIERR_MISSING_PARAMETER;
|
|
|
|
wCounted = 0;
|
|
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
|
|
{
|
|
if ((Node = MCI_lpDeviceList[wID]) == 0)
|
|
continue;
|
|
|
|
if (wDeviceID == MCI_ALL_DEVICE_ID &&
|
|
Node->hOpeningTask == hCurrentTask)
|
|
++wCounted;
|
|
else
|
|
{
|
|
if (Node->wDeviceType == lpSysinfo->wDeviceType &&
|
|
Node->hOpeningTask == hCurrentTask)
|
|
++wCounted;
|
|
}
|
|
if (dwFlags & MCI_SYSINFO_NAME &&
|
|
wCounted == (UINT)lpSysinfo->dwNumber)
|
|
{
|
|
lstrcpy (lpSysinfo->lpstrReturn, Node->lpstrName);
|
|
return 0L;
|
|
}
|
|
}
|
|
if (dwFlags & MCI_SYSINFO_NAME)
|
|
{
|
|
if (lpSysinfo->lpstrReturn != NULL)
|
|
lpSysinfo->lpstrReturn = '\0';
|
|
return MCIERR_OUTOFRANGE;
|
|
|
|
} else if (dwFlags & MCI_SYSINFO_QUANTITY &&
|
|
lpSysinfo->lpstrReturn != NULL &&
|
|
lpSysinfo->dwRetSize >= 4)
|
|
|
|
*(LPDWORD)lpSysinfo->lpstrReturn = wCounted;
|
|
}
|
|
return MCI_INTEGER_RETURNED;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | wAddDeviceNodeToList | Add the given global handle into the
|
|
* MCI device table and return that entry's ID#
|
|
*
|
|
* @parm LPMCI_DEVICE_NODE | node | device description
|
|
*
|
|
* @rdesc The ID value for this device or 0 if there is no memory to expand
|
|
* the device list
|
|
*
|
|
*/
|
|
STATICFN UINT PASCAL NEAR
|
|
wAddDeviceNodeToList(
|
|
LPMCI_DEVICE_NODE node
|
|
)
|
|
{
|
|
UINT wDeviceID = node->wDeviceID;
|
|
LPMCI_DEVICE_NODE FAR *lpTempList;
|
|
UINT iReallocSize;
|
|
|
|
while (wDeviceID >= MCI_wDeviceListSize)
|
|
{
|
|
// The list is full so try to grow it
|
|
iReallocSize = MCI_wDeviceListSize + 1 + MCI_DEVICE_LIST_GROW_SIZE;
|
|
iReallocSize *= sizeof(LPMCI_DEVICE_NODE);
|
|
if ((lpTempList = mciReAlloc(MCI_lpDeviceList, iReallocSize)) == NULL)
|
|
{
|
|
DOUT ("wReserveDeviceID: cannot grow device list\r\n");
|
|
return 0;
|
|
}
|
|
MCI_lpDeviceList = lpTempList;
|
|
MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
|
|
}
|
|
|
|
if (wDeviceID >= MCI_wNextDeviceID) {
|
|
MCI_wNextDeviceID = wDeviceID + 1;
|
|
}
|
|
|
|
MCI_lpDeviceList[wDeviceID] = node;
|
|
|
|
return wDeviceID;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the given string and assign the name to the given
|
|
// device.
|
|
// Return FALSE if could not allocate memory
|
|
//
|
|
STATICFN BOOL PASCAL NEAR
|
|
mciAddDeviceName(
|
|
LPMCI_DEVICE_NODE nodeWorking,
|
|
LPCSTR lpDeviceName
|
|
)
|
|
{
|
|
nodeWorking->lpstrName = mciAlloc(lstrlen(lpDeviceName)+1);
|
|
|
|
if (nodeWorking->lpstrName == NULL)
|
|
{
|
|
DOUT ("mciAddDeviceName: Out of memory allocating device name\r\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// copy device name to mci node and lowercase it
|
|
|
|
lstrcpy(nodeWorking->lpstrName, lpDeviceName);
|
|
//!! mciToLower(nodeWorking->lpstrName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | mciAllocateNode | Allocate a new driver entry
|
|
*
|
|
* @parm DWORD | dwFlags | As sent with MCI_OPEN message
|
|
* @parm LPCSTR | lpDeviceName | The device name
|
|
* @parm LPMCI_DEVICE_NODE FAR * | lpnodeNew | new node allocated
|
|
*
|
|
* @rdesc The device ID to the new node. 0 on error.
|
|
*
|
|
*/
|
|
STATICFN UINT PASCAL NEAR mciAllocateNode(
|
|
DWORD dwFlags,
|
|
LPCSTR lpDeviceName,
|
|
LPMCI_DEVICE_NODE FAR *lpnodeNew
|
|
)
|
|
{
|
|
LPMCI_DEVICE_NODE nodeWorking;
|
|
UINT wDeviceID;
|
|
|
|
if ((nodeWorking = mciAlloc(sizeof(MCI_DEVICE_NODE))) == NULL)
|
|
{
|
|
DOUT("Out of memory in mciAllocateNode\r\n");
|
|
return 0;
|
|
}
|
|
|
|
// The device ID is a global resource so we fetch it from 32-bit MCI.
|
|
// A node is also allocated on the 32-bit side, and marked as 16-bit. The
|
|
// node will be freed during mciFreeDevice, and acts as a place holder for
|
|
// the device ID.
|
|
|
|
wDeviceID = (UINT) mciMessage(THUNK_MCI_ALLOCATE_NODE,
|
|
dwFlags,
|
|
(DWORD)lpDeviceName,
|
|
0L, 0L);
|
|
|
|
// Copy the working node to the device list
|
|
nodeWorking->wDeviceID = wDeviceID;
|
|
if (wAddDeviceNodeToList(nodeWorking) == 0)
|
|
{
|
|
DOUT ("mciAllocateNode: Cannot allocate new node\r\n");
|
|
mciFree(nodeWorking);
|
|
return 0;
|
|
}
|
|
|
|
// Initialize node
|
|
nodeWorking->hCreatorTask = GetCurrentTask ();
|
|
nodeWorking->dwMCIFlags |= MCINODE_16BIT_DRIVER;
|
|
|
|
if (dwFlags & MCI_OPEN_ELEMENT_ID) {
|
|
// No device name, just an element ID
|
|
nodeWorking->dwElementID = (DWORD)lpDeviceName;
|
|
}
|
|
else {
|
|
if (!mciAddDeviceName (nodeWorking, lpDeviceName))
|
|
{
|
|
mciFree (nodeWorking);
|
|
return 0;
|
|
}
|
|
}
|
|
*lpnodeNew = nodeWorking;
|
|
|
|
return nodeWorking->wDeviceID;
|
|
}
|
|
|
|
//
|
|
// Reparse the original command parameters
|
|
// Returns MCIERR code. If the reparse fails the original error code
|
|
// from the first parsing is returned.
|
|
//
|
|
STATICFN UINT PASCAL NEAR
|
|
mciReparseOpen(
|
|
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
|
|
UINT wCustomTable,
|
|
UINT wTypeTable,
|
|
LPDWORD lpdwFlags,
|
|
LPMCI_OPEN_PARMS FAR *lplpOpen,
|
|
UINT wDeviceID
|
|
)
|
|
{
|
|
LPSTR lpCommand;
|
|
LPDWORD lpdwParams;
|
|
UINT wErr;
|
|
DWORD dwOldFlags = *lpdwFlags;
|
|
|
|
// If the custom table contains no open command
|
|
if (wCustomTable == -1 ||
|
|
(lpCommand = FindCommandInTable (wCustomTable, szOpen, NULL)) == NULL)
|
|
{
|
|
// Try the type specific table
|
|
lpCommand = FindCommandInTable (wTypeTable, szOpen, NULL);
|
|
// If it still cannot be parsed
|
|
if (lpCommand == NULL)
|
|
return lpOpenInfo->wParsingError;
|
|
wCustomTable = wTypeTable;
|
|
}
|
|
// A new version of 'open' was found
|
|
// Free previous set of parameters
|
|
mciParserFree (lpOpenInfo->lpstrPointerList);
|
|
*lpdwFlags = 0;
|
|
|
|
if ((lpdwParams =
|
|
(LPDWORD)mciAlloc (sizeof(DWORD) * MCI_MAX_PARAM_SLOTS))
|
|
== NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
|
|
wErr = mciParseParams (lpOpenInfo->lpstrParams, lpCommand,
|
|
lpdwFlags,
|
|
(LPSTR)lpdwParams,
|
|
sizeof(DWORD) * MCI_MAX_PARAM_SLOTS,
|
|
&lpOpenInfo->lpstrPointerList, NULL);
|
|
// We don't need this around anymore
|
|
mciUnlockCommandTable (wCustomTable);
|
|
|
|
// If there was a parsing error
|
|
if (wErr != 0)
|
|
{
|
|
// Make sure this does not get free'd by mciSendString
|
|
lpOpenInfo->lpstrPointerList = NULL;
|
|
|
|
mciFree (lpdwParams);
|
|
return wErr;
|
|
}
|
|
if (dwOldFlags & MCI_OPEN_TYPE)
|
|
{
|
|
// Device type was already extracted so add it manually
|
|
((LPMCI_OPEN_PARMS)lpdwParams)->lpstrDeviceType
|
|
= (*lplpOpen)->lpstrDeviceType;
|
|
*lpdwFlags |= MCI_OPEN_TYPE;
|
|
}
|
|
if (dwOldFlags & MCI_OPEN_ELEMENT)
|
|
{
|
|
// Element name was already extracted so add it manually
|
|
((LPMCI_OPEN_PARMS)lpdwParams)->lpstrElementName
|
|
= (*lplpOpen)->lpstrElementName;
|
|
*lpdwFlags |= MCI_OPEN_ELEMENT;
|
|
}
|
|
if (dwOldFlags & MCI_OPEN_ALIAS)
|
|
{
|
|
// Alias name was already extracted so add it manually
|
|
((LPMCI_OPEN_PARMS)lpdwParams)->lpstrAlias
|
|
= (*lplpOpen)->lpstrAlias;
|
|
*lpdwFlags |= MCI_OPEN_ALIAS;
|
|
}
|
|
if (dwOldFlags & MCI_NOTIFY)
|
|
// Notify was already extracted so add it manually
|
|
((LPMCI_OPEN_PARMS)lpdwParams)->dwCallback
|
|
= (*lplpOpen)->dwCallback;
|
|
|
|
// Replace old parameter list with new list
|
|
*lplpOpen = (LPMCI_OPEN_PARMS)lpdwParams;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// See if lpstrDriverName exists in the profile strings of the [mci]
|
|
// section and return the keyname in lpstrDevice and the
|
|
// profile string in lpstrProfString
|
|
// Returns 0 on success or an error code
|
|
STATICFN UINT PASCAL NEAR
|
|
mciFindDriverName(
|
|
LPCSTR lpstrDriverName,
|
|
LPSTR lpstrDevice,
|
|
LPSTR lpstrProfString,
|
|
UINT wProfLength
|
|
)
|
|
{
|
|
LPSTR lpstrEnum, lpstrEnumStart;
|
|
UINT wEnumLen = 100;
|
|
UINT wErr;
|
|
LPSTR lpstrDriverTemp, lpstrProfTemp;
|
|
|
|
// Enumerate values, trying until they fit into the buffer
|
|
while (TRUE) {
|
|
if ((lpstrEnum = mciAlloc (wEnumLen)) == NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
|
|
wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
|
|
NULL, szNull, lpstrEnum, wEnumLen,
|
|
szSystemIni);
|
|
|
|
if (*lpstrEnum == '\0')
|
|
{
|
|
mciFree (lpstrEnum);
|
|
return MCIERR_DEVICE_NOT_INSTALLED;
|
|
}
|
|
|
|
if (wErr == wEnumLen - 2)
|
|
{
|
|
wEnumLen *= 2;
|
|
mciFree (lpstrEnum);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
lpstrEnumStart = lpstrEnum;
|
|
if (lstrlen(lpstrDriverName) >= MCI_MAX_DEVICE_TYPE_LENGTH) {
|
|
wErr = MCIERR_DEVICE_LENGTH;
|
|
goto exit_fn;
|
|
}
|
|
lstrcpy(lpstrDevice, lpstrDriverName);
|
|
//!! mciToLower (lpstrDevice);
|
|
|
|
// Walk through each string
|
|
while (TRUE) {
|
|
wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
|
|
lpstrEnum, szNull, lpstrProfString,
|
|
wProfLength,
|
|
szSystemIni);
|
|
if (*lpstrProfString == '\0')
|
|
{
|
|
DOUT ("mciFindDriverName: cannot load valid keyname\r\n");
|
|
wErr = MCIERR_CANNOT_LOAD_DRIVER;
|
|
goto exit_fn;
|
|
}
|
|
// See if driver pathname matches input
|
|
//!! mciToLower (lpstrProfString);
|
|
lpstrDriverTemp = lpstrDevice;
|
|
lpstrProfTemp = lpstrProfString;
|
|
// Find end of file name
|
|
while (*lpstrProfTemp != '\0' && *lpstrProfTemp != ' ')
|
|
++lpstrProfTemp;
|
|
// Find begining of simple file name
|
|
--lpstrProfTemp;
|
|
while (*lpstrProfTemp != '\\' && *lpstrProfTemp != '/' &&
|
|
*lpstrProfTemp != ':')
|
|
if (--lpstrProfTemp < lpstrProfString)
|
|
break;
|
|
++lpstrProfTemp;
|
|
// Compare to input
|
|
while (*lpstrDriverTemp != '\0')
|
|
if (*lpstrDriverTemp++ != *lpstrProfTemp++ ||
|
|
(UINT)(lpstrProfTemp - lpstrProfString) >= wProfLength)
|
|
{
|
|
--lpstrProfTemp;
|
|
break;
|
|
}
|
|
// If the input was contained in the profile string and followed by
|
|
// a space or a '.' the we've got it!
|
|
if (*lpstrDriverTemp == '\0' &&
|
|
(*lpstrProfTemp == ' ' || *lpstrProfTemp == '.'))
|
|
{
|
|
if (lstrlen (lpstrEnum) >= MCI_MAX_DEVICE_TYPE_LENGTH)
|
|
{
|
|
DOUT ("mciFindDriverName: device name too long\r\n");
|
|
wErr = MCIERR_DEVICE_LENGTH;
|
|
goto exit_fn;
|
|
}
|
|
lstrcpy (lpstrDevice, lpstrEnum);
|
|
wErr = 0;
|
|
goto exit_fn;
|
|
}
|
|
// Skip to next keyname
|
|
while (*lpstrEnum++ != '\0') {}
|
|
// Error if no more left
|
|
if (*lpstrEnum == 0)
|
|
{
|
|
wErr = MCIERR_INVALID_DEVICE_NAME;
|
|
goto exit_fn;
|
|
}
|
|
}
|
|
|
|
exit_fn:
|
|
mciFree (lpstrEnumStart);
|
|
return wErr;
|
|
}
|
|
|
|
//
|
|
// Identifies the driver name to load
|
|
// Loads the driver
|
|
// Reparses open command if necessary
|
|
// Sets a default break key
|
|
//
|
|
// lpOpenInfo contains various info for reparsing
|
|
//
|
|
// bDefaultAlias indicates that the alias need not be verified because
|
|
// it was internally assigned
|
|
//
|
|
STATICFN UINT PASCAL NEAR
|
|
mciLoadDevice(
|
|
DWORD dwFlags,
|
|
LPMCI_OPEN_PARMS lpOpen,
|
|
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
|
|
BOOL bDefaultAlias
|
|
)
|
|
{
|
|
LPMCI_DEVICE_NODE nodeWorking;
|
|
HINSTANCE hDriver;
|
|
UINT wID, wErr;
|
|
char strProfileString[MCI_PROFILE_STRING_LENGTH];
|
|
MCI_OPEN_DRIVER_PARMS DriverOpen;
|
|
HDRVR hDrvDriver;
|
|
LPSTR lpstrParams;
|
|
LPCSTR lpstrInstallName, lpstrDeviceName;
|
|
LPSTR lpstrCopy = NULL;
|
|
LPMCI_OPEN_PARMS lpOriginalOpenParms = lpOpen;
|
|
|
|
/* Check for the device name in SYSTEM.INI */
|
|
lpstrInstallName = lpOpen->lpstrDeviceType;
|
|
wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
|
|
lpstrInstallName,
|
|
szNull, (LPSTR)strProfileString,
|
|
MCI_PROFILE_STRING_LENGTH,
|
|
szSystemIni);
|
|
|
|
// If device name not found
|
|
if (wErr == 0)
|
|
{
|
|
int nLen = lstrlen (lpstrInstallName);
|
|
int index;
|
|
|
|
// Try for the device name with a '1' thru a '9' appended to it
|
|
|
|
if ((lpstrCopy = (LPSTR)mciAlloc (nLen + 2)) // space for digit too
|
|
== NULL)
|
|
{
|
|
DOUT ("mciLoadDevice: cannot allocate device name copy\r\n");
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
}
|
|
lstrcpy (lpstrCopy, lpstrInstallName);
|
|
|
|
lpstrCopy[nLen + 1] = '\0';
|
|
|
|
for (index = 1; index <= 9; ++index)
|
|
{
|
|
lpstrCopy[nLen] = (char)('0' + index);
|
|
wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
|
|
lpstrCopy,
|
|
szNull, (LPSTR)strProfileString,
|
|
MCI_PROFILE_STRING_LENGTH,
|
|
szSystemIni);
|
|
if (wErr != 0)
|
|
break;
|
|
}
|
|
if (wErr == 0)
|
|
{
|
|
mciFree (lpstrCopy);
|
|
if ((lpstrCopy = (LPSTR)mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH))
|
|
== NULL)
|
|
{
|
|
DOUT ("mciLoadDevice: cannot allocate device name copy\r\n");
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
}
|
|
if ((wErr = mciFindDriverName (lpstrInstallName, lpstrCopy,
|
|
(LPSTR)strProfileString,
|
|
MCI_PROFILE_STRING_LENGTH)) != 0)
|
|
goto exit_fn;
|
|
}
|
|
lpstrInstallName = lpstrCopy;
|
|
}
|
|
|
|
// Break out the device driver pathname and the parameter list
|
|
|
|
lpstrParams = strProfileString;
|
|
|
|
// Eat blanks
|
|
while (*lpstrParams != ' ' && *lpstrParams != '\0')
|
|
++lpstrParams;
|
|
|
|
// Terminate driver file name
|
|
if (*lpstrParams == ' ') *lpstrParams++ = '\0';
|
|
|
|
//Now "strProfileString" is the device driver and "lpstrParams" is
|
|
//the parameter string
|
|
if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
|
|
lpstrDeviceName = lpOpen->lpstrElementName;
|
|
else
|
|
lpstrDeviceName = lpOpen->lpstrDeviceType;
|
|
|
|
if (dwFlags & MCI_OPEN_ALIAS)
|
|
{
|
|
// If the alias is default then we've already checked its uniqueness
|
|
if (!bDefaultAlias &&
|
|
mciGetDeviceIDInternalEx (lpOpen->lpstrAlias,
|
|
lpOpenInfo->hCallingTask) != 0)
|
|
{
|
|
wErr = MCIERR_DUPLICATE_ALIAS;
|
|
goto exit_fn;
|
|
}
|
|
lpstrDeviceName = lpOpen->lpstrAlias;
|
|
}
|
|
|
|
wID = mciAllocateNode (dwFlags, lpstrDeviceName, &nodeWorking);
|
|
|
|
if (wID == 0)
|
|
{
|
|
wErr = MCIERR_CANNOT_LOAD_DRIVER;
|
|
goto exit_fn;
|
|
}
|
|
|
|
// Identify the task which initiated the open command
|
|
nodeWorking->hOpeningTask = lpOpenInfo->hCallingTask;
|
|
|
|
// Initialize the driver
|
|
DriverOpen.lpstrParams = lpstrParams;
|
|
DriverOpen.wCustomCommandTable = (UINT)-1;
|
|
DriverOpen.wType = 0;
|
|
DriverOpen.wDeviceID = wID;
|
|
|
|
// Load the driver
|
|
hDrvDriver = OpenDriver ((LPSTR)strProfileString, szMCISectionName,
|
|
(LPARAM)(DWORD)(LPMCI_OPEN_DRIVER_PARMS)&DriverOpen);
|
|
if (hDrvDriver == NULL)
|
|
{
|
|
DOUT ("mciLoadDevice: OpenDriver failed\r\n");
|
|
// Assume driver has free'd any custom command table when it failed the open
|
|
mciFreeDevice (nodeWorking);
|
|
wErr = MCIERR_CANNOT_LOAD_DRIVER;
|
|
goto exit_fn;
|
|
}
|
|
|
|
lpOpen->wDeviceID = wID;
|
|
lpOpen->wReserved0 = 0;
|
|
|
|
hDriver = GetDriverModuleHandle (hDrvDriver);
|
|
|
|
nodeWorking->hDrvDriver = hDrvDriver;
|
|
nodeWorking->hDriver = hDriver;
|
|
|
|
// Driver provides custom device table and type
|
|
nodeWorking->wCustomCommandTable = DriverOpen.wCustomCommandTable;
|
|
nodeWorking->wDeviceType = DriverOpen.wType;
|
|
|
|
// Load driver's type table
|
|
if ((nodeWorking->wCommandTable = mciLoadTableType (DriverOpen.wType))
|
|
== -1)
|
|
// Load from a file if necessary
|
|
nodeWorking->wCommandTable =
|
|
mciLoadCommandResource (ghInst, lpOpen->lpstrDeviceType,
|
|
DriverOpen.wType);
|
|
|
|
// Record this for 'sysinfo installname'
|
|
if ((nodeWorking->lpstrInstallName =
|
|
mciAlloc (lstrlen (lpstrInstallName) + 1))
|
|
== NULL)
|
|
{
|
|
mciCloseDevice (wID, 0L, NULL, FALSE);
|
|
wErr = MCIERR_OUT_OF_MEMORY;
|
|
goto exit_fn;
|
|
} else
|
|
lstrcpy (nodeWorking->lpstrInstallName, lpstrInstallName);
|
|
|
|
// Reparse the input command if no type was known the first time or if
|
|
// there was a custom command table
|
|
// and there were any open command parameters
|
|
if (lpOpenInfo->lpstrParams != NULL)
|
|
{
|
|
if ((wErr = mciReparseOpen (lpOpenInfo,
|
|
nodeWorking->wCustomCommandTable,
|
|
nodeWorking->wCommandTable,
|
|
&dwFlags, &lpOpen, wID)) != 0)
|
|
{
|
|
mciCloseDevice (wID, 0L, NULL, FALSE);
|
|
goto exit_fn;
|
|
}
|
|
// If there is no custom command table but mciSendString had a parsing
|
|
// error then close the device and report the error now
|
|
} else if (lpOpenInfo->wParsingError != 0)
|
|
{
|
|
mciCloseDevice (wID, 0L, NULL, FALSE);
|
|
wErr = lpOpenInfo->wParsingError;
|
|
goto exit_fn;
|
|
}
|
|
|
|
/* Send MCI_OPEN_DRIVER command to device */
|
|
wErr = LOWORD(mciSendCommand (wID, MCI_OPEN_DRIVER,
|
|
dwFlags, (DWORD)lpOpen));
|
|
|
|
// If the OPEN failed then close the device (don't send a CLOSE though)
|
|
if (wErr != 0)
|
|
mciCloseDevice (wID, 0L, NULL, FALSE);
|
|
else
|
|
// Set default break key
|
|
mciSetBreakKey (nodeWorking->wDeviceID, VK_CANCEL, NULL);
|
|
|
|
// If we replaced the open parms here then free them
|
|
if (lpOriginalOpenParms != lpOpen && lpOpen != NULL)
|
|
mciFree (lpOpen);
|
|
|
|
exit_fn:
|
|
if (lpstrCopy != NULL)
|
|
mciFree (lpstrCopy);
|
|
|
|
return wErr;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func BOOL | mciExtractDeviceType | If the given device name ends with
|
|
* a file extension (.???) then try to get a typename from the
|
|
* [mci extensions] section of WIN.INI
|
|
*
|
|
* @parm LPCSTR | lpstrDeviceName | The name to get the type from
|
|
*
|
|
* @parm LPSTR | lpstrDeviceType | The device type, returned to caller.
|
|
*
|
|
* @parm UINT | wBufLen | The length of the output buffer
|
|
*
|
|
* @rdesc TRUE if the type was found, FALSE otherwise
|
|
*
|
|
*/
|
|
BOOL PASCAL NEAR mciExtractDeviceType (
|
|
LPCSTR lpstrDeviceName,
|
|
LPSTR lpstrDeviceType,
|
|
UINT wBufLen)
|
|
{
|
|
LPCSTR lpstrExt = lpstrDeviceName;
|
|
int i;
|
|
|
|
// Goto end of string
|
|
while (*lpstrExt != '\0')
|
|
{
|
|
// '!' case is handled elsewhere
|
|
if (*lpstrExt == '!')
|
|
return FALSE;
|
|
++lpstrExt;
|
|
}
|
|
|
|
// Must be at least 2 characters in string
|
|
if (lpstrExt - lpstrDeviceName < 2)
|
|
return FALSE;
|
|
|
|
lpstrExt -= 1;
|
|
|
|
// Does not count if last character is '.'
|
|
if (*lpstrExt == '.')
|
|
return FALSE;
|
|
|
|
lpstrExt -= 1;
|
|
// Now looking at second to the last character. Check this and the two
|
|
// previous characters for a '.'
|
|
|
|
for (i=1; i<=3; ++i)
|
|
{
|
|
// Cannot have path separator here
|
|
if (*lpstrExt == '/' || *lpstrExt == '\\')
|
|
return FALSE;
|
|
|
|
if (*lpstrExt == '.')
|
|
{
|
|
++lpstrExt;
|
|
if (GetProfileString (MCI_EXTENSIONS, lpstrExt, szNull,
|
|
lpstrDeviceType, wBufLen) != 0)
|
|
return TRUE;
|
|
}
|
|
if (lpstrExt == lpstrDeviceName)
|
|
return FALSE;
|
|
--lpstrExt;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Copy characters up to cSeparater into output which is allocated
|
|
// by this function using mciAlloc. Return the input pointer pointing
|
|
// to the character after cSeparator
|
|
// unless the separator is '\0' in which case it points to the end.
|
|
//
|
|
// Return the allocated pointer
|
|
//
|
|
// If bMustFind then the output string is created only if the token
|
|
// is found and is otherwise NULL. Else the output string is always created.
|
|
//
|
|
// cSeparator is ignored inside matching quotes ("abd"), the quotes
|
|
// are not coppied and doubled
|
|
// quotes inside are compressed to one. There must be a terminating quote.
|
|
// Quotes are treated normally unless the first character is a quote
|
|
//
|
|
// Function return value is 0 or an MCIERR code. A missing separator does
|
|
// not cause an error return.
|
|
UINT PASCAL NEAR mciEatToken (LPCSTR FAR *lplpstrInput, char cSeparater,
|
|
LPSTR FAR *lplpstrOutput, BOOL bMustFind)
|
|
{
|
|
LPCSTR lpstrEnd = *lplpstrInput, lpstrCounter;
|
|
LPSTR lpstrOutput;
|
|
UINT wLen;
|
|
BOOL bInQuotes = FALSE, bParseQuotes = TRUE, bQuoted = FALSE;
|
|
|
|
// Clear output
|
|
*lplpstrOutput = NULL;
|
|
|
|
// Scan for token or end of string
|
|
while ((*lpstrEnd != cSeparater || bInQuotes) && *lpstrEnd != '\0')
|
|
{
|
|
// If quote
|
|
if (*lpstrEnd == '"' && bParseQuotes)
|
|
{
|
|
// If inside quotes
|
|
if (bInQuotes)
|
|
{
|
|
// If next character is a quote also
|
|
if (*(lpstrEnd + 1) == '"')
|
|
// Skip it
|
|
++lpstrEnd;
|
|
else
|
|
bInQuotes = FALSE;
|
|
} else
|
|
{
|
|
bInQuotes = TRUE;
|
|
bQuoted = TRUE;
|
|
}
|
|
} else if (!bInQuotes)
|
|
{
|
|
if (bQuoted)
|
|
return MCIERR_EXTRA_CHARACTERS;
|
|
// A non-quote was read first so treat any quotes as normal characters
|
|
bParseQuotes = FALSE;
|
|
}
|
|
++lpstrEnd;
|
|
}
|
|
|
|
if (bInQuotes)
|
|
return MCIERR_NO_CLOSING_QUOTE;
|
|
|
|
// Fail if the token was not found and bMustFind is TRUE
|
|
if (*lpstrEnd != cSeparater && bMustFind)
|
|
return 0;
|
|
|
|
// Length of new string (INCLUDES QUOTES NOT COPIED)
|
|
wLen = lpstrEnd - *lplpstrInput + 1;
|
|
|
|
if ((*lplpstrOutput = mciAlloc (wLen)) == NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
|
|
// Copy into allocated space
|
|
lpstrCounter = *lplpstrInput;
|
|
lpstrOutput = *lplpstrOutput;
|
|
bInQuotes = FALSE;
|
|
|
|
while (lpstrCounter != lpstrEnd)
|
|
{
|
|
if (*lpstrCounter == '"' && bParseQuotes)
|
|
{
|
|
if (bInQuotes)
|
|
{
|
|
// If this is a doubled quote
|
|
if (*(lpstrCounter + 1) == '"')
|
|
// Copy it
|
|
*lpstrOutput++ = *lpstrCounter++;
|
|
else
|
|
bInQuotes = FALSE;
|
|
} else
|
|
bInQuotes = TRUE;
|
|
// Skip the quote
|
|
++lpstrCounter;
|
|
} else
|
|
*lpstrOutput++ = *lpstrCounter++;
|
|
}
|
|
|
|
*lpstrOutput = '\0';
|
|
if (*lpstrEnd == '\0')
|
|
*lplpstrInput = lpstrEnd;
|
|
else
|
|
*lplpstrInput = lpstrEnd + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Take the type number from the open parameters and return
|
|
// it as a string in lplpstrType which must be free'd with mciFree
|
|
// Returns 0 or an MCI error code
|
|
UINT PASCAL NEAR mciExtractTypeFromID (
|
|
LPMCI_OPEN_PARMS lpOpen)
|
|
{
|
|
int nSize;
|
|
LPSTR lpstrType;
|
|
|
|
if ((lpstrType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH)) == NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
|
|
// Load the type string corresponding to the ID
|
|
if ((nSize = LoadString (ghInst,
|
|
LOWORD ((DWORD)lpOpen->lpstrDeviceType),
|
|
lpstrType, MCI_MAX_DEVICE_TYPE_LENGTH)) == 0)
|
|
return MCIERR_EXTENSION_NOT_FOUND;
|
|
|
|
// Add ordinal (if any) onto the end of the device type name
|
|
if (HIWORD (lpOpen->lpstrDeviceType) != 0)
|
|
{
|
|
if (nSize > MCI_MAX_DEVICE_TYPE_LENGTH - 11)
|
|
{
|
|
DOUT ("mciExtractTypeFromID: type + ordinal too long\r\n");
|
|
return MCIERR_DEVICE_ORD_LENGTH;
|
|
}
|
|
|
|
wsprintf (lpstrType + nSize, szUnsignedFormat,
|
|
HIWORD ((DWORD)lpOpen->lpstrDeviceType));
|
|
}
|
|
lpOpen->lpstrDeviceType = lpstrType;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | mciOpenDevice | Open an MCI device for access.
|
|
* Used in processing the MCI_OPEN message.
|
|
*
|
|
* @parm DWORD | dwFlags | Open Flags
|
|
* @parm LPMCI_OPEN_PARMS | lpOpen | Description of device
|
|
*
|
|
* @rdesc 0 if successful or an error code
|
|
* @flag MCIERR_INVALID_DEVICE_NAME | Name not known
|
|
* @flag MCIERR_DEVICE_OPEN | Device is already open and is not sharable
|
|
*
|
|
* @comm This function does the following:
|
|
* 1) Check to see if device is already open. If so, return an error
|
|
*
|
|
* 2) Locate the device name in the SYSTEM.INI file and load
|
|
* the corresponding device driver DLL
|
|
*
|
|
* 3) Allocate and initialize a new device description block
|
|
*
|
|
*/
|
|
UINT NEAR PASCAL mciOpenDevice (
|
|
DWORD dwStartingFlags,
|
|
LPMCI_OPEN_PARMS lpOpen,
|
|
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
|
|
{
|
|
LPSTR lpstrNewType = NULL;
|
|
UINT wID, wReturn;
|
|
LPCSTR lpstrDeviceName;
|
|
LPSTR lpstrNewElement = NULL;
|
|
BOOL bFromTypeID = FALSE;
|
|
LPCSTR lpstrOriginalType;
|
|
LPCSTR lpstrOriginalElement;
|
|
LPCSTR lpstrOriginalAlias;
|
|
DWORD dwFlags = dwStartingFlags;
|
|
BOOL bDefaultAlias = FALSE;
|
|
|
|
// Initialize
|
|
if (lpOpen == NULL)
|
|
return MCIERR_NULL_PARAMETER_BLOCK;
|
|
lpstrOriginalType = lpOpen->lpstrDeviceType;
|
|
lpstrOriginalElement = lpOpen->lpstrElementName;
|
|
lpstrOriginalAlias = lpOpen->lpstrAlias;
|
|
|
|
// The type number is given explicitly, convert it to a type name
|
|
if (dwFlags & MCI_OPEN_TYPE_ID)
|
|
if ((wReturn = mciExtractTypeFromID (lpOpen)) != 0)
|
|
return wReturn;
|
|
else
|
|
bFromTypeID = TRUE;
|
|
|
|
// The device name is the device type of a simple device or the device
|
|
// element of a compound device
|
|
|
|
if (dwFlags & MCI_OPEN_ELEMENT)
|
|
lpstrDeviceName = lpstrOriginalElement;
|
|
else if (dwFlags & MCI_OPEN_TYPE)
|
|
lpstrDeviceName = lpOpen->lpstrDeviceType;
|
|
else
|
|
return MCIERR_MISSING_PARAMETER;
|
|
|
|
if (lpstrDeviceName == NULL)
|
|
{
|
|
DOUT ("mciOpenDevice: Device name is NULL\r\n");
|
|
return MCIERR_INVALID_DEVICE_NAME;
|
|
}
|
|
|
|
// Is the device already open?
|
|
if (dwFlags & MCI_OPEN_ELEMENT_ID)
|
|
wID = mciGetDeviceIDFromElementID ((DWORD)lpstrDeviceName,
|
|
lpOpen->lpstrDeviceType);
|
|
else
|
|
wID = mciGetDeviceIDInternalEx ((dwFlags & MCI_OPEN_ALIAS ?
|
|
lpOpen->lpstrAlias : lpstrDeviceName),
|
|
lpOpenInfo->hCallingTask);
|
|
|
|
// If the device is open already then return an error
|
|
if (wID != 0)
|
|
return dwFlags & MCI_OPEN_ALIAS ? MCIERR_DUPLICATE_ALIAS :
|
|
MCIERR_DEVICE_OPEN;
|
|
|
|
// The device is not already open in that task by the name
|
|
|
|
// If the type was derived then skip all this crap
|
|
if (bFromTypeID)
|
|
goto load_device;
|
|
|
|
// If an element name is given but no type name (only via mciSendCommand)
|
|
if (dwFlags & MCI_OPEN_ELEMENT && !(dwFlags & MCI_OPEN_TYPE))
|
|
{
|
|
lpstrNewType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH);
|
|
if (lpstrNewType == NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
|
|
// Try to get the device type from the element name via a file extension
|
|
if (mciExtractDeviceType (lpstrOriginalElement,
|
|
lpstrNewType, MCI_MAX_DEVICE_TYPE_LENGTH))
|
|
{
|
|
lpOpen->lpstrDeviceType = lpstrNewType;
|
|
dwFlags |= MCI_OPEN_TYPE;
|
|
} else
|
|
{
|
|
mciFree (lpstrNewType);
|
|
return MCIERR_EXTENSION_NOT_FOUND;
|
|
}
|
|
} else if (dwFlags & MCI_OPEN_TYPE && !(dwFlags & MCI_OPEN_ELEMENT))
|
|
// A type name is given but no element
|
|
{
|
|
// Try to extract a device type from the given device name via a file extension
|
|
lpstrNewType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH);
|
|
if (lpstrNewType == NULL)
|
|
return MCIERR_OUT_OF_MEMORY;
|
|
if (mciExtractDeviceType (lpOpen->lpstrDeviceType, lpstrNewType,
|
|
MCI_MAX_DEVICE_TYPE_LENGTH))
|
|
{
|
|
// Fix up the type and element names
|
|
dwFlags |= MCI_OPEN_ELEMENT;
|
|
lpOpen->lpstrElementName = lpOpen->lpstrDeviceType;
|
|
lpOpen->lpstrDeviceType = lpstrNewType;
|
|
} else
|
|
// Failed to extract type so...
|
|
// Try to get a compound element name ('!' separator)
|
|
{
|
|
LPCSTR lpstrTemp = lpOpen->lpstrDeviceType;
|
|
|
|
mciFree (lpstrNewType);
|
|
lpstrNewType = NULL;
|
|
|
|
if ((wReturn = mciEatToken (&lpstrTemp, '!', &lpstrNewType, TRUE))
|
|
!= 0)
|
|
goto cleanup;
|
|
else if (lpstrNewType != NULL)
|
|
{
|
|
if ((wReturn = mciEatToken (&lpstrTemp, '\0',
|
|
&lpstrNewElement, TRUE))
|
|
!= 0)
|
|
goto cleanup;
|
|
else if (lpstrNewElement != NULL &&
|
|
*lpstrNewElement != '\0')
|
|
{
|
|
// See if this element name is in use
|
|
if (!(dwFlags & MCI_OPEN_ALIAS))
|
|
if (mciGetDeviceIDInternalEx (lpstrNewElement,
|
|
lpOpenInfo->hCallingTask))
|
|
{
|
|
wReturn = MCIERR_DEVICE_OPEN;
|
|
goto cleanup;
|
|
}
|
|
// Swap type and element for new ones
|
|
lpOpen->lpstrElementName = lpstrNewElement;
|
|
lpOpen->lpstrDeviceType = lpstrNewType;
|
|
dwFlags |= MCI_OPEN_ELEMENT;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
lpstrNewType = NULL;
|
|
|
|
// Tack on a default alias if none is given
|
|
if (! (dwFlags & MCI_OPEN_ALIAS))
|
|
{
|
|
LPCSTR lpstrAlias;
|
|
|
|
// If an element name exists then the alias is the element name
|
|
if (dwFlags & MCI_OPEN_ELEMENT)
|
|
{
|
|
// If a device ID was specified then there is no alias
|
|
if (dwFlags & MCI_OPEN_ELEMENT_ID)
|
|
lpstrAlias = NULL;
|
|
else
|
|
lpstrAlias = lpOpen->lpstrElementName;
|
|
// Otherwise the alias is the device type
|
|
} else
|
|
lpstrAlias = lpOpen->lpstrDeviceType;
|
|
|
|
if (lpstrAlias != NULL)
|
|
{
|
|
lpOpen->lpstrAlias = lpstrAlias;
|
|
dwFlags |= MCI_OPEN_ALIAS;
|
|
bDefaultAlias = TRUE;
|
|
}
|
|
}
|
|
|
|
load_device:;
|
|
wReturn = mciLoadDevice (dwFlags, lpOpen, lpOpenInfo, bDefaultAlias);
|
|
|
|
cleanup:
|
|
if (lpstrNewElement != NULL)
|
|
mciFree (lpstrNewElement);
|
|
if (lpstrNewType != NULL)
|
|
mciFree (lpstrNewType);
|
|
if (bFromTypeID)
|
|
mciFree ((LPSTR)lpOpen->lpstrDeviceType);
|
|
|
|
// Replace original items
|
|
lpOpen->lpstrDeviceType = lpstrOriginalType;
|
|
lpOpen->lpstrElementName = lpstrOriginalElement;
|
|
lpOpen->lpstrAlias = lpstrOriginalAlias;
|
|
|
|
return wReturn;
|
|
}
|
|
|
|
STATICFN void PASCAL NEAR
|
|
mciFreeDevice(
|
|
LPMCI_DEVICE_NODE nodeWorking
|
|
)
|
|
{
|
|
UINT wID = nodeWorking->wDeviceID;
|
|
|
|
mciMessage(THUNK_MCI_FREE_NODE, (DWORD) nodeWorking->wDeviceID, 0L, 0L, 0L);
|
|
|
|
if (nodeWorking->lpstrName != NULL)
|
|
mciFree (nodeWorking->lpstrName);
|
|
|
|
if (nodeWorking->lpstrInstallName != NULL)
|
|
mciFree (nodeWorking->lpstrInstallName);
|
|
|
|
mciFree(MCI_lpDeviceList[wID]);
|
|
MCI_lpDeviceList[wID] = NULL;
|
|
|
|
/* If this was the last device in the list, decrement next ID value */
|
|
if (wID + 1 == MCI_wNextDeviceID) {
|
|
--MCI_wNextDeviceID;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | mciCloseDevice | Close an MCI device. Used in
|
|
* processing the MCI_CLOSE message.
|
|
*
|
|
* @parm UINT | wID | The ID of the device to close
|
|
* @parm DWORD | dwFlags | Close Flags
|
|
* @parm LPMCI_GENERIC_PARMS | lpClose | Generic parameters
|
|
* @parm BOOL | bCloseDriver | TRUE if the CLOSE command should be sent
|
|
* on to the driver.
|
|
*
|
|
* @rdesc 0 if successful or an error code
|
|
*
|
|
* @comm This function sends an MCI_CLOSE_DRIVER message to the corresponding
|
|
* driver if the use count is zero and then unloads the driver DLL
|
|
*
|
|
*/
|
|
UINT NEAR PASCAL mciCloseDevice (
|
|
UINT wID,
|
|
DWORD dwFlags,
|
|
LPMCI_GENERIC_PARMS lpGeneric,
|
|
BOOL bCloseDriver)
|
|
{
|
|
LPMCI_DEVICE_NODE nodeWorking;
|
|
UINT wErr, wTable;
|
|
|
|
nodeWorking = MCI_lpDeviceList[wID];
|
|
|
|
if (nodeWorking == NULL)
|
|
{
|
|
DOUT ("mciCloseDevice: NULL node from device ID--error if not auto-close\r\n");
|
|
return 0;
|
|
}
|
|
|
|
// If a close is in progress (usually this message comes from a Yield
|
|
// after a mciDriverNotify actuated by the active close) then exit
|
|
if (nodeWorking->dwMCIFlags & MCINODE_ISCLOSING)
|
|
return 0;
|
|
|
|
nodeWorking->dwMCIFlags |= MCINODE_ISCLOSING;
|
|
if (bCloseDriver)
|
|
{
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
// Make fake generic params if close came internally
|
|
if (lpGeneric == NULL)
|
|
lpGeneric = &GenericParms;
|
|
|
|
wErr = LOWORD(mciSendCommand (wID, MCI_CLOSE_DRIVER, dwFlags,
|
|
(DWORD)lpGeneric));
|
|
}
|
|
else
|
|
wErr = 0;
|
|
|
|
// Must zero this to allow the table to be freed by the driver
|
|
nodeWorking->wCustomCommandTable = 0;
|
|
|
|
wTable = nodeWorking->wCommandTable;
|
|
// Must zero this to allow the table to be freed
|
|
nodeWorking->wCommandTable = 0;
|
|
mciFreeCommandResource (wTable);
|
|
|
|
CloseDriver (nodeWorking->hDrvDriver, 0L, 0L);
|
|
|
|
mciFreeDevice (nodeWorking);
|
|
|
|
return wErr;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI DDK
|
|
* @api DWORD | mciGetDriverData | Returns a pointer to the instance
|
|
* data associated with an MCI device
|
|
*
|
|
* @parm UINT | wDeviceID | The MCI device ID
|
|
*
|
|
* @rdesc The driver instance data. On error, returns 0 but since
|
|
* the driver data might be zero, this cannot be verified by the caller
|
|
* unless the instance data is known to be non-zero (e.g. a pointer)
|
|
*
|
|
*/
|
|
DWORD WINAPI mciGetDriverData (
|
|
UINT wDeviceID)
|
|
{
|
|
if (!MCI_VALID_DEVICE_ID(wDeviceID))
|
|
{
|
|
DOUT ("mciGetDriverData: invalid device ID\r\n");
|
|
return 0;
|
|
}
|
|
return MCI_lpDeviceList[wDeviceID]->lpDriverData;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI DDK
|
|
* @func BOOL | mciSetDriverData | Sets the instance
|
|
* data associated with an MCI device
|
|
*
|
|
* @parm UINT | wDeviceID | The MCI device ID
|
|
*
|
|
* @parm DWORD | dwData | Driver data to set
|
|
*
|
|
* @rdesc FALSE if the device ID is not known or there is insufficient
|
|
* memory to load the device description, else TRUE.
|
|
*
|
|
*/
|
|
BOOL WINAPI mciSetDriverData (
|
|
UINT wDeviceID,
|
|
DWORD dwData)
|
|
{
|
|
if (!MCI_VALID_DEVICE_ID(wDeviceID))
|
|
{
|
|
DOUT ("mciSetDriverData: invalid device ID\r\n");
|
|
return FALSE;
|
|
}
|
|
MCI_lpDeviceList[wDeviceID]->lpDriverData = dwData;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI DDK
|
|
* @api UINT | mciDriverYield | Used in a driver's idle loop
|
|
* to yield to Windows
|
|
*
|
|
* @parm UINT | wDeviceID | Device ID that is yielding.
|
|
*
|
|
* @rdesc Non-zero if the driver should abort the operation.
|
|
*
|
|
*/
|
|
UINT WINAPI mciDriverYield (
|
|
UINT wDeviceID)
|
|
{
|
|
if (MCI_VALID_DEVICE_ID(wDeviceID))
|
|
{
|
|
LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
|
|
|
|
if (node->fpYieldProc != NULL)
|
|
return (node->fpYieldProc)(wDeviceID, node->dwYieldData);
|
|
}
|
|
|
|
Yield();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @doc EXTERNAL MCI
|
|
* @api BOOL | mciSetYieldProc | This function sets the address
|
|
* of a callback procedure to be called periodically when an MCI device
|
|
* is completing a command specified with the WAIT flag.
|
|
*
|
|
* @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
|
|
* which the yield procedure is to be assigned.
|
|
*
|
|
* @parm YIELDPROC | fpYieldProc | Specifies the callback procedure
|
|
* to be called when the given device is yielding. Specify a NULL value
|
|
* to disable any existing yield procedure.
|
|
*
|
|
* @parm DWORD | dwYieldData | Specifies the data sent to the yield procedure
|
|
* when it is called for the given device.
|
|
*
|
|
* @rdesc Returns TRUE if successful. Returns FALSE for an invalid device ID.
|
|
*
|
|
* @cb int CALLBACK | YieldProc | <f YieldProc> is a placeholder for
|
|
* the application-supplied function name. Export the actual name
|
|
* by including it in the EXPORTS statement in your module-definition
|
|
* file.
|
|
*
|
|
* @parm UINT | wDeviceID | Specifies the device ID of the MCI device.
|
|
*
|
|
* @parm DWORD | dwData | Specifies the application-supplied yield data
|
|
* originally supplied in the <p dwYieldData> parameter.
|
|
*
|
|
* @rdesc Return zero to continue the operation. To cancel the operation,
|
|
* return a nonzero value.
|
|
*
|
|
* @comm This call overrides any previous yield procedure for this device.
|
|
*
|
|
*/
|
|
BOOL WINAPI mciSetYieldProc (
|
|
UINT wDeviceID,
|
|
YIELDPROC fpYieldProc,
|
|
DWORD dwYieldData)
|
|
{
|
|
V_CALLBACK((FARPROC)fpYieldProc, FALSE);
|
|
|
|
if (Is16bitDrv(wDeviceID)) {
|
|
|
|
LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
|
|
|
|
node->fpYieldProc = fpYieldProc;
|
|
node->dwYieldData = dwYieldData;
|
|
return TRUE;
|
|
}
|
|
|
|
return (BOOL)mciMessage( THUNK_MCI_SETYIELDPROC, (DWORD)wDeviceID,
|
|
(DWORD)fpYieldProc, dwYieldData, 0L );
|
|
|
|
}
|
|
|
|
/*
|
|
* @doc EXTERNAL MCI
|
|
* @api YIELDPROC | mciGetYieldProc | This function gets the address
|
|
* of the callback procedure to be called periodically when an MCI device
|
|
* is completing a command specified with the WAIT flag.
|
|
*
|
|
* @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
|
|
* which the yield procedure is to be retrieved from.
|
|
*
|
|
* @parm LPDWORD | lpdwYieldData | Optionally specifies a buffer to place
|
|
* the yield data passed to the function in. If the parameter is NULL, it
|
|
* is ignored.
|
|
*
|
|
* @rdesc Returns the current yield proc if any, else returns NULL for an
|
|
* invalid device ID.
|
|
*
|
|
*/
|
|
YIELDPROC WINAPI mciGetYieldProc (
|
|
UINT wDeviceID,
|
|
LPDWORD lpdwYieldData)
|
|
{
|
|
/*
|
|
** Is this a 16 bit device ID ?
|
|
*/
|
|
if (Is16bitDrv(wDeviceID)) {
|
|
|
|
if (lpdwYieldData != NULL) {
|
|
V_WPOINTER(lpdwYieldData, sizeof(DWORD), NULL);
|
|
*lpdwYieldData = MCI_lpDeviceList[wDeviceID]->dwYieldData;
|
|
}
|
|
return MCI_lpDeviceList[wDeviceID]->fpYieldProc;
|
|
}
|
|
|
|
/*
|
|
** No, so pass it on to the 32 bit code.
|
|
*/
|
|
return (YIELDPROC)mciMessage( THUNK_MCI_GETYIELDPROC, (DWORD)wDeviceID,
|
|
(DWORD)lpdwYieldData, 0L, 0L );
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @api int | mciBreakKeyYieldProc | Procedure called to check a
|
|
* key state for the given device
|
|
*
|
|
* @parm UINT | wDeviceID | Device ID which is yielding
|
|
*
|
|
* @parm DWORD | dwYieldData | Data for this device's yield proc
|
|
*
|
|
* @rdesc Non-zero if the driver should abort the operation. Currently
|
|
* always returns 0.
|
|
*
|
|
*/
|
|
int CALLBACK mciBreakKeyYieldProc (
|
|
UINT wDeviceID,
|
|
DWORD dwYieldData)
|
|
{
|
|
HWND hwndCheck;
|
|
int nState;
|
|
|
|
hwndCheck = (HWND)HIWORD (dwYieldData);
|
|
if (hwndCheck == NULL || hwndCheck == GetActiveWindow())
|
|
{
|
|
nState = GetAsyncKeyState (LOWORD(dwYieldData));
|
|
|
|
// Break if key is down or has been down
|
|
if (nState & 1)
|
|
{
|
|
MSG msg;
|
|
|
|
while (PeekMessage (&msg, hwndCheck, WM_KEYFIRST, WM_KEYLAST,
|
|
PM_REMOVE));
|
|
return -1;
|
|
}
|
|
}
|
|
Yield();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCI
|
|
* @func UINT | mciSetBreakKey | Set a key which will break a wait loop
|
|
* for a given driver
|
|
*
|
|
* @parm UINT | wDeviceID | The device ID to assign a break key to
|
|
*
|
|
* @parm int | nVirtKey | Virtual key code to trap
|
|
*
|
|
* @parm HWND | hwndTrap | The handle to a window that must be active
|
|
* for the key to be trapped. If NULL then all windows will be checked
|
|
*
|
|
* @rdesc TRUE if successful, FALSE if invalid device ID
|
|
*
|
|
*/
|
|
UINT PASCAL NEAR mciSetBreakKey (
|
|
UINT wDeviceID,
|
|
int nVirtKey,
|
|
HWND hwndTrap)
|
|
{
|
|
return mciSetYieldProc (wDeviceID, mciBreakKeyYieldProc,
|
|
MAKELONG (nVirtKey, hwndTrap));
|
|
}
|