|
|
/*****************************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; }
|