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