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

parameter. * * @rdesc Return zero to continue the operation. To cancel the operation, * return a nonzero value. * * @comm This call overrides any previous yield procedure for this device. * */ BOOL WINAPI mciSetYieldProc ( UINT wDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData) { V_CALLBACK((FARPROC)fpYieldProc, FALSE); if (Is16bitDrv(wDeviceID)) { LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID]; node->fpYieldProc = fpYieldProc; node->dwYieldData = dwYieldData; return TRUE; } return (BOOL)mciMessage( THUNK_MCI_SETYIELDPROC, (DWORD)wDeviceID, (DWORD)fpYieldProc, dwYieldData, 0L ); } /* * @doc EXTERNAL MCI * @api YIELDPROC | mciGetYieldProc | This function gets the address * of the callback procedure to be called periodically when an MCI device * is completing a command specified with the WAIT flag. * * @parm UINT | wDeviceID | Specifies the device ID of the MCI device to * which the yield procedure is to be retrieved from. * * @parm LPDWORD | lpdwYieldData | Optionally specifies a buffer to place * the yield data passed to the function in. If the parameter is NULL, it * is ignored. * * @rdesc Returns the current yield proc if any, else returns NULL for an * invalid device ID. * */ YIELDPROC WINAPI mciGetYieldProc ( UINT wDeviceID, LPDWORD lpdwYieldData) { /* ** Is this a 16 bit device ID ? */ if (Is16bitDrv(wDeviceID)) { if (lpdwYieldData != NULL) { V_WPOINTER(lpdwYieldData, sizeof(DWORD), NULL); *lpdwYieldData = MCI_lpDeviceList[wDeviceID]->dwYieldData; } return MCI_lpDeviceList[wDeviceID]->fpYieldProc; } /* ** No, so pass it on to the 32 bit code. */ return (YIELDPROC)mciMessage( THUNK_MCI_GETYIELDPROC, (DWORD)wDeviceID, (DWORD)lpdwYieldData, 0L, 0L ); } /* * @doc INTERNAL MCI * @api int | mciBreakKeyYieldProc | Procedure called to check a * key state for the given device * * @parm UINT | wDeviceID | Device ID which is yielding * * @parm DWORD | dwYieldData | Data for this device's yield proc * * @rdesc Non-zero if the driver should abort the operation. Currently * always returns 0. * */ int CALLBACK mciBreakKeyYieldProc ( UINT wDeviceID, DWORD dwYieldData) { HWND hwndCheck; int nState; hwndCheck = (HWND)HIWORD (dwYieldData); if (hwndCheck == NULL || hwndCheck == GetActiveWindow()) { nState = GetAsyncKeyState (LOWORD(dwYieldData)); // Break if key is down or has been down if (nState & 1) { MSG msg; while (PeekMessage (&msg, hwndCheck, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); return -1; } } Yield(); return 0; } /* * @doc INTERNAL MCI * @func UINT | mciSetBreakKey | Set a key which will break a wait loop * for a given driver * * @parm UINT | wDeviceID | The device ID to assign a break key to * * @parm int | nVirtKey | Virtual key code to trap * * @parm HWND | hwndTrap | The handle to a window that must be active * for the key to be trapped. If NULL then all windows will be checked * * @rdesc TRUE if successful, FALSE if invalid device ID * */ UINT PASCAL NEAR mciSetBreakKey ( UINT wDeviceID, int nVirtKey, HWND hwndTrap) { return mciSetYieldProc (wDeviceID, mciBreakKeyYieldProc, MAKELONG (nVirtKey, hwndTrap)); }