/********************************************************************** Copyright (c) 1992-1999 Microsoft Corporation mididev.c DESCRIPTION: Code to match device ID's with associated registry entries HISTORY: 02/24/95 [jimge] created. *********************************************************************/ #include #include #include #include #include #include #include "idf.h" #include "midimap.h" #include "debug.h" typedef struct tagMDEV_NODE *PMDEV_NODE; typedef struct tagMDEV_NODE { PMDEV_NODE pNext; TCHAR szAlias[CB_MAXALIAS]; DWORD dwDevNode; TCHAR szDriver[CB_MAXDRIVER]; UINT uDeviceID; UINT uPort; BOOL fNewDriver; } MDEV_NODE; static TCHAR BCODE gszMediaRsrcKey[] = REGSTR_PATH_MEDIARESOURCES TEXT ("\\MIDI"); static TCHAR BCODE gszDriverKey[] = REGSTR_PATH_MEDIARESOURCES TEXT ("\\MIDI\\%s"); static TCHAR BCODE gszDriverValue[] = TEXT ("Driver"); static TCHAR BCODE gszDevNodeValue[] = TEXT ("DevNode"); static TCHAR BCODE gszPortValue[] = TEXT ("Port"); static TCHAR BCODE gszActiveValue[] = TEXT ("Active"); static TCHAR BCODE gszMapperConfig[] = TEXT ("MapperConfig"); static PMDEV_NODE gpMDevList = NULL; static DWORD gdwNewDrivers = (DWORD)-1L; PRIVATE BOOL FNLOCAL mdev_BuildRegList( void); PRIVATE BOOL FNLOCAL mdev_SyncDeviceIDs( void); PRIVATE BOOL FNLOCAL mdev_MarkActiveDrivers( void); #ifdef DEBUG PRIVATE VOID FNLOCAL mdev_ListActiveDrivers( void); #endif BOOL FNGLOBAL mdev_Init( void) { if (gpMDevList) mdev_Free(); if ((!mdev_BuildRegList()) || (!mdev_SyncDeviceIDs()) || (!mdev_MarkActiveDrivers())) { mdev_Free(); return FALSE; } #ifdef DEBUG mdev_ListActiveDrivers(); #endif return TRUE; } // // mdev_BuildRegList // // Builds the base device list out of the registry // // Assumes the list has been cleared // // For each alias (key) under MediaResources\MIDI // Make sure the Active value exists and is '1' // Allocate a list node // Try to read the alias's devnode // If the alias's devnode is 0 or missing, // Read the alias's driver name // Read the alias's port number // Add the alias to the global list // // The uDeviceID member will not be initialized by this routine; // mdev_SyncDeviceIDs must be called to figure out the current // device ID mapping. // PRIVATE BOOL FNLOCAL mdev_BuildRegList( void) { BOOL fRet = FALSE; HKEY hKeyMediaRsrc = NULL; HKEY hKeyThisAlias = NULL; DWORD dwEnumAlias = 0; LPTSTR pstrAlias = NULL; PMDEV_NODE pmd = NULL; TCHAR szActive[2]; DWORD dwPort; DWORD cbValue; DWORD dwType; DWORD dwMapperConfig; cbValue = CB_MAXALIAS * sizeof(TCHAR); pstrAlias = (LPTSTR)LocalAlloc(LPTR, CB_MAXALIAS * sizeof(TCHAR)); if (NULL == pstrAlias) { DPF(1, TEXT ("mdev_Init: Out of memory")); goto mBRL_Cleanup; } if (ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE, gszMediaRsrcKey, &hKeyMediaRsrc)) { DPF(1, TEXT ("mdev_Init: Could not open ...\\MediaResoruces\\MIDI")); goto mBRL_Cleanup; } while (ERROR_SUCCESS == RegEnumKey(hKeyMediaRsrc, dwEnumAlias++, pstrAlias, CB_MAXALIAS)) { if (ERROR_SUCCESS != (RegOpenKey(hKeyMediaRsrc, pstrAlias, &hKeyThisAlias))) { DPF(1, TEXT ("mdev_Init: Could not open enum'ed key %s"), (LPTSTR)pstrAlias); continue; } // MUST have Active == "1" to be running // cbValue = sizeof(szActive); if (ERROR_SUCCESS != (RegQueryValueEx(hKeyThisAlias, gszActiveValue, NULL, &dwType, (LPSTR)szActive, &cbValue)) || *szActive != '1') { DPF(2, TEXT ("mdev_Init: Device %s exists but is not loaded."), (LPTSTR)pstrAlias); RegCloseKey(hKeyThisAlias); continue; } // Determine if we have ever configured with this driver before // cbValue = sizeof(dwMapperConfig); if (ERROR_SUCCESS != (RegQueryValueEx(hKeyThisAlias, gszMapperConfig, NULL, &dwType, (LPSTR)&dwMapperConfig, &cbValue))) dwMapperConfig = 0; #ifdef DEBUG if (!dwMapperConfig) DPF(1, TEXT ("Alias '%s' is a new driver."), (LPTSTR)pstrAlias); #endif // We have a running driver, go ahead and alloc a node // for it // pmd = (PMDEV_NODE)LocalAlloc(LPTR, sizeof(*pmd)); if (NULL == pmd) { DPF(1, TEXT ("mdev_Init: Out of memory allocating node for %s"), (LPTSTR)pstrAlias); RegCloseKey(hKeyThisAlias); continue; } lstrcpyn(pmd->szAlias, pstrAlias, (sizeof(pmd->szAlias)/sizeof(pmd->szAlias[0])) - 1); pmd->szAlias[(sizeof(pmd->szAlias)/sizeof(pmd->szAlias[0])) - 1] = '\0'; pmd->fNewDriver = (dwMapperConfig ? FALSE : TRUE); // Try to get the DevNode value // cbValue = sizeof(pmd->dwDevNode); if (ERROR_SUCCESS != RegQueryValueEx(hKeyThisAlias, gszDevNodeValue, NULL, &dwType, (LPSTR)(LPDWORD)&pmd->dwDevNode, &cbValue)) { // Ok to not have a devnode value, 3.1 drivers don't // DPF(2, TEXT ("mdev_Init: Device %s has no devnode; must be 3.1"), (LPTSTR)pstrAlias); pmd->dwDevNode = 0; } // Leave something reasonable in driver even if we don't // expect to use it // *pmd->szDriver = '\0'; // If we didn't get a devnode or it was 0, and we can't find the // driver name to match against, we can't use this entry. (If it // has no ring 3 driver, it can't be running anyway). // if (!pmd->dwDevNode) { cbValue = sizeof(pmd->szDriver); if (ERROR_SUCCESS != RegQueryValueEx( hKeyThisAlias, gszDriverValue, NULL, &dwType, (LPSTR)pmd->szDriver, &cbValue)) { DPF(1, TEXT ("mdev_Init: Device %s has no ring 3 driver entry"), (LPTSTR)pstrAlias); LocalFree((HLOCAL)pmd); RegCloseKey(hKeyThisAlias); continue; } } // Success! Now try to figure out the port number // cbValue = sizeof(dwPort); // Guard against INF's which only specify a byte's worth of // port value // dwPort = 0; if (ERROR_SUCCESS != RegQueryValueEx(hKeyThisAlias, gszPortValue, NULL, &dwType, (LPSTR)(LPDWORD)&dwPort, &cbValue)) { DPF(2, TEXT ("mdev_Init: Device %s has no port entry; using 0."), (LPTSTR)pstrAlias); dwPort = 0; } pmd->uPort = (UINT)dwPort; // We have a valid node, put it into the list // pmd->pNext = gpMDevList; gpMDevList = pmd; RegCloseKey(hKeyThisAlias); } fRet = TRUE; mBRL_Cleanup: if (hKeyMediaRsrc) RegCloseKey(hKeyMediaRsrc); if (pstrAlias) LocalFree((HLOCAL)pstrAlias); return fRet; } // // mdev_SyncDeviceIDs // // Traverse the device list and bring the uDeviceID members up to date. // Also remove any devices which MMSYSTEM claims are not really running. // // NOTE: The uDeviceID member is actually the device ID of the base driver. // If you want to open the device, you have to add uDeviceID and uPort for // the node you want to open. // // Set all uDeviceID's to NO_DEVICEID // // For each base device ID in MMSYSTEM (i.e. port 0 on each loaded driver) // Get the matching alias from MMSYSTEM // Locate the node with that alias in the device list // Set that node's uDeviceID // // For each node in the device list with non-zero port // If this node has a DevNode // Find a matching node by DevNode with port == 0 and get its device ID // else // Find a matching node by driver name with port == 0 and get its device ID // // NOTE: We match by driver name on DevNode == 0 (3.1 devices) because it // isn't possible to have multiple instances of a 3.1 driver loaded. // // For each node in the device list, // If the node's uDeviceID is still not set, // Remove and free the node // PRIVATE BOOL FNLOCAL mdev_SyncDeviceIDs( void) { BOOL fRet = FALSE; LPTSTR pstrAlias = NULL; PMDEV_NODE pmdCurr; PMDEV_NODE pmdPrev; PMDEV_NODE pmdEnum; UINT cDev; UINT idxDev; DWORD cPort; MMRESULT mmr; DWORD cbSize; cbSize = CB_MAXALIAS * sizeof(TCHAR); pstrAlias = (LPTSTR)LocalAlloc(LPTR, cbSize); if (NULL == pstrAlias) { goto mSDI_Cleanup; } // The device list has been built and the uPort member is valid. // Now update the uDeviceID field to be proper. First, walk the list // and set them all to NO_DEVICEID. for (pmdCurr = gpMDevList; pmdCurr; pmdCurr = pmdCurr->pNext) pmdCurr->uDeviceID = NO_DEVICEID; // Now walk MMSYSTEM's list of loaded drivers and fill in all the port 0 // nodes with their proper device ID // cDev = midiOutGetNumDevs(); for (idxDev = 0; idxDev < cDev; ) { mmr = (MMRESULT)midiOutMessage((HMIDIOUT)(UINT_PTR)idxDev, #ifdef WINNT DRV_QUERYNUMPORTS, #else MODM_GETNUMDEVS, #endif // End WINNT (DWORD_PTR)(LPDWORD)&cPort, 0); if (mmr) { DPF(1, TEXT ("mdev_Sync: Device ID %u returned %u for MODM_GETNUMDEVS"), (UINT)idxDev, (UINT)mmr); ++idxDev; continue; } mmr = (MMRESULT)midiOutMessage((HMIDIOUT)(UINT_PTR)idxDev, DRV_QUERYDRVENTRY, #ifdef WINNT (DWORD_PTR)(LPTSTR)pstrAlias, #else (DWORD_PTR)(LPTSTR)pstrPath, #endif // End Winnt CB_MAXALIAS); if (!mmr) { for (pmdCurr = gpMDevList; pmdCurr; pmdCurr = pmdCurr->pNext) { if ((0 == pmdCurr->uPort) && (! lstrcmpi(pstrAlias, pmdCurr->szAlias))) { pmdCurr->uDeviceID = idxDev; break; } } #ifdef DEBUG if (!pmdCurr) { DPF(1, TEXT ("mdev_Sync: Device ID %u not found in device list."), (UINT)idxDev); } #endif } else { DPF(1, TEXT ("mdev_Sync: Device ID %u returned %u for DRV_QUERYDRVENTRY"), (UINT)idxDev, (UINT)mmr); } idxDev += (UINT)cPort; } // Now walk the list again. This time we catch all the non-zero ports // and set their uDeviceID properly. // for (pmdCurr = gpMDevList; pmdCurr; pmdCurr = pmdCurr->pNext) { if (!pmdCurr->uPort) continue; if (pmdCurr->dwDevNode) { for (pmdEnum = gpMDevList; pmdEnum; pmdEnum = pmdEnum->pNext) if (0 == pmdEnum->uPort && pmdEnum->dwDevNode == pmdCurr->dwDevNode) { pmdCurr->uDeviceID = pmdEnum->uDeviceID; break; } } else { for (pmdEnum = gpMDevList; pmdEnum; pmdEnum = pmdEnum->pNext) if (0 == pmdEnum->uPort && !lstrcmpi(pmdEnum->szDriver, pmdCurr->szDriver)) { pmdCurr->uDeviceID = pmdEnum->uDeviceID; break; } } #ifdef DEBUG if (!pmdEnum) { DPF(1, TEXT ("mdev_Sync: No parent driver found for %s"), (LPTSTR)pmdCurr->szAlias); } #endif } // Now we walk the list one more time and discard anyone without a device // ID assigned. // pmdPrev = NULL; pmdCurr = gpMDevList; while (pmdCurr) { if (NO_DEVICEID == pmdCurr->uDeviceID) { DPF(1, TEXT ("mdev_Sync: Removing %s; never found a device ID"), (LPTSTR)pmdCurr->szAlias); if (pmdPrev) pmdPrev->pNext = pmdCurr->pNext; else gpMDevList = pmdCurr->pNext; LocalFree((HLOCAL)pmdCurr); pmdCurr = (pmdPrev ? pmdPrev->pNext : gpMDevList); } else { pmdPrev = pmdCurr; pmdCurr = pmdCurr->pNext; } } fRet = TRUE; mSDI_Cleanup: if (pstrAlias) LocalFree((HLOCAL)pstrAlias); return fRet; } // // mdev_MarkActiveDrivers // // Mark drivers which are loaded and have not been seen before by // mapper configuration as seen. Also flag that we want to run // RunOnce if there are any of these // PRIVATE BOOL FNLOCAL mdev_MarkActiveDrivers( void) { BOOL fRet = FALSE; HKEY hKeyMediaRsrc = NULL; HKEY hKeyThisAlias = NULL; DWORD dwMapperConfig; PMDEV_NODE pmd; if (ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE, gszMediaRsrcKey, &hKeyMediaRsrc)) { DPF(1, TEXT ("mdev_MarkActiveDrivers: Could not open ") TEXT ("...\\MediaResources\\MIDI")); goto mMAD_Cleanup; } gdwNewDrivers = 0; for (pmd = gpMDevList; pmd; pmd = pmd->pNext) if (pmd->fNewDriver) { ++gdwNewDrivers; // Mark this driver as seen // if (ERROR_SUCCESS != (RegOpenKey(hKeyMediaRsrc, pmd->szAlias, &hKeyThisAlias))) { DPF(1, TEXT ("mdev_MarkActiveDrivers: Could not open alias '%s'"), (LPTSTR)pmd->szAlias); goto mMAD_Cleanup; } dwMapperConfig = 1; RegSetValueEx(hKeyThisAlias, gszMapperConfig, 0, REG_DWORD, (LPSTR)&dwMapperConfig, sizeof(dwMapperConfig)); RegCloseKey(hKeyThisAlias); } fRet = TRUE; mMAD_Cleanup: if (hKeyMediaRsrc) RegCloseKey(hKeyMediaRsrc); return fRet; } // // mdev_ListActiveDrivers // // List the currently loaded drivers to debug output // #ifdef DEBUG PRIVATE VOID FNLOCAL mdev_ListActiveDrivers( void) { PMDEV_NODE pmd; static TCHAR BCODE szNo[] = TEXT ("No"); static TCHAR BCODE szYes[] = TEXT ("Yes"); DPF(2, TEXT ("=== mdev_ListActiveDrivers start ===")); for (pmd = gpMDevList; pmd; pmd = pmd->pNext) { DPF(2, TEXT ("Alias %-31.31s Driver %-31.31s"), (LPTSTR)pmd->szAlias, (LPTSTR)pmd->szDriver); DPF(2, TEXT (" dwDevNode %08lX uDeviceID %u uPort %u fNewDriver %s"), pmd->dwDevNode, pmd->uDeviceID, pmd->uPort, (LPTSTR)(pmd->fNewDriver ? szYes : szNo)); } DPF(2, TEXT ("=== mdev_ListActiveDrivers end ===")); } #endif // // mdev_Free // // Discard the current device list // void FNGLOBAL mdev_Free( void) { PMDEV_NODE pmdNext; PMDEV_NODE pmdCurr; pmdCurr = gpMDevList; while (pmdCurr) { pmdNext = pmdCurr->pNext; LocalFree((HLOCAL)pmdCurr); pmdCurr = pmdNext; } gpMDevList = NULL; gdwNewDrivers = (DWORD)-1L; } // // mdev_GetDeviceID // // Get the current device ID for the given alias. // UINT FNGLOBAL mdev_GetDeviceID( LPTSTR lpstrAlias) { PMDEV_NODE pmd; for (pmd = gpMDevList; pmd; pmd = pmd->pNext) if (!lstrcmpi(pmd->szAlias, lpstrAlias)) return pmd->uDeviceID + pmd->uPort; DPF(1, TEXT ("mdev_GetDeviceID: Failed for %s"), lpstrAlias); return NO_DEVICEID; } // // mdev_GetAlias // // Get the registry alias for the requested device ID // BOOL FNGLOBAL mdev_GetAlias( UINT uDeviceID, LPTSTR lpstrBuffer, UINT cbBuffer) { PMDEV_NODE pmd; for (pmd = gpMDevList; pmd; pmd = pmd->pNext) if (uDeviceID == (pmd->uDeviceID + pmd->uPort)) { lstrcpyn(lpstrBuffer, pmd->szAlias, cbBuffer); return TRUE; } DPF(1, TEXT ("mdev_GetAlias: Failed for device ID %u"), uDeviceID); return FALSE; } // // mdev_NewDrivers // // Returns TRUE if there were new drivers in the registry that we've never // encountered before // BOOL FNGLOBAL mdev_NewDrivers( void) { if (gdwNewDrivers == (DWORD)-1L) { DPF(0, TEXT ("mdevNewDrivers() called before mdev_Init()!")); return FALSE; } return (BOOL)(gdwNewDrivers != 0); }