windows-nt/Source/XPSP1/NT/multimedia/media/winmm/mcisys.c
2020-09-26 16:20:57 +08:00

2250 lines
66 KiB
C

/*****************************Module*Header*********************************\
* Module Name: mcisys.c
*
* Media Control Architecture System Functions
*
* Created: 2/28/90
* Author: DLL (DavidLe)
* 5/22/91: Ported to Win32 - NigelT
*
* History:
* Mar 92 SteveDav - brought up to Win 3.1 ship level
*
* Copyright (c) 1991-1999 Microsoft Corporation
*
\******************************************************************************/
#define UNICODE
#define _CTYPE_DISABLE_MACROS
#include "winmmi.h"
#include "mci.h"
#include "wchar.h"
#include "ctype.h"
extern WSZCODE wszOpen[]; // in MCI.C
STATICDT WSZCODE wszMciExtensions[] = L"Mci Extensions";
#define MCI_EXTENSIONS wszMciExtensions
#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 or to mciGetDeviceID or to mciGetErrorString
// We could do it when WINMM is loaded - but that is a bit excessive.
// The user may not need MCI functions.
BOOL MCI_bDeviceListInitialized = FALSE;
// The next device ID to use for a new device
MCIDEVICEID 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 = NULL;
// The current size of the list of MCI devices
UINT MCI_wDeviceListSize = 0;
#if 0 // we don't use this (NigelT)
// The internal mci heap used by mciAlloc and mciFree
HANDLE hMciHeap = NULL;
#endif
STATICDT WSZCODE wszAllDeviceName[] = L"all";
STATICDT WSZCODE szUnsignedFormat[] = L"%u";
STATICFN void mciFreeDevice(LPMCI_DEVICE_NODE nodeWorking);
//------------------------------------------------------------------
// Initialize device list
// Called once by mciSendString or mciSendCommand
// Returns TRUE on success
//------------------------------------------------------------------
BOOL mciInitDeviceList(void)
{
BOOL fReturn=FALSE;
#if 0 // we don't use this (NigelT)
if ((hMciHeap = HeapCreate(0)) == 0)
{
dprintf1(("Mci heap create failed!"));
return FALSE;
}
#endif
try {
mciEnter("mciInitDeviceList");
if (!MCI_bDeviceListInitialized) {
// We have to retest the init flag to be totally thread safe.
// Otherwise in theory we could end up initializing twice.
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;
fReturn = TRUE;
} else {
dprintf1(("MCIInit: could not allocate master MCI device list"));
fReturn = FALSE;
}
}
} finally {
mciLeave("mciInitDeviceList");
}
return(fReturn);
}
/*
* @doc EXTERNAL MCI
* @api MCIDEVICEID | mciGetDeviceIDFromElementID | This function
* retrieves the MCI device ID corresponding to and element ID
*
* @parm DWORD | dwElementID | The element ID
*
* @parm LPCTSTR | 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.
*
*/
MCIDEVICEID APIENTRY mciGetDeviceIDFromElementIDA (
DWORD dwElementID,
LPCSTR lpstrType)
{
LPCWSTR lpwstr;
MCIDEVICEID mr;
lpwstr = AllocUnicodeStr( (LPSTR)lpstrType );
if ( lpwstr == NULL ) {
return (MCIDEVICEID)(UINT_PTR)NULL;
}
mr = mciGetDeviceIDFromElementIDW( dwElementID, lpwstr );
FreeUnicodeStr( (LPWSTR)lpwstr );
return mr;
}
MCIDEVICEID APIENTRY mciGetDeviceIDFromElementIDW (
DWORD dwElementID,
LPCWSTR lpstrType)
{
MCIDEVICEID wID;
LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
WCHAR strTemp[MCI_MAX_DEVICE_TYPE_LENGTH];
if (lpstrType == NULL) {
return 0;
}
mciEnter("mciGetDeviceIDFromElementID");
nodeCounter = &MCI_lpDeviceList[1];
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
{
if (NULL == (nodeWorking = *nodeCounter++)) {
continue;
}
if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID &&
nodeWorking->dwElementID == dwElementID) {
if (LoadStringW( ghInst, nodeWorking->wDeviceType, strTemp,
sizeof(strTemp) / sizeof(WCHAR) ) != 0
&& lstrcmpiW( strTemp, lpstrType) == 0) {
mciLeave("mciGetDeviceIDFromElementID");
return wID;
}
}
}
mciLeave("mciGetDeviceIDFromElementID");
return 0;
}
// Retrieves the device ID corresponding to the name of an opened device
// matching the given task
STATICFN MCIDEVICEID mciGetDeviceIDInternal (
LPCWSTR lpstrName,
HANDLE hCurrentTask)
{
MCIDEVICEID wID;
LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
#if DBG
if (!lpstrName) {
dprintf(("!! NULL POINTER !! Internal error"));
return(0);
}
#endif
if ( lstrcmpiW(wszAllDeviceName, lpstrName) == 0)
return MCI_ALL_DEVICE_ID;
if (MCI_lpDeviceList == NULL)
return 0;
// Loop through the MCI device list. Skip any 16-bit devices.
mciEnter("mciGetDeviceIDInternal");
nodeCounter = &MCI_lpDeviceList[1];
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
{
if (NULL == (nodeWorking = *nodeCounter++)) {
continue;
}
// If this device is 16-bit then skip it
if (nodeWorking->dwMCIFlags & MCINODE_16BIT_DRIVER) {
continue;
}
// If this device does not have a name then skip it
if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID) {
continue;
}
// If the names match, and the previous device is not being closed
if ( lstrcmpiW( nodeWorking->lpstrName, lpstrName ) == 0 ) {
if (ISAUTOCLOSING(nodeWorking))
{
// As this auto opened device is being closed we do not match
// against its name. The result is that a new auto opened
// device will be used. This would be the case if this
// command was issued momentarily later by which time we
// would have finished closing the existing device.
} else {
// If the device belongs to the current task
if (nodeWorking->hOpeningTask == hCurrentTask ||
nodeWorking->hCreatorTask == hCurrentTask) {
// Return this device ID
mciLeave("mciGetDeviceIDInternal");
return wID;
}
}
}
}
mciLeave("mciGetDeviceIDInternal");
return 0;
}
/*
* @doc EXTERNAL MCI
* @api MCIDEVICEID | mciGetDeviceID | This function retrieves the device
* ID corresponding to the name of an opened device.
*
* @parm LPCTSTR | lpstrName | Points to the device name from SYSTEM.INI, or
* the alias name by which the device is known.
*
* @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. Each compound device element has a unique device ID.
* The ID of the "all" device is MCI_ALL_DEVICE_ID
*
* @xref MCI_OPEN
*
*/
MCIDEVICEID mciGetDeviceIDW (
LPCWSTR lpstrName)
{
return mciGetDeviceIDInternal (lpstrName, GetCurrentTask());
}
MCIDEVICEID mciGetDeviceIDA (
LPCSTR lpstrName)
{
LPCWSTR lpwstr;
MCIDEVICEID mr;
lpwstr = AllocUnicodeStr( (LPSTR)lpstrName );
if ( lpwstr == NULL ) {
return (MCIDEVICEID)(UINT_PTR)NULL;
}
mr = mciGetDeviceIDInternal( lpwstr, GetCurrentTask() );
FreeUnicodeStr( (LPWSTR)lpwstr );
return mr;
}
/*
* @doc EXTERNAL MCI
* @api HMODULE | mciGetCreatorTask | This function retrieves the creator task
* corresponding with the device ID passed.
*
* @parm MCIDEVICEID | 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 APIENTRY mciGetCreatorTask (
MCIDEVICEID wDeviceID)
{
HTASK hCreatorTask;
mciEnter("mciGetCreatorTask");
if (MCI_VALID_DEVICE_ID(wDeviceID)) {
hCreatorTask = MCI_lpDeviceList[wDeviceID]->hCreatorTask;
} else {
hCreatorTask = NULL;
}
mciLeave("mciGetCreatorTask");
return hCreatorTask;
}
/*
* @doc INTERNAL MCI
* @api BOOL FAR | 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 LPWSTR | lpstrDeviceName | The device name, possibly
* with trailing digits but no blanks.
*
* @parm LPWSTR | lpstrDeviceType | The device type with no trailing digits
* or blanks
*
* @rdesc TRUE if the strings match the above test, FALSE otherwise
*
*/
STATICFN BOOL mciDeviceMatch (
LPCWSTR lpstrDeviceName,
LPCWSTR lpstrDeviceType)
{
BOOL bRetVal = TRUE, bAtLeastOne = FALSE;
// Scan until one of the strings ends
dprintf2(("mciDeviceMatch: %ls Vs %ls",lpstrDeviceName,lpstrDeviceType));
while (*lpstrDeviceName != '\0' && *lpstrDeviceType != '\0') {
if (towlower(*lpstrDeviceName++) == towlower(*lpstrDeviceType++)) {
bAtLeastOne = TRUE;
} else {
break;
}
}
// If end of device type, scan to the end of device name, trailing digits
// are OK
if (!bAtLeastOne || *lpstrDeviceType != '\0') {
return FALSE;
}
while (*lpstrDeviceName != '\0')
{
// No match, but that is OK if a digit trails
// Is the remainder of the string a digit? We could check using
// a simple if test (<0 or >9) but that would run into problems if
// anyone ever passed a unicode "numeric" string outside the ascii
// number range. Using isdigit should be safer if marginally slower.
if (!isdigit(*lpstrDeviceName)) {
// No match - a non digit trails
return FALSE;
}
++lpstrDeviceName;
}
return TRUE;
}
/*
* @doc INTERNAL MCI
* @api UINT | mciLookUpType | Look up the type given a type name
*
* @parm LPCWSTR | lpstrTypeName | The type name to look up. Trailing
* digits are ignored.
*
* @rdesc The MCI type number (MCI_DEVTYPE_<x>) or 0 if not found
*
*/
UINT mciLookUpType (
LPCWSTR lpstrTypeName)
{
UINT wType;
WCHAR strType[MCI_MAX_DEVICE_TYPE_LENGTH];
for (wType = MCI_DEVTYPE_FIRST; wType <= MCI_DEVTYPE_LAST; ++wType)
{
if ( LoadStringW( ghInst,
wType,
strType,
sizeof(strType) / sizeof(WCHAR) ) == 0)
{
dprintf1(("mciLookUpType: could not load string for type"));
continue;
}
if (mciDeviceMatch (lpstrTypeName, strType)) {
return wType;
}
}
return 0;
}
/*
* @doc INTERNAL MCI
* @api DWORD | mciSysinfo | Get system information about a device
*
* @parm MCIDEVICEID | 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 mciSysinfo (
MCIDEVICEID wDeviceID,
DWORD dwFlags,
LPMCI_SYSINFO_PARMSW lpSysinfo)
{
UINT nCounted;
WCHAR strBuffer[MCI_PROFILE_STRING_LENGTH];
LPWSTR lpstrBuffer = strBuffer, lpstrStart;
if (dwFlags & MCI_SYSINFO_NAME && lpSysinfo->dwNumber == 0)
return MCIERR_OUTOFRANGE;
if (lpSysinfo->lpstrReturn == NULL || lpSysinfo->dwRetSize == 0)
return MCIERR_PARAM_OVERFLOW;
#ifdef LATER
// if ((dwFlags & (MCI_SYSINFO_NAME | MCI_SYSINFO_INSTALLNAME))
// && (dwFlags & MCI_SYSINFO_QUANTITY))
// Should be invalid to ask for Quantity and any sort of name
#endif
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;
mciEnter("mciSysinfo");
if (!MCI_VALID_DEVICE_ID (wDeviceID)) {
mciLeave("mciSysinfo");
return MCIERR_INVALID_DEVICE_NAME;
}
#if DBG
if ((nodeWorking = MCI_lpDeviceList[wDeviceID]) == NULL ||
nodeWorking->lpstrInstallName == NULL)
{
dprintf1(("mciSysinfo: NULL device node or installname"));
mciLeave("mciSysinfo");
return MCIERR_INTERNAL;
}
#else
nodeWorking = MCI_lpDeviceList[wDeviceID];
#endif
if ( (DWORD)wcslen( nodeWorking->lpstrInstallName ) >=
lpSysinfo->dwRetSize )
{
mciLeave("mciSysinfo");
return MCIERR_PARAM_OVERFLOW;
}
wcscpy (lpSysinfo->lpstrReturn, nodeWorking->lpstrInstallName);
mciLeave("mciSysinfo");
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;
GetPrivateProfileStringW( MCI_HANDLERS, NULL, wszNull,
lpstrBuffer,
MCI_PROFILE_STRING_LENGTH,
MCIDRIVERS_INI_FILE);
nCounted = 0;
while (TRUE)
{
if (dwFlags & MCI_SYSINFO_QUANTITY)
{
if (*lpstrBuffer == '\0')
{
if ( (lpSysinfo->lpstrReturn == NULL) ||
(sizeof(DWORD) > lpSysinfo->dwRetSize))
return MCIERR_PARAM_OVERFLOW;
*(UNALIGNED DWORD *)lpSysinfo->lpstrReturn = (DWORD)nCounted;
return MCI_INTEGER_RETURNED;
}
if (wDeviceID == MCI_ALL_DEVICE_ID ||
mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
++nCounted;
// Skip past the terminating '\0'
while (*lpstrBuffer++ != '\0') {}
}
else if (dwFlags & MCI_SYSINFO_NAME) // if test is redundant
{
if (nCounted == lpSysinfo->dwNumber)
{
/* NOTE:
* We know that lpSysinfo->dwNumber > 0
* Hence we will have been through the loop at least once
* Hence lpstrStart has been set up
*/
if ( (DWORD)wcslen( lpstrStart ) >= lpSysinfo->dwRetSize )
{
return MCIERR_PARAM_OVERFLOW;
}
wcscpy (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)
++nCounted;
// Skip past the terminating '\0'
while (*lpstrBuffer++ != '\0') {}
}
}
}
} else
// Process MCI_SYSINFO_OPEN cases
{
MCIDEVICEID wID;
HANDLE 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;
nCounted = 0;
mciEnter("mciSysinfo");
for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
{
if ((Node = MCI_lpDeviceList[wID]) == 0)
continue;
if (wDeviceID == MCI_ALL_DEVICE_ID &&
Node->hOpeningTask == hCurrentTask) {
++nCounted;
}
else
{
if (Node->wDeviceType == lpSysinfo->wDeviceType &&
Node->hOpeningTask == hCurrentTask)
++nCounted;
}
if (dwFlags & MCI_SYSINFO_NAME &&
nCounted == lpSysinfo->dwNumber)
{
DWORD dwReturn;
if ( (DWORD)wcslen( Node->lpstrName ) >= lpSysinfo->dwRetSize )
{
dwReturn = MCIERR_PARAM_OVERFLOW;
} else {
wcscpy (lpSysinfo->lpstrReturn, Node->lpstrName);
dwReturn = 0;
}
mciLeave("mciSysinfo");
return dwReturn;
}
}
mciLeave("mciSysinfo");
if (dwFlags & MCI_SYSINFO_NAME)
{
if (lpSysinfo->lpstrReturn != NULL)
lpSysinfo->lpstrReturn = '\0';
return MCIERR_OUTOFRANGE;
} else if (dwFlags & MCI_SYSINFO_QUANTITY && // checking for QUANTITY is redundant
lpSysinfo->lpstrReturn != NULL &&
lpSysinfo->dwRetSize >= 4) {
*(UNALIGNED DWORD *)lpSysinfo->lpstrReturn = nCounted;
return MCI_INTEGER_RETURNED;
}
}
return MCIERR_PARAM_OVERFLOW;
}
/*
* @doc INTERNAL MCI
* @api MCIDEVICEID | wReserveDeviceID | Copy the given global handle into the
* first free entry in the MCI device table and return that entry's ID#
*
* @parm HANDLE | hNode | Local handle to device description
*
* @rdesc The ID value that has been reserved for this device or 0 if
* there are no more free entries
*
*/
STATICFN MCIDEVICEID wReserveDeviceID (
LPMCI_DEVICE_NODE node)
{
UINT wDeviceID;
LPMCI_DEVICE_NODE FAR *lpTempList;
mciEnter("wReserveDeviceID");
// Search for an empty slot
for (wDeviceID = 1; wDeviceID < MCI_wNextDeviceID; ++wDeviceID)
if (MCI_lpDeviceList[wDeviceID] == NULL) {
goto slot_found;
}
// No empty slots found so add to end
if (wDeviceID >= MCI_wDeviceListSize)
{
// The list is full (or non existent) so try to grow it
if ((lpTempList = mciReAlloc (MCI_lpDeviceList,
sizeof (LPMCI_DEVICE_NODE) * (MCI_wDeviceListSize + 1 +
MCI_DEVICE_LIST_GROW_SIZE)))
== NULL)
{
dprintf1(("wReserveDeviceID: cannot grow device list"));
mciLeave("wReserveDeviceID");
return 0;
}
MCI_lpDeviceList = lpTempList;
MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
}
++MCI_wNextDeviceID;
slot_found:;
MCI_lpDeviceList[wDeviceID] = node;
mciLeave("wReserveDeviceID");
return (MCIDEVICEID)wDeviceID;
}
//
// Allocate space for the given string and assign the name to the given
// device.
// Return FALSE if could not allocate memory
//
STATICFN BOOL NEAR mciAddDeviceName(
LPMCI_DEVICE_NODE nodeWorking,
LPCWSTR lpDeviceName)
{
nodeWorking->lpstrName = (LPWSTR)mciAlloc(
BYTE_GIVEN_CHAR( wcslen(lpDeviceName) + 1 ) );
if (nodeWorking->lpstrName == NULL)
{
dprintf1(("mciAddDeviceName: Out of memory allocating device name"));
return FALSE;
}
// copy device name to mci node and lowercase it
wcscpy(nodeWorking->lpstrName, (LPWSTR)lpDeviceName);
//!! mciToLower(nodeWorking->lpstrName);
return TRUE;
}
/*
* @doc INTERNAL MCI
* @api HANDLE | mciAllocateNode | Allocate a new driver entry
*
* @parm DWORD | dwFlags | As sent with MCI_OPEN message
* @parm LPCWSTR | lpDeviceName | The device name
* @parm LPMCI_DEVICE_NODE * | *lpNewNode | Return pointer location
*
* @rdesc The device ID to the new node. 0 on error.
*
* @comm Leaves the new node locked
*
*/
STATICFN MCIDEVICEID NEAR mciAllocateNode (
DWORD dwFlags,
LPCWSTR lpDeviceName,
LPMCI_DEVICE_NODE FAR *lpnodeNew)
{
LPMCI_DEVICE_NODE nodeWorking;
if ((nodeWorking = mciAlloc(sizeof(MCI_DEVICE_NODE))) == NULL)
{
dprintf1(("Out of memory in mciAllocateNode"));
return 0;
}
/* Fill in the new node */
/* Get a new device ID, if there are none available then bail */
if ((nodeWorking->wDeviceID = wReserveDeviceID(nodeWorking)) == 0)
{
dprintf1(("mciAllocateNode: Cannot allocate new node"));
mciFree(nodeWorking);
return 0;
}
// Initialize node
nodeWorking->dwMCIOpenFlags = dwFlags;
nodeWorking->hCreatorTask = GetCurrentTask ();
nodeWorking->hOpeningTask = nodeWorking->hCreatorTask;
// The new node is zeroed
// nodeWorking->fpYieldProc = NULL;
// nodeWorking->dwMCIFlags = 0;
if (dwFlags & MCI_OPEN_ELEMENT_ID)
// No device name, just an element ID
nodeWorking->dwElementID = PtrToUlong(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 mciReparseOpen (
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
UINT wCustomTable,
UINT wTypeTable,
LPDWORD lpdwFlags,
LPMCI_OPEN_PARMSW FAR *lplpOpen,
MCIDEVICEID wDeviceID)
{
LPWSTR lpCommand;
LPDWORD lpdwParams = NULL;
UINT wErr;
UINT wTable = wCustomTable;
DWORD dwOldFlags = *lpdwFlags;
// If the custom table contains no open command
if (wCustomTable == MCI_TABLE_NOT_PRESENT ||
(lpCommand = FindCommandInTable (wCustomTable, wszOpen, NULL)) == NULL)
{
// Try the type specific table
lpCommand = FindCommandInTable (wTypeTable, wszOpen, 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_PTR) * MCI_MAX_PARAM_SLOTS))
== NULL)
return MCIERR_OUT_OF_MEMORY;
wErr = mciParseParams ( MCI_OPEN ,
lpOpenInfo->lpstrParams, lpCommand,
lpdwFlags,
(LPWSTR)lpdwParams,
sizeof(DWORD_PTR) * MCI_MAX_PARAM_SLOTS,
&lpOpenInfo->lpstrPointerList, NULL);
// We don't need this around anymore
mciUnlockCommandTable (wTable);
// If there was a parsing error
if (wErr != 0)
{
// Close device down
mciCloseDevice (wDeviceID, 0L, NULL, FALSE);
// 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_PARMSW)lpdwParams)->lpstrDeviceType
= (*lplpOpen)->lpstrDeviceType;
*lpdwFlags |= MCI_OPEN_TYPE;
}
if (dwOldFlags & MCI_OPEN_ELEMENT)
{
// Element name was already extracted so add it manually
((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrElementName
= (*lplpOpen)->lpstrElementName;
*lpdwFlags |= MCI_OPEN_ELEMENT;
}
if (dwOldFlags & MCI_OPEN_ALIAS)
{
// Alias name was already extracted so add it manually
((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrAlias
= (*lplpOpen)->lpstrAlias;
*lpdwFlags |= MCI_OPEN_ALIAS;
}
if (dwOldFlags & MCI_NOTIFY)
// Notify was already extracted so add it manually
((LPMCI_OPEN_PARMSW)lpdwParams)->dwCallback
= (*lplpOpen)->dwCallback;
// Replace old parameter list with new list
*lplpOpen = (LPMCI_OPEN_PARMSW)lpdwParams;
return 0;
}
//**************************************************************************
// mciFindDriverName
//
// 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 DWORD mciFindDriverName (
LPCWSTR lpstrDriverName,
LPWSTR lpstrDevice,
LPWSTR lpstrProfString,
UINT wProfLength) // this should be a character count
{
LPWSTR lpstrEnum, lpstrEnumStart;
UINT wEnumLen = 100;
DWORD wErr;
LPWSTR lpstrDriverTemp, lpstrProfTemp;
// Enumerate values, trying until they fit into the buffer
while (TRUE) {
if ((lpstrEnum = mciAlloc( BYTE_GIVEN_CHAR(wEnumLen) ) ) == NULL)
return MCIERR_OUT_OF_MEMORY;
wErr = GetPrivateProfileStringW( MCI_HANDLERS,
NULL, wszNull,
lpstrEnum,
wEnumLen,
MCIDRIVERS_INI_FILE );
if (*lpstrEnum == '\0')
{
mciFree (lpstrEnum);
return MCIERR_DEVICE_NOT_INSTALLED;
}
if (wErr == wEnumLen - 2)
{
wEnumLen *= 2;
mciFree (lpstrEnum);
} else
break;
}
lpstrEnumStart = lpstrEnum;
if ( wcslen(lpstrDriverName) >= MCI_MAX_DEVICE_TYPE_LENGTH ) {
wErr = MCIERR_DEVICE_LENGTH;
goto exit_fn;
}
wcscpy(lpstrDevice, lpstrDriverName);
//!! mciToLower (lpstrDevice);
// Walk through each string
while (TRUE) {
wErr = GetPrivateProfileStringW( MCI_HANDLERS,
lpstrEnum, wszNull, lpstrProfString,
wProfLength,
MCIDRIVERS_INI_FILE );
if (*lpstrProfString == '\0')
{
dprintf1(("mciFindDriverName: cannot load valid keyname"));
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 '.' then we've got it!
if (*lpstrDriverTemp == '\0' &&
(*lpstrProfTemp == ' ' || *lpstrProfTemp == '.'))
{
if (wcslen (lpstrEnum) >= MCI_MAX_DEVICE_TYPE_LENGTH)
{
dprintf1(("mciFindDriverName: device name too long"));
wErr = MCIERR_DEVICE_LENGTH;
goto exit_fn;
}
wcscpy (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 DWORD mciLoadDevice (
DWORD dwFlags,
LPMCI_OPEN_PARMSW lpOpen,
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
BOOL bDefaultAlias)
{
LPMCI_DEVICE_NODE nodeWorking;
HANDLE hDriver;
MCIDEVICEID wID;
DWORD wErr;
WCHAR strProfileString[MCI_PROFILE_STRING_LENGTH];
WCHAR szDriverParms[128];
MCI_OPEN_DRIVER_PARMS DriverOpen;
HANDLE hDrvDriver;
LPWSTR lpstrParams;
LPCWSTR lpstrInstallName, lpstrDeviceName;
LPWSTR lpstrCopy = NULL;
LPMCI_OPEN_PARMSW lpOriginalOpenParms = lpOpen;
/* Open a normal device */
#if DBG
if (lpOpen && lpOpen->lpstrDeviceType) {
dprintf2(("mciLoadDevice(%ls)", lpOpen->lpstrDeviceType));
} else {
dprintf2(("mciLoadDevice()"));
}
#endif
/* Check for the device name in MCIDRIVERS_INI_FILE */
lpstrInstallName = lpOpen->lpstrDeviceType;
wErr = GetPrivateProfileStringW( MCI_HANDLERS,
lpstrInstallName,
wszNull,
strProfileString,
MCI_PROFILE_STRING_LENGTH,
MCIDRIVERS_INI_FILE );
// If device name not found
if (wErr == 0)
{
int nLen = wcslen(lpstrInstallName);
int index;
// Try for the device name with a '1' thru a '9' appended to it
if ((lpstrCopy = (LPWSTR)mciAlloc( BYTE_GIVEN_CHAR(nLen+2)
/* space for digit too */ ) ) == NULL)
{
dprintf1(("mciLoadDevice: cannot allocate device name copy"));
return MCIERR_OUT_OF_MEMORY;
}
wcscpy( lpstrCopy, lpstrInstallName );
lpstrCopy[nLen + 1] = '\0';
for (index = 1; index <= 9; ++index)
{
lpstrCopy[nLen] = (WCHAR)('0' + index);
wErr = GetPrivateProfileStringW(
MCI_HANDLERS,
lpstrCopy,
wszNull,
strProfileString,
MCI_PROFILE_STRING_LENGTH,
MCIDRIVERS_INI_FILE );
if (wErr != 0) {
dprintf2(("Loaded driver name %ls >> %ls", lpstrCopy, strProfileString));
break;
}
}
if (wErr == 0)
{
mciFree (lpstrCopy);
if ((lpstrCopy = (LPWSTR)mciAlloc( BYTE_GIVEN_CHAR( MCI_MAX_DEVICE_TYPE_LENGTH )))
== NULL)
{
dprintf1(("mciLoadDevice: cannot allocate device name copy"));
return MCIERR_OUT_OF_MEMORY;
}
if ((wErr = mciFindDriverName(
lpstrInstallName,
lpstrCopy,
strProfileString,
MCI_PROFILE_STRING_LENGTH )) != 0)
{
dprintf1(("mciLoadDevice - invalid device name %ls", lpstrInstallName));
goto exit_fn;
}
}
lpstrInstallName = lpstrCopy;
}
// Break out the device driver pathname and the parameter list
lpstrParams = strProfileString;
// Eat characters until blank or null reached
while (*lpstrParams != ' ' && *lpstrParams != '\0') {
++lpstrParams;
}
// Terminate driver file name, and separate the driver file name from its
// parameters. If there are no parameters, i.e. *lpstrParams=='\0',
// leave lpstrParams pointing at the null. Otherwise put a null
// character to terminate the driver file name and step the pointer to
// the first character in the parameter string.
if (*lpstrParams == ' ') { *lpstrParams++ = '\0'; }
//
// We have changed from Win 3.1. Because users cannot write to
// system.ini the parameters have to be read from Win.Ini
// section name [dll_name]
// keyword alias=parameters
// If there are any parameters on the line read from [Drivers] use
// them as a default. This does preserve compatibility for those
// applications that write directly to system.ini (and have the
// privileges to get away with it).
//
// LATER: This stuff will be in the registry once the drivers themselves
// (or it could be the drivers applet) creates a registry mapping.
GetProfileString(strProfileString, lpstrInstallName, lpstrParams,
szDriverParms, sizeof(szDriverParms)/sizeof(WCHAR));
lpstrParams = szDriverParms;
dprintf3(("Parameters for device %ls (Driver %ls) >%ls<",
lpstrInstallName, strProfileString, szDriverParms));
//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
&& mciGetDeviceIDInternal (lpOpen->lpstrAlias,
lpOpenInfo->hCallingTask) != 0)
{
wErr = MCIERR_DUPLICATE_ALIAS;
dprintf1(("mciLoadDevice - duplicate alias"));
goto exit_fn;
}
lpstrDeviceName = lpOpen->lpstrAlias;
}
wID = mciAllocateNode (dwFlags, lpstrDeviceName, &nodeWorking);
if (wID == 0)
{
dprintf1(("mciLoadDevice - cannot allocate new node, driver not loaded"));
wErr = MCIERR_CANNOT_LOAD_DRIVER;
goto exit_fn;
}
// Identify the task which initiated the open command
if (lpOpenInfo->hCallingTask != NULL) {
nodeWorking->hOpeningTask = lpOpenInfo->hCallingTask;
} else {
nodeWorking->hOpeningTask = GetCurrentTask();
}
if (nodeWorking->hOpeningTask != nodeWorking->hCreatorTask)
nodeWorking->dwMCIFlags |= MCINODE_ISAUTOOPENED;
// Initialize the driver
DriverOpen.lpstrParams = lpstrParams;
DriverOpen.wCustomCommandTable = MCI_TABLE_NOT_PRESENT;
DriverOpen.wType = 0;
DriverOpen.wDeviceID = wID;
// Load the driver
hDrvDriver = DrvOpen (strProfileString, MCI_HANDLERS,
(DWORD_PTR)(LPMCI_OPEN_DRIVER_PARMS)&DriverOpen);
if (hDrvDriver == NULL)
{
dprintf1(("mciLoadDevice: DrvOpen failed"));
// 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; Field does not exist in 32bit NT
hDriver = DrvGetModuleHandle (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))
== MCI_TABLE_NOT_PRESENT) {
// Load from a file if necessary
nodeWorking->wCommandTable =
mciLoadCommandResource (ghInst, lpOpen->lpstrDeviceType,
DriverOpen.wType);
dprintf3((" Command table id: %08XH", nodeWorking->wCommandTable));
}
// Record this for 'sysinfo installname'
if ((nodeWorking->lpstrInstallName =
mciAlloc( BYTE_GIVEN_CHAR( wcslen( lpstrInstallName ) + 1 )))
== NULL)
{
mciCloseDevice (wID, 0L, NULL, FALSE);
dprintf1(("mciLoadDevice - out of memory"));
wErr = MCIERR_OUT_OF_MEMORY;
goto exit_fn;
} else
wcscpy( 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)
{
dprintf1(("mciLoadDevice - error reparsing input command"));
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(mciSendCommandW(wID, MCI_OPEN_DRIVER,
dwFlags, (DWORD_PTR)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 (wID, 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
* @api 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 LPCWSTR | lpstrDeviceName | The name to get the type from
*
* @parm LPWSTR | 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 mciExtractDeviceType (
LPCWSTR lpstrDeviceName,
LPWSTR lpstrDeviceType,
UINT wBufLen)
{
LPCWSTR lpstrExt = lpstrDeviceName;
int i;
dprintf2(("mciExtractDeviceType(%ls)", lpstrDeviceName));
#if 0
#ifdef BAD_CODE
//This block cannot be used because it returns FALSE whenever a ! is found.
//Hence if the directory name has a ! ...
//N.B. The ! is used by MCI as a compound device name separator, but that is
//not applicable when going through this routine.
// Goto end of string
while (*lpstrExt != '\0')
{
// WARNING: This causes problems when the directory name has a !
// '!' case is handled elsewhere
if (*lpstrExt++ == '!')
return FALSE;
// Pointer has been incremented in the test
}
#else
// Goto end of string
lpstrExt += wcslen(lpstrExt);
#endif
#else
/*
** scan the string looking for a '!' character. If we find one
** replace it with a NULL and see if the string to its left is a
** supported device type. If it is return FALSE, either way replace the
** '\0' character with a '!'.
*/
{
LPWSTR lpwstr = wcschr(lpstrExt, '!' );
/*
** If we found a '!' and it wasn't the first character in the
** the string we might have a compound device name.
*/
if ( (lpwstr != NULL) && (lpwstr != lpstrExt) ) {
int nResult;
WCHAR wTmp[33];
/*
** We're not interested in the actual string returned only if
** it is present in the list of mci devices. A return code
** of 0 from GetPrivateProfileStringW means we don't have a
** compound name.
*/
*lpwstr = '\0';
nResult = GetPrivateProfileStringW( MCI_HANDLERS, lpstrExt, wszNull,
wTmp, sizeof(wTmp) / sizeof(WCHAR),
MCIDRIVERS_INI_FILE);
/*
** Restore the original string
*/
*lpwstr = '!';
if ( nResult != 0 ) {
return FALSE;
}
}
}
// Goto end of string
lpstrExt += wcslen(lpstrExt);
#endif
// Must be at least 2 characters in string
if (lpstrExt - lpstrDeviceName < 2) {
return FALSE;
}
// Now looking at the NULL terminator. Check the
// previous characters for a '.'
for (i=1; i<=32; ++i)
{
--lpstrExt;
// Cannot have path separator here
if (*lpstrExt == '/' || *lpstrExt == '\\') {
return FALSE;
}
if (*lpstrExt == '.')
{
if (1==i) {
return(FALSE);
// Would mean that extension is a null string
}
#if DBG
if (0 != (GetProfileStringW(MCI_EXTENSIONS, ++lpstrExt,
wszNull, lpstrDeviceType, wBufLen))) {
dprintf2(("Read extension %ls from section %ls. Driver=%ls", lpstrExt, MCI_EXTENSIONS, lpstrDeviceType));
return(TRUE);
} else {
dprintf2(("Failed to read extension %s from section %s.", lpstrExt, MCI_EXTENSIONS));
return(FALSE);
}
#else
return(0 != (GetProfileStringW(MCI_EXTENSIONS, ++lpstrExt,
wszNull, lpstrDeviceType, wBufLen)));
#endif
}
if (lpstrExt == lpstrDeviceName) {
return FALSE;
// We have run out of string
}
}
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 mciEatToken (
LPCWSTR *lplpstrInput,
WCHAR cSeparater,
LPWSTR *lplpstrOutput,
BOOL bMustFind)
{
LPCWSTR lpstrEnd = *lplpstrInput, lpstrCounter;
LPWSTR 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 = (UINT)(lpstrEnd - *lplpstrInput + 1);
if ((*lplpstrOutput = mciAlloc( BYTE_GIVEN_CHAR( 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 mciExtractTypeFromID (
LPMCI_OPEN_PARMSW lpOpen)
{
int nSize;
LPWSTR lpstrType;
if ((lpstrType = mciAlloc( BYTE_GIVEN_CHAR( MCI_MAX_DEVICE_TYPE_LENGTH ))) == NULL)
return MCIERR_OUT_OF_MEMORY;
// Load the type string corresponding to the ID
if ((nSize = LoadStringW( ghInst,
LOWORD (PtrToUlong(lpOpen->lpstrDeviceType)),
lpstrType, MCI_MAX_DEVICE_TYPE_LENGTH ) ) == 0) {
mciFree(lpstrType);
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)
{
dprintf1(("mciExtractTypeFromID: type + ordinal too long"));
mciFree(lpstrType);
return MCIERR_DEVICE_ORD_LENGTH;
}
wsprintfW (lpstrType + nSize, szUnsignedFormat,
HIWORD (PtrToUlong(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
* @parm LPMCI_INTERNAL_OPEN_PARMS | lpOpenInfo | Internal device description
*
* @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, increase the use count
* and return the device ID
*
* Otherwise:
*
* 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 mciOpenDevice (
DWORD dwStartingFlags,
LPMCI_OPEN_PARMSW lpOpen,
LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
{
LPWSTR lpstrNewType = NULL;
UINT wID;
DWORD wReturn;
LPCWSTR lpstrDeviceName;
LPWSTR lpstrNewElement = NULL;
BOOL bFromTypeID = FALSE;
LPCWSTR lpstrOriginalType;
LPCWSTR lpstrOriginalElement;
LPCWSTR lpstrOriginalAlias;
DWORD dwFlags = dwStartingFlags;
BOOL bDefaultAlias = FALSE;
// Initialize
if (lpOpen == NULL) {
dprintf2(("mciOpenDevice() NULL parameter block"));
return MCIERR_NULL_PARAMETER_BLOCK;
}
ClientUpdatePnpInfo();
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 (UINT)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)
{
dprintf1(("mciOpenDevice: Device name is NULL"));
return MCIERR_INVALID_DEVICE_NAME;
}
// Is the device already open?
if (dwFlags & MCI_OPEN_ELEMENT_ID)
wID = mciGetDeviceIDFromElementIDW( PtrToUlong(lpstrDeviceName),
lpOpen->lpstrDeviceType);
else
wID = mciGetDeviceIDInternal ((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))
{
// Allocate a piece of memory for resolving the device type
lpstrNewType = mciAlloc( BYTE_GIVEN_CHAR(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
{
// Allocate a piece of memory for resolving the device type
lpstrNewType = mciAlloc( BYTE_GIVEN_CHAR(MCI_MAX_DEVICE_TYPE_LENGTH) );
if (lpstrNewType == NULL) {
return MCIERR_OUT_OF_MEMORY;
}
// Try to extract a device type from the given device name via a file extension
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)
{
LPCWSTR 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 (mciGetDeviceIDInternal (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))
{
LPCWSTR 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 (lpOpen->lpstrDeviceType);
// Replace original items
lpOpen->lpstrDeviceType = lpstrOriginalType;
lpOpen->lpstrElementName = lpstrOriginalElement;
lpOpen->lpstrAlias = lpstrOriginalAlias;
return (UINT)wReturn;
}
STATICFN void mciFreeDevice (LPMCI_DEVICE_NODE nodeWorking)
{
LPMCI_DEVICE_NODE FAR *lpTempList;
MCIDEVICEID uID = nodeWorking->wDeviceID;
mciEnter("mciFreeDevice");
if (nodeWorking->lpstrName != NULL)
mciFree (nodeWorking->lpstrName);
if (nodeWorking->lpstrInstallName != NULL)
mciFree (nodeWorking->lpstrInstallName);
mciFree(MCI_lpDeviceList[uID]);
MCI_lpDeviceList[uID] = NULL;
/* If this was the last device in the list, decrement next ID value */
if (uID + (MCIDEVICEID)1 == MCI_wNextDeviceID)
{
--MCI_wNextDeviceID;
// Try to reclaim any excess free space
if (MCI_wDeviceListSize - MCI_wNextDeviceID + 1
> MCI_DEVICE_LIST_GROW_SIZE)
{
MCI_wDeviceListSize -= MCI_DEVICE_LIST_GROW_SIZE;
if ((lpTempList =
mciReAlloc (MCI_lpDeviceList, sizeof (LPMCI_DEVICE_NODE) *
MCI_wDeviceListSize)) == NULL)
MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
else
MCI_lpDeviceList = lpTempList;
}
}
mciLeave("mciFreeDevice");
}
typedef struct tagNotificationMsg {
WPARAM wParam;
LPARAM lParam;
} NOTIFICATIONMSG;
/*
* @doc INTERNAL MCI
* @api void | FilterNotification | Removes notifications for a given node
* from our notification window's message queue
*
* @parm LPMCI_DEVICE_NODE | nodeWorking | The internal device node
*
* @comm This function removes all MM_MCINOTIFY messages from hwndNotify's
* message queue by removing all notifications for devices that have been
* closed (i.e. do not belong to us), then putting the others back
*/
void FilterNotification(
LPMCI_DEVICE_NODE nodeWorking)
{
NOTIFICATIONMSG anotmsg[256];
UINT uCurrentMsg;
MSG msg;
/* We can't have the mci critical section on here because this PeekMessage
will dispatch other messages in the queue */
uCurrentMsg = 0;
while (PeekMessage(&msg, hwndNotify, MM_MCINOTIFY, MM_MCINOTIFY, PM_NOYIELD | PM_REMOVE)) {
if (LOWORD(msg.lParam) != nodeWorking->wDeviceID) {
anotmsg[uCurrentMsg].wParam = msg.wParam;
anotmsg[uCurrentMsg].lParam = msg.lParam;
uCurrentMsg++;
}
}
for (; uCurrentMsg;) {
uCurrentMsg--;
PostMessage(hwndNotify, MM_MCINOTIFY, anotmsg[uCurrentMsg].wParam, anotmsg[uCurrentMsg].lParam);
}
}
/*
* @doc INTERNAL MCI
* @api UINT | mciCloseDevice | Close an MCI device. Used in
* processing the MCI_CLOSE message.
*
* @parm MCIDEVICEID | uID | 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_DEVICE message to the corresponding
* driver if the use count is zero and then unloads the driver DLL
*
*/
UINT mciCloseDevice (
MCIDEVICEID uID,
DWORD dwFlags,
LPMCI_GENERIC_PARMS lpGeneric,
BOOL bCloseDriver)
{
LPMCI_DEVICE_NODE nodeWorking;
UINT wErr;
UINT wTable;
mciEnter("mciCloseDevice");
nodeWorking = MCI_lpDeviceList[uID];
if (nodeWorking == NULL)
{
mciLeave("mciCloseDevice");
dprintf1(("mciCloseDevice: NULL node from device ID--error if not auto-close"));
return 0;
}
// We should never be closed from the wrong task
#if 0
WinAssert(nodeWorking->hCreatorTask == GetCurrentTask());
#endif
// If a close is in progress (usually this message comes from a Yield
// after a mciDriverNotify actuated by the active close) then exit
if (ISCLOSING(nodeWorking)) {
mciLeave("mciCloseDevice");
return 0;
}
SETISCLOSING(nodeWorking);
if (bCloseDriver)
{
MCI_GENERIC_PARMS GenericParms;
mciLeave("mciCloseDevice");
// Make fake generic params if close came internally
if (lpGeneric == NULL) {
lpGeneric = &GenericParms;
}
wErr = LOWORD(mciSendCommandW(uID, MCI_CLOSE_DRIVER, dwFlags,
(DWORD_PTR)lpGeneric));
mciEnter("mciCloseDevice");
}
else
wErr = 0;
wTable = nodeWorking->wCustomCommandTable;
//
// Must zero this to allow the table to be freed later by driver
//
// We mustn't call mciFreeCommandResource for the custom table
// because the driver is going to do that when it gets DRV_FREE
//
nodeWorking->wCustomCommandTable = 0;
wTable = nodeWorking->wCommandTable;
nodeWorking->wCommandTable = 0;
mciLeave("mciCloseDevice");
mciFreeCommandResource (wTable);
//
// We're closing this node so remove any notifications queued to
// hwndNotify because these would cause this node to be erroneously
// closed again
//
if (ISAUTOOPENED(nodeWorking)) {
FilterNotification(nodeWorking);
}
DrvClose (nodeWorking->hDrvDriver, 0L, 0L); // ala CloseDriver
mciFreeDevice (nodeWorking);
return wErr;
}
/*
* @doc INTERNAL MCI DDK
* @api DWORD | mciGetDriverData | Returns a pointer to the instance
* data associated with an MCI device
*
* @parm MCIDEVICEID | 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)
*
* @xref mciSetDriverData
*/
DWORD_PTR mciGetDriverData (
MCIDEVICEID wDeviceID)
{
DWORD_PTR lpDriverData;
mciEnter("mciGetDriverData");
if (!MCI_VALID_DEVICE_ID(wDeviceID))
{
dprintf1(("mciGetDriverData: invalid device ID"));
lpDriverData = 0;
} else {
if (NULL == MCI_lpDeviceList[wDeviceID])
{
dprintf1(("mciGetDriverData: NULL node from device ID"));
lpDriverData = 0;
} else {
lpDriverData = MCI_lpDeviceList[wDeviceID]->lpDriverData;
}
}
mciLeave("mciGetDriverData");
return lpDriverData;
}
/*
* @doc INTERNAL MCI DDK
* @api BOOL | mciSetDriverData | Sets the instance
* data associated with an MCI device
*
* @parm MCIDEVICEID | uDeviceID | The MCI device ID
*
* @parm DWORD | dwData | Driver data to set
*
* @rdesc 0 if the device ID is not known or there is insufficient
* memory to load the device description.
*
*/
BOOL mciSetDriverData (
MCIDEVICEID wDeviceID,
DWORD_PTR dwData)
{
BOOL fReturn = TRUE;
mciEnter("mciSetDriverData");
if (!MCI_VALID_DEVICE_ID(wDeviceID))
{
dprintf1(("mciSetDriverData: NULL node from device ID"));
fReturn = FALSE;
} else {
MCI_lpDeviceList[wDeviceID]->lpDriverData = dwData;
}
mciLeave("mciSetDriverData");
return fReturn;
}
/*
* @doc INTERNAL MCI DDK
* @api UINT | mciDriverYield | Used in a driver's idle loop
* to yield to Windows
*
* @parm MCIDEVICEID | wDeviceID | Device ID that is yielding.
*
* @rdesc Non-zero if the driver should abort the operation.
*
*/
UINT mciDriverYield (
MCIDEVICEID wDeviceID)
{
mciEnter("mciDriverYield");
if (MCI_VALID_DEVICE_ID(wDeviceID))
{
YIELDPROC YieldProc = (MCI_lpDeviceList[wDeviceID])->fpYieldProc;
if (YieldProc != NULL) {
DWORD YieldData = (MCI_lpDeviceList[wDeviceID])->dwYieldData;
mciLeave("mciDriverYield");
mciCheckOut();
return (YieldProc)(wDeviceID, YieldData);
}
}
mciLeave("mciDriverYield");
Yield();
return 0;
}
/*
* @doc EXTERNAL MCI
* @api BOOL | mciSetYieldProc | This function sets the address
* of a procedure to be called periodically
* when an MCI device is waiting for a command to complete because the WAIT
* parameter was specified.
*
* @parm MCIDEVICEID | wDeviceID | Specifies the device ID to assign a procedure to.
*
* @parm YIELDPROC | fpYieldProc | Specifies the procedure to call
* when yielding for the given device. Set to NULL to disable
* any existing yield proc.
*
* @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.
*
* @comm This call overides any previous yield procedure for this device.
*
*/
BOOL APIENTRY mciSetYieldProc (
MCIDEVICEID wDeviceID,
YIELDPROC fpYieldProc,
DWORD dwYieldData)
{
BOOL fReturn = FALSE;
mciEnter("mciSetYieldProc");
if (MCI_VALID_DEVICE_ID(wDeviceID))
{
LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
node->fpYieldProc = fpYieldProc;
node->dwYieldData = dwYieldData;
fReturn = TRUE;
} else
fReturn = FALSE;
mciLeave("mciSetYieldProc");
return fReturn;
}
/*
* @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)
{
YIELDPROC fpYieldProc;
mciEnter("mciGetYieldProc");
if (MCI_VALID_DEVICE_ID(wDeviceID))
{
if (lpdwYieldData != NULL) {
V_WPOINTER(lpdwYieldData, sizeof(DWORD), NULL);
*lpdwYieldData = MCI_lpDeviceList[wDeviceID]->dwYieldData;
}
fpYieldProc = MCI_lpDeviceList[wDeviceID]->fpYieldProc;
} else {
fpYieldProc = NULL;
}
mciLeave("mciGetYieldProc");
return fpYieldProc;
}
/*
* @doc INTERNAL MCI
* @api int | mciBreakKeyYieldProc | Procedure called to check a
* key state for the given device
*
* @parm MCIDEVICEID | 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.
*
*/
UINT mciBreakKeyYieldProc (
MCIDEVICEID wDeviceID,
DWORD dwYieldData)
{
HWND hwndCheck = NULL;
int nVirtKey, nState;
nVirtKey = dwYieldData;
UNREFERENCED_PARAMETER(wDeviceID);
nState = GetAsyncKeyState (nVirtKey);
// Break if key is down or has been down
if (nState & 1 /* used to be 0x8000*/ )
{
MSG msg;
while (PeekMessage (&msg, hwndCheck, WM_KEYFIRST, WM_KEYLAST,
PM_REMOVE));
return MCI_ERROR_VALUE;
}
Yield();
return 0;
}
/*
* @doc INTERNAL MCI
* @api UINT FAR | mciSetBreakKey | Set a key which will break a wait loop
* for a given driver
*
* @parm UINT | uDeviceID | 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 FAR mciSetBreakKey (
MCIDEVICEID wDeviceID,
int nVirtKey,
HWND hwndTrap)
{
dprintf2(("Setting break key for device %d to %x", wDeviceID, nVirtKey));
return mciSetYieldProc (wDeviceID, mciBreakKeyYieldProc, nVirtKey);
// Note: we have no way of passing hwndTrap... will check all windows
// on this thread of the application
}
/*
* @doc INTERNAL MCI
* @api BOOL | mciDriverNotify | Used by a driver to send
* a notification message
*
* @parm HANDLE | hCallback | The window to notify
*
* @parm UINT | wDeviceID | The device ID which triggered the callback
*
* @parm UINT | wStatus | The status of the callback. May be one of
* MCI_NOTIFY_SUCCESSFUL or MCI_NOTIFY_SUPERSEDED or MCI_NOTIFY_ABORTED or
* MCI_NOTIFY_FAILURE
*
* @rdesc returns TRUE if notify was successfully sent, FALSE otherwise.
*
* @comm This function is callable at interrupt time
*
*/
BOOL mciDriverNotify (
HANDLE hCallback,
MCIDEVICEID wDeviceID,
UINT uStatus)
{
BOOL f;
#if DBG
// IsWindow() is in segment marked PRELOAD for WIN3.0 so OK at interrupt time
if (hCallback != NULL && !IsWindow(hCallback))
{
dprintf1(("mciDriverNotify: invalid window!"));
return FALSE;
}
#endif
f = PostMessage(hCallback, MM_MCINOTIFY, uStatus, wDeviceID);
#if DBG
if (!f)
dprintf1(("mciDriverNotify: PostMessage failed!"));
#endif
return f;
}