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

2880 lines
83 KiB
C

/****************************************************************************\
*
* Module Name : winmm.c
*
* Multimedia support library
*
* This module contains the entry point, startup and termination code
*
* Copyright (c) 1991-2001 Microsoft Corporation
*
\****************************************************************************/
#define UNICODE
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "winmmi.h"
#include "mmioi.h"
#include "mci.h"
#include <regstr.h>
#include <winuser.h>
#include <wtsapi32.h>
#include <dbt.h>
#include <ks.h>
#include <ksmedia.h>
#include <winsta.h>
#include <stdlib.h>
#include "winuserp.h"
#include "audiosrvc.h"
#include "agfxp.h"
#define _INC_WOW_CONVERSIONS
#include "mmwow32.h"
BOOL WaveInit(void);
BOOL MidiInit(void);
BOOL AuxInit(void);
BOOL MixerInit(void);
void InitDevices(void);
HANDLE mmDrvOpen(LPWSTR szAlias);
void WOWAppExit(HANDLE hTask);
void MigrateSoundEvents(void);
UINT APIENTRY mmDrvInstall(HANDLE hDriver, WCHAR * wszDrvEntry, DRIVERMSGPROC drvMessage, UINT wFlags);
STATIC void NEAR PASCAL mregAddIniScheme(LPTSTR lszSection,
LPTSTR lszSchemeID,
LPTSTR lszSchemeName,
LPTSTR lszINI);
STATIC void NEAR PASCAL mregCreateSchemeID(LPTSTR szSchemeName, LPTSTR szSchemeID);
int lstrncmpi (LPTSTR pszA, LPTSTR pszB, size_t cch);
void RemoveMediaPath (LPTSTR pszTarget, LPTSTR pszSource);
MMRESULT waveOutDesertHandle(HWAVEOUT hWaveOut);
MMRESULT waveInDesertHandle(HWAVEIN hWaveIn);
MMRESULT midiOutDesertHandle(HMIDIOUT hMidiOut);
MMRESULT midiInDesertHandle(HMIDIIN hMidiIn);
MMRESULT mixerDesertHandle(HMIXER hmx);
#ifndef cchLENGTH
#define cchLENGTH(_sz) (sizeof(_sz) / sizeof(_sz[0]))
#endif
/****************************************************************************
global data
****************************************************************************/
HANDLE ghInst; // Module handle
BOOL gfDisablePreferredDeviceReordering = FALSE;
BOOL WinmmRunningInServer; // Are we running in the user/base server?
BOOL WinmmRunningInWOW; // Are we running in WOW
BOOL WinmmRunningInSession; // Are we running in remote session
WCHAR SessionProtocolName[WPROTOCOLNAME_LENGTH];
// |
// The tls is used simply as an indication that the thread has entered
// waveOutOpen or waveOutGetDevCaps for a non-mapper device. Then, we detect
// re-entrancy into either of these APIs. In the case of re-entrancy we
// might have a driver that is enumerating and caching device IDs. To improve
// the chances of such a driver working, we disable preferred device
// reordering in this case. Note we rely on the OS to initialize the tls to 0.
//
DWORD gTlsIndex = TLS_OUT_OF_INDEXES; // Thread local storage index;
CRITICAL_SECTION DriverListCritSec; // Protect driver interface globals
CRITICAL_SECTION DriverLoadFreeCritSec; // Protect driver load/unload
CRITICAL_SECTION NumDevsCritSec; // Protect Numdevs/Device ID's
CRITICAL_SECTION MapperInitCritSec; // Protect test of mapper initialized
HANDLE hClientPnpInfo = NULL;
PMMPNPINFO pClientPnpInfo = NULL;
CRITICAL_SECTION PnpCritSec;
RTL_RESOURCE gHandleListResource; // Serializes access to handles.
BOOL gfLogon = FALSE;
HANDLE hEventApiInit = NULL;
WAVEDRV waveoutdrvZ; // wave output device driver list head
WAVEDRV waveindrvZ; // wave input device driver list head
MIDIDRV midioutdrvZ; // midi output device driver list
MIDIDRV midiindrvZ; // midi input device driver list
AUXDRV auxdrvZ; // aux device driver list
UINT wTotalMidiOutDevs; // total midi output devices
UINT wTotalMidiInDevs; // total midi input devices
UINT wTotalWaveOutDevs; // total wave output devices
UINT wTotalWaveInDevs; // total wave input devices
UINT wTotalAuxDevs; // total auxiliary output devices
LONG cPnpEvents; // number of processed pnp events
LONG cPreferredDeviceChanges = 0; // number of processed preferred device changes
typedef struct tag_wdmdeviceinterface *PWDMDEVICEINTERFACE;
typedef struct tag_wdmdeviceinterface
{
PWDMDEVICEINTERFACE Next;
DWORD cUsage;
LONG cPnpEvents;
WCHAR szDeviceInterface[0];
} WDMDEVICEINTERFACE, *PWDMDEVICEINTERFACE;
WDMDEVICEINTERFACE wdmDevZ;
LPCRITICAL_SECTION acs[] = {
&HandleListCritSec,
&DriverListCritSec,
&DriverLoadFreeCritSec,
&MapperInitCritSec,
&NumDevsCritSec,
&PnpCritSec,
&WavHdrCritSec,
&SoundCritSec,
&midiStrmHdrCritSec,
&joyCritSec,
&mciGlobalCritSec,
&mciCritSec,
&TimerThreadCritSec,
&ResolutionCritSec
};
// HACK!!!
SERVICE_STATUS_HANDLE hss;
SERVICE_STATUS gss;
#ifdef DEBUG_RETAIL
BYTE fIdReverse; // reverse wave/midi id's
#endif
// For sounds:
STATIC TCHAR gszControlIniTime[] = TEXT("ControlIniTimeStamp");
TCHAR gszControlPanel[] = TEXT("Control Panel");
TCHAR gszSchemesRootKey[] = TEXT("AppEvents\\Schemes");
TCHAR gszJustSchemesKey[] = TEXT("Schemes");
TCHAR aszExplorer[] = TEXT("Explorer");
TCHAR aszDefault[] = TEXT(".Default");
TCHAR aszCurrent[] = TEXT(".Current");
TCHAR gszAppEventsKey[] = TEXT("AppEvents");
TCHAR gszSchemeAppsKey[] = TEXT("Apps");
TCHAR aszSoundsSection[] = TEXT("Sounds");
TCHAR aszSoundSection[] = TEXT("Sound");
TCHAR aszActiveKey[] = TEXT("Active");
TCHAR aszBoolOne[] = TEXT("1");
TCHAR asz2Format[] = TEXT("%s\\%s");
TCHAR asz3Format[] = TEXT("%s\\%s\\%s");
TCHAR asz4Format[] = TEXT("%s\\%s\\%s\\%s");
TCHAR asz5Format[] = TEXT("%s\\%s\\%s\\%s\\%s");
TCHAR asz6Format[] = TEXT("%s\\%s\\%s\\%s\\%s\\%s");
STATIC TCHAR aszSchemeLabelsKey[] = TEXT("EventLabels");
STATIC TCHAR aszSchemeNamesKey[] = TEXT("Names");
STATIC TCHAR aszControlINI[] = TEXT("control.ini");
STATIC TCHAR aszWinINI[] = TEXT("win.ini");
STATIC TCHAR aszSchemesSection[] = TEXT("SoundSchemes");
STATIC TCHAR gszSoundScheme[] = TEXT("SoundScheme.%s");
STATIC TCHAR aszCurrentSection[] = TEXT("Current");
STATIC TCHAR aszYourOldScheme[] = TEXT("Your Old Scheme");
STATIC TCHAR aszNone[] = TEXT("<none>");
STATIC TCHAR aszDummyDrv[] = TEXT("mmsystem.dll");
STATIC TCHAR aszDummySnd[] = TEXT("SystemDefault");
STATIC TCHAR aszDummySndValue[] = TEXT(",");
STATIC TCHAR aszExtendedSounds[] = TEXT("ExtendedSounds");
STATIC TCHAR aszExtendedSoundsYes[] = TEXT("yes");
STATIC TCHAR gszApp[] = TEXT("App");
STATIC TCHAR gszSystem[] = TEXT("System");
STATIC TCHAR gszAsterisk[] = TEXT("Asterisk");
STATIC TCHAR gszDefault[] = TEXT("Default");
STATIC TCHAR gszExclamation[] = TEXT("Exclamation");
STATIC TCHAR gszExit[] = TEXT("Exit");
STATIC TCHAR gszQuestion[] = TEXT("Question");
STATIC TCHAR gszStart[] = TEXT("Start");
STATIC TCHAR gszHand[] = TEXT("Hand");
STATIC TCHAR gszClose[] = TEXT("Close");
STATIC TCHAR gszMaximize[] = TEXT("Maximize");
STATIC TCHAR gszMinimize[] = TEXT("Minimize");
STATIC TCHAR gszOpen[] = TEXT("Open");
STATIC TCHAR gszRestoreDown[] = TEXT("RestoreDown");
STATIC TCHAR gszRestoreUp[] = TEXT("RestoreUp");
STATIC TCHAR aszOptionalClips[] = REGSTR_PATH_SETUP REGSTR_KEY_SETUP TEXT("\\OptionalComponents\\Clips");
STATIC TCHAR aszInstalled[] = TEXT("Installed");
STATIC TCHAR * gpszSounds[] = {
gszClose,
gszMaximize,
gszMinimize,
gszOpen,
gszRestoreDown,
gszRestoreUp,
gszAsterisk,
gszDefault,
gszExclamation,
gszExit,
gszQuestion,
gszStart,
gszHand
};
STATIC TCHAR aszMigration[] = TEXT("Migrated Schemes");
#define wCurrentSchemeMigrationLEVEL 1
static struct {
LPCTSTR pszEvent;
int idDescription;
LPCTSTR pszApp;
} gaEventLabels[] = {
{ TEXT("AppGPFault"), STR_LABEL_APPGPFAULT, aszDefault },
{ TEXT("Close"), STR_LABEL_CLOSE, aszDefault },
{ TEXT("EmptyRecycleBin"), STR_LABEL_EMPTYRECYCLEBIN, aszExplorer },
{ TEXT("Maximize"), STR_LABEL_MAXIMIZE, aszDefault },
{ TEXT("MenuCommand"), STR_LABEL_MENUCOMMAND, aszDefault },
{ TEXT("MenuPopup"), STR_LABEL_MENUPOPUP, aszDefault },
{ TEXT("Minimize"), STR_LABEL_MINIMIZE, aszDefault },
{ TEXT("Open"), STR_LABEL_OPEN, aszDefault },
{ TEXT("RestoreDown"), STR_LABEL_RESTOREDOWN, aszDefault },
{ TEXT("RestoreUp"), STR_LABEL_RESTOREUP, aszDefault },
{ TEXT("RingIn"), STR_LABEL_RINGIN, aszDefault },
{ TEXT("RingOut"), STR_LABEL_RINGOUT, aszDefault },
{ TEXT("SystemAsterisk"), STR_LABEL_SYSTEMASTERISK, aszDefault },
{ TEXT(".Default"), STR_LABEL_SYSTEMDEFAULT, aszDefault },
{ TEXT("SystemExclamation"), STR_LABEL_SYSTEMEXCLAMATION, aszDefault },
{ TEXT("SystemExit"), STR_LABEL_SYSTEMEXIT, aszDefault },
{ TEXT("SystemHand"), STR_LABEL_SYSTEMHAND, aszDefault },
{ TEXT("SystemQuestion"), STR_LABEL_SYSTEMQUESTION, aszDefault },
{ TEXT("SystemStart"), STR_LABEL_SYSTEMSTART, aszDefault },
};
TCHAR gszDefaultBeepOldAlias[] = TEXT("SystemDefault");
#define nEVENTLABELS (sizeof(gaEventLabels)/sizeof(gaEventLabels[0]))
STATIC TCHAR gszChimes[] = TEXT("chimes.wav");
STATIC TCHAR gszDing[] = TEXT("ding.wav");
STATIC TCHAR gszTada[] = TEXT("tada.wav");
STATIC TCHAR gszChord[] = TEXT("chord.wav");
STATIC TCHAR * gpszKnownWAVFiles[] = {
gszChord,
gszTada,
gszChimes,
gszDing,
};
#define INISECTION 768
#define BIGINISECTION 2048
TCHAR szNull[] = TEXT("");
TCHAR aszSetup[] = REGSTR_PATH_SETUP;
TCHAR aszValMedia[] = REGSTR_VAL_MEDIA;
TCHAR aszValMediaUnexpanded[] = TEXT("MediaPathUnexpanded");
extern HANDLE hInstalledDriverList; // List of installed driver instances
extern int cInstalledDrivers; // High water count of installed driver instances
HANDLE ghSessionNotification = NULL;
HANDLE ghSessionNotificationEvent = NULL;
BOOL gfSessionDisconnected = FALSE;
#define g_szWinmmConsoleAudioEvent L"Global\\WinMMConsoleAudioEvent"
//=============================================================================
//=== Reg helpers ===
//=============================================================================
LONG RegPrepareEnum(HKEY hkey, PDWORD pcSubkeys, PTSTR *ppstrSubkeyNameBuffer, PDWORD pcchSubkeyNameBuffer)
{
DWORD cSubkeys;
DWORD cchMaxSubkeyName;
LONG lresult;
lresult = RegQueryInfoKey(hkey, NULL, NULL, NULL, &cSubkeys, &cchMaxSubkeyName, NULL, NULL, NULL, NULL, NULL, NULL);
if (ERROR_SUCCESS == lresult) {
PTSTR SubkeyName;
SubkeyName = (PTSTR)HeapAlloc(hHeap, 0, (cchMaxSubkeyName+1) * sizeof(TCHAR));
if (SubkeyName) {
*pcSubkeys = cSubkeys;
*ppstrSubkeyNameBuffer = SubkeyName;
*pcchSubkeyNameBuffer = cchMaxSubkeyName+1;
} else {
lresult = ERROR_OUTOFMEMORY;
}
}
return lresult;
}
LONG RegEnumOpenKey(HKEY hkey, DWORD dwIndex, PTSTR SubkeyName, DWORD cchSubkeyName, REGSAM samDesired, PHKEY phkeyResult)
{
LONG lresult;
lresult = RegEnumKeyEx(hkey, dwIndex, SubkeyName, &cchSubkeyName, NULL, NULL, NULL, NULL);
if (ERROR_SUCCESS == lresult) {
HKEY hkeyResult;
lresult = RegOpenKeyEx(hkey, SubkeyName, 0, samDesired, &hkeyResult);
if (ERROR_SUCCESS == lresult) *phkeyResult = hkeyResult;
}
return lresult;
}
/**************************************************************************
Terminal server helper functions
**************************************************************************/
BOOL
IsPersonalTerminalServicesEnabled(
VOID
)
{
static BOOL fRet;
static BOOL fVerified = FALSE;
DWORDLONG dwlConditionMask;
OSVERSIONINFOEX osVersionInfo;
if ( fVerified )
goto exitpt;
RtlZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osVersionInfo.wProductType = VER_NT_WORKSTATION;
osVersionInfo.wSuiteMask = VER_SUITE_SINGLEUSERTS;
dwlConditionMask = 0;
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR);
fRet = VerifyVersionInfo(
&osVersionInfo,
VER_PRODUCT_TYPE | VER_SUITENAME,
dwlConditionMask
);
fVerified = TRUE;
exitpt:
return(fRet);
}
//
// Check if console audio is enabled in remote session
//
BOOL
IsTsConsoleAudio(
VOID
)
{
BOOL RemoteConsoleAudio = FALSE; // Allow audio play at the console
static HANDLE hConsoleAudioEvent = NULL;
if (NtCurrentPeb()->SessionId == 0 ||
IsPersonalTerminalServicesEnabled()) {
if (hConsoleAudioEvent == NULL) {
hConsoleAudioEvent = OpenEvent(SYNCHRONIZE, FALSE, g_szWinmmConsoleAudioEvent);
}
if (hConsoleAudioEvent != NULL) {
DWORD status;
status = WaitForSingleObject(hConsoleAudioEvent, 0);
if (status == WAIT_OBJECT_0) {
RemoteConsoleAudio = TRUE;
}
}
else {
dprintf(("Remote session: console audio event NULL with error: %d\n", GetLastError()));
}
}
return RemoteConsoleAudio;
}
//
// returns TRUE if we are on the console
//
BOOL IsActiveConsoleSession( VOID )
{
return (USER_SHARED_DATA->ActiveConsoleId == NtCurrentPeb()->SessionId);
}
void InitSession(void);
BOOL WaveReInit(void);
//
// Check if the session is changed and load additional audio drivers
// this is a case only for reconnecting the console from Terminal Server
//
BOOL
CheckSessionChanged(VOID)
{
static BOOL bCalled = FALSE;
static BOOL bWasntRedirecting;
BOOL bOld;
BOOL bDontRedirect;
BOOL bRefreshPreferredDevices;
bRefreshPreferredDevices = FALSE;
bDontRedirect = IsActiveConsoleSession() || IsTsConsoleAudio();
if ( !InterlockedExchange( &bCalled, TRUE ))
{
bWasntRedirecting = !bDontRedirect;
}
bOld = InterlockedExchange( &bWasntRedirecting, bDontRedirect);
if ( bOld ^ bWasntRedirecting )
{
//
// session conditions changed
//
dprintf(( "Session state changed: %s",
(bWasntRedirecting)?"CONSOLE":"SESSION" ));
//
// close the old registry handle
//
mmRegFree();
//
// add new devices
//
InitSession();
WaveReInit();
bRefreshPreferredDevices = TRUE;
}
return bRefreshPreferredDevices;
}
/*****************************************************************************
*
* WTSCurrentSessionIsDisonnected
*
* Determines whether current session is disconnected.
*
****************************************************************************/
BOOL WTSCurrentSessionIsDisconnected(void)
{
if (NULL == ghSessionNotification)
{
// We create the event signalled so that we'll get the connect state
// from audiosrv on first successful pass through this function.
WinAssert(NULL == ghSessionNotificationEvent);
ghSessionNotificationEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if (ghSessionNotificationEvent) {
LONG lresult;
lresult = winmmRegisterSessionNotificationEvent(ghSessionNotificationEvent, &ghSessionNotification);
if (lresult) {
CloseHandle(ghSessionNotificationEvent);
ghSessionNotificationEvent = NULL;
ghSessionNotification = NULL;
}
}
}
if (ghSessionNotification) {
WinAssert(ghSessionNotificationEvent);
if (WAIT_OBJECT_0 == WaitForSingleObjectEx(ghSessionNotificationEvent, 0, TRUE)) {
INT ConnectState;
LONG lresult;
// Get new state from audiosrv
lresult = winmmSessionConnectState(&ConnectState);
if (!lresult) {
gfSessionDisconnected = (WTSDisconnected == ConnectState);
}
}
}
return gfSessionDisconnected;
}
/**************************************************************************
@doc EXTERNAL
@api BOOL | mmDeleteMultipleCriticalSections | This procedure
deletes multiple critical sections.
@parm LPCRITICAL_SECTION* | ppCritcalSections | Pointer to an array of
pointers to critical sections
@parm LONG | nCount | Number of critical sections pointers in the array.
@rdesc VOID
**************************************************************************/
void mmDeleteMultipleCriticalSections(LPCRITICAL_SECTION *ppCriticalSections, LONG nCount)
{
int i;
for (i = 0; i < nCount; i++) DeleteCriticalSection(ppCriticalSections[i]);
return;
}
/**************************************************************************
@doc EXTERNAL
@api BOOL | mmInitializeMultipleCriticalSections | This procedure
initializes multiple critical sections.
@parm LPCRITICAL_SECTION* | ppCritcalSections | Pointer to an array of
pointers to critical sections
@parm LONG | nCount | Number of critical sections pointers in the array.
@rdesc The return value is TRUE if the initialization completed ok,
FALSE if not.
**************************************************************************/
BOOL mmInitializeMultipleCriticalSections(LPCRITICAL_SECTION *ppCriticalSections, LONG nCount)
{
int i; // Must be signed for loops to work properly
for (i = 0; i < nCount; i++)
{
if (!mmInitializeCriticalSection(ppCriticalSections[i])) break;
}
if (i == nCount) return TRUE;
// Back up index to the last successful initialization
i--;
// There must have been a failure. Clean up the ones that succeeded.
for ( ; i >= 0; i--)
{
DeleteCriticalSection(ppCriticalSections[i]);
}
return FALSE;
}
/*
* Initialization for terminal server
*/
void InitSession(void) {
WSINFO SessionInfo;
BOOL bCons = (BOOL)IsActiveConsoleSession();
if ( bCons || IsTsConsoleAudio() )
WinmmRunningInSession = FALSE;
else
WinmmRunningInSession = TRUE;
if (WinmmRunningInSession) {
memset( &SessionInfo, 0, sizeof(SessionInfo));
GetWinStationInfo(&SessionInfo);
lstrcpyW(SessionProtocolName, SessionInfo.ProtocolName);
dprintf(("Remote session protocol %ls", SessionProtocolName));
dprintf(("Remote audio driver name %ls", SessionInfo.AudioDriverName));
} else {
SessionProtocolName[0] = 0;
}
}
/**************************************************************************
@doc INTERNAL
@api VOID | DeletePnpInfo | Frees the pClientPnpInfo file mapping
@rdesc There is no return value
**************************************************************************/
void DeletePnpInfo(void)
{
if (pClientPnpInfo) {
BOOL f;
WinAssert(hClientPnpInfo);
f = UnmapViewOfFile(pClientPnpInfo);
WinAssert(f);
pClientPnpInfo = NULL;
f = CloseHandle(hClientPnpInfo);
WinAssert(f);
hClientPnpInfo = NULL;
}
return;
}
/**************************************************************************
@doc EXTERNAL
@api BOOL | DllProcessAttach | This procedure is called whenever a
process attaches to the DLL.
@parm PVOID | hModule | Handle of the DLL.
@rdesc The return value is TRUE if the initialisation completed ok,
FALSE if not.
**************************************************************************/
BOOL DllProcessAttach(PVOID hModule)
{
HANDLE hModWow32;
PIMAGE_NT_HEADERS NtHeaders; // For checking if we're in the server.
BOOL fSuccess;
#if DBG
CHAR strname[MAX_PATH];
GetModuleFileNameA(NULL, strname, sizeof(strname));
dprintf2(("Process attaching, exe=%hs (Pid %x Tid %x)", strname, GetCurrentProcessId(), GetCurrentThreadId()));
#endif
// We don't need to know when threads start
DisableThreadLibraryCalls(hModule);
// Get access to the process heap. This is cheaper in terms of
// overall resource being chewed up than creating our own heap.
hHeap = RtlProcessHeap();
if (hHeap == NULL) {
return FALSE;
}
// Allocate our tls
gTlsIndex = TlsAlloc();
if (TLS_OUT_OF_INDEXES == gTlsIndex) return FALSE;
//
// Find out if we're in WOW
//
#ifdef _WIN64
WinmmRunningInWOW = FALSE;
#else
if ( (hModWow32 = GetModuleHandleW( L"WOW32.DLL" )) != NULL ) {
WinmmRunningInWOW = TRUE;
GetVDMPointer = (LPGETVDMPOINTER)GetProcAddress( hModWow32, "WOWGetVDMPointer");
lpWOWHandle32 = (LPWOWHANDLE32)GetProcAddress( hModWow32, "WOWHandle32" );
lpWOWHandle16 = (LPWOWHANDLE16)GetProcAddress( hModWow32, "WOWHandle16" );
} else {
WinmmRunningInWOW = FALSE;
}
#endif
//
// Find out if we're in the server
//
NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
WinmmRunningInServer = (NtHeaders->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI) &&
(NtHeaders->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI);
if (mmInitializeMultipleCriticalSections(acs, sizeof(acs)/sizeof(acs[0])))
{
NTSTATUS nts;
hEventApiInit = CreateEvent(NULL, TRUE, FALSE, NULL);
__try {
RtlInitializeResource(&gHandleListResource);
nts = STATUS_SUCCESS;
} __except (EXCEPTION_EXECUTE_HANDLER) {
nts = GetExceptionCode();
}
if ((hEventApiInit) && (NT_SUCCESS(nts))) {
InitDebugLevel();
InitSession();
InitDevices();
// it is important that the MCI window initialisation is done AFTER
// we have initialised Wave, Midi, etc. devices. Note the server
// uses Wave devices, but nothing else (e.g. MCI, midi...)
if (!WinmmRunningInServer) {
mciGlobalInit();
}
} else {
// EventApiInit Create failed
if (hEventApiInit) CloseHandle(hEventApiInit);
hEventApiInit = NULL;
mmDeleteMultipleCriticalSections(acs, sizeof(acs)/sizeof(acs[0]));
TlsFree(gTlsIndex);
return (FALSE);
}
}
else
{
// Failed to initialize critical sections.
TlsFree(gTlsIndex);
return (FALSE);
}
// Added to remove warning.
return TRUE;
}
/**************************************************************************
@doc EXTERNAL
@api BOOL | DllInstanceInit | This procedure is called whenever a
process attaches or detaches from the DLL.
@parm PVOID | hModule | Handle of the DLL.
@parm ULONG | Reason | What the reason for the call is.
@parm PCONTEXT | pContext | Some random other information.
@rdesc The return value is TRUE if the initialisation completed ok,
FALSE if not.
**************************************************************************/
BOOL DllInstanceInit(PVOID hModule, ULONG Reason, PCONTEXT pContext)
{
PIMAGE_NT_HEADERS NtHeaders; // For checking if we're in the server.
HANDLE hModWow32;
DWORD dwThread;
BOOL f;
ghInst = (HANDLE) hModule;
DBG_UNREFERENCED_PARAMETER(pContext);
if (Reason == DLL_PROCESS_ATTACH) {
return DllProcessAttach(hModule);
} else if (Reason == DLL_PROCESS_DETACH) {
dprintf2(("Process ending (Pid %x Tid %x)", GetCurrentProcessId(), GetCurrentThreadId()));
// Squirt("Entering process detach");
// Can't really use RPC during DllMain, so let's just close first
AudioSrvBindingFree();
if (ghSessionNotification)
{
WinAssert(ghSessionNotificationEvent);
winmmUnregisterSessionNotification(ghSessionNotification);
CloseHandle(ghSessionNotificationEvent);
ghSessionNotification = NULL;
ghSessionNotificationEvent = NULL;
}
else
{
WinAssert(!ghSessionNotificationEvent);
}
if (!WinmmRunningInServer) {
TimeCleanup(0); // DLL cleanup
}
mmRegFree();
JoyCleanup(); //qzheng
DeletePnpInfo();
if (hInstalledDriverList)
{
GlobalFree ((HGLOBAL)hInstalledDriverList);
hInstalledDriverList = NULL;
cInstalledDrivers = 0; // Count of installed drivers
}
InvalidatePreferredDevices();
if (hEventApiInit) CloseHandle(hEventApiInit);
mmDeleteMultipleCriticalSections(acs, sizeof(acs)/sizeof(acs[0]));
RtlDeleteResource(&gHandleListResource);
TlsFree(gTlsIndex);
} else if (Reason == 999) {
// This is a dummy call to an entry point in ADVAPI32.DLL. By
// statically linking to the library we avoid the following:
// An application links to winmm.dll and advapi32.dll
// When the application loads the list of dependent dlls is built,
// and a list of the dll init routines is created. It happens
// that the winmm init routine is called first.
// IF there is a sound card in the system, winmm's dll init routine
// call LoadLibrary on the sound driver DLL. This DLL WILL
// reference advapi32.dll - and call entry points in advapi32.
// Unfortunately the init routine of advapi32.dll is marked as
// having RUN - although that is not yet the case as we are still
// within the load routine for winmm.
// When the advapi32 entry point runs, it relies on its init
// routine having completed; specifically a CriticalSection should
// have been initialised. This is not the case, and BOOM!
// The workaround is to ensure that advapi32.dll runs its init
// routine first. This is done by making sure that WINMM has a
// static link to the dll.
ImpersonateSelf(999); // This routine will never be called.
// If it is called, it will fail.
}
return TRUE;
}
/*****************************************************************************
* @doc EXTERNAL MMSYSTEM
*
* @api void | WOWAppExit | This function cleans up when a (WOW) application
* terminates.
*
* @parm HANDLE | hTask | Thread id of application (equivalent to windows task
* handle).
*
* @rdesc Nothing
*
* @comm Note that NOT ALL threads are WOW threads. We rely here on the
* fact that ONLY MCI creates threads other than WOW threads which
* use our low level device resources.
*
* Note also that once a thread is inside here no other threads can
* go through here so, since we clean up MCI devices first, their
* low level devices will be freed before we get to their threads.
*
****************************************************************************/
void WOWAppExit(HANDLE hTask)
{
MCIDEVICEID DeviceID;
HANDLE h, hNext;
dprintf3(("WOW Multi-media - thread %x exiting", hTask));
//
// Free MCI devices allocated by this task (thread).
//
EnterCriticalSection(&mciCritSec);
for (DeviceID=1; DeviceID < MCI_wNextDeviceID; DeviceID++)
{
if (MCI_VALID_DEVICE_ID(DeviceID) &&
MCI_lpDeviceList[DeviceID]->hCreatorTask == hTask)
{
//
// Note that the loop control variables are globals so will be
// reloaded on each iteration.
//
// Also no new devices will be opened by APPs because this is WOW
//
// Hence it's safe (and essential!) to leave the critical
// section which we send the close command
//
dprintf2(("MCI device %ls (%d) not released.", MCI_lpDeviceList[DeviceID]->lpstrInstallName, DeviceID));
LeaveCriticalSection(&mciCritSec);
mciSendCommandW(DeviceID, MCI_CLOSE, 0, 0);
EnterCriticalSection(&mciCritSec);
}
}
LeaveCriticalSection(&mciCritSec);
//
// Free any timers
//
TimeCleanup((DWORD)(DWORD_PTR)hTask);
//
// free all WAVE/MIDI/MMIO handles
//
// ISSUE-2001/01/16-FrankYe This violates the order in which locks should
// be acquired. The HandleListCritSec should be the last lock taken,
// but here it is held while calling winmm APIs
EnterCriticalSection(&HandleListCritSec);
h = GetHandleFirst();
while (h)
{
hNext = GetHandleNext(h);
if (GetHandleOwner(h) == hTask)
{
HANDLE hdrvDestroy;
//
// hack for the wave/midi mapper, always free handles backward.
//
if (hNext && GetHandleOwner(hNext) == hTask) {
h = hNext;
continue;
}
//
// do this so even if the close fails we will not
// find it again.
//
SetHandleOwner(h, NULL);
//
// set the hdrvDestroy global so DriverCallback will not
// do anything for this device
//
hdrvDestroy = h;
switch(GetHandleType(h))
{
case TYPE_WAVEOUT:
dprintf1(("WaveOut handle (%04X) was not released.", h));
waveOutReset((HWAVEOUT)h);
waveOutClose((HWAVEOUT)h);
break;
case TYPE_WAVEIN:
dprintf1(("WaveIn handle (%04X) was not released.", h));
waveInReset((HWAVEIN)h);
waveInClose((HWAVEIN)h);
break;
case TYPE_MIDIOUT:
dprintf1(("MidiOut handle (%04X) was not released.", h));
midiOutReset((HMIDIOUT)h);
midiOutClose((HMIDIOUT)h);
break;
case TYPE_MIDIIN:
dprintf1(("MidiIn handle (%04X) was not released.", h));
midiInReset((HMIDIIN)h);
midiInClose((HMIDIIN)h);
break;
//
// This is not required because WOW does not open any
// mmio files.
//
// case TYPE_MMIO:
// dprintf1(("MMIO handle (%04X) was not released.", h));
// if (mmioClose((HMMIO)h, 0) != 0)
// mmioClose((HMMIO)h, MMIO_FHOPEN);
// break;
}
//
// unset hdrvDestroy so DriverCallback will work.
// some hosebag drivers (like the TIMER driver)
// may pass NULL as their driver handle.
// so dont set it to NULL.
//
hdrvDestroy = (HANDLE)-1;
//
// the reason we start over is because a single free may cause
// multiple free's (ie MIDIMAPPER has another HMIDI open, ...)
//
h = GetHandleFirst();
} else {
h = GetHandleNext(h);
}
}
LeaveCriticalSection(&HandleListCritSec);
//
// Clean up an installed IO procs for mmio
//
// This is not required because wow does not install any io procs.
//
// mmioCleanupIOProcs(hTask);
//
// If avicap32.dll is loaded, then ask it to clean up
// capture drivers
{
HMODULE hmod;
hmod = GetModuleHandle(TEXT("avicap32.dll"));
if (hmod) {
typedef void (*AppCleanupProc)(HANDLE);
AppCleanupProc fp;
fp = (AppCleanupProc) GetProcAddress(hmod, "AppCleanup");
if (fp) {
fp(hTask);
}
}
}
}
BOOL IsWinlogon(void)
{
TCHAR szTarget[] = TEXT("winlogon.Exe");
TCHAR szTemp[MAX_PATH];
UINT ii;
static BOOL fAlreadyChecked = FALSE;
static BOOL fIsWinlogon = FALSE;
if (fAlreadyChecked) return fIsWinlogon;
if (0 == GetModuleFileName(NULL, szTemp, sizeof(szTemp)/sizeof(szTemp[0])))
{
//
// GetModuleFileName fails...
//
return FALSE;
}
for (ii = lstrlen(szTemp) - 1; ii; ii--)
{
if ('\\' == szTemp[ii])
{
ii++;
fIsWinlogon = !lstrcmpi(&(szTemp[ii]), szTarget);
fAlreadyChecked = TRUE;
return fIsWinlogon;
}
}
return FALSE;
}
void FreeUnusedDrivers(PMMDRV pmmdrvZ)
{
PMMDRV pmmdrv = pmmdrvZ->Next;
while (pmmdrv != pmmdrvZ)
{
PMMDRV pmmdrvNext = pmmdrv->Next;
ASSERT(pmmdrv->hDriver);
if ((0 == pmmdrv->NumDevs) && (0 == (pmmdrv->fdwDriver & MMDRV_DESERTED)))
{
// For pnp driver we send DRVM_EXIT
if (pmmdrv->cookie) pmmdrv->drvMessage(0, DRVM_EXIT, 0L, 0L, (DWORD_PTR)pmmdrv->cookie);
DrvClose(pmmdrv->hDriver, 0, 0);
DeleteCriticalSection(&pmmdrv->MixerCritSec);
// Remove from list
pmmdrv->Prev->Next = pmmdrv->Next;
pmmdrv->Next->Prev = pmmdrv->Prev;
// Zero memory to help catch reuse bugs
ZeroMemory(pmmdrv, sizeof(*pmmdrv));
HeapFree(hHeap, 0, pmmdrv);
}
pmmdrv = pmmdrvNext;
}
return;
}
extern BOOL IMixerLoadDrivers( void );
void InitDevices(void)
{
cPnpEvents = 0;
// Initialize various lists
ZeroMemory(&wdmDevZ, sizeof(wdmDevZ));
ZeroMemory(&waveoutdrvZ, sizeof(waveoutdrvZ));
ZeroMemory(&waveindrvZ, sizeof(waveindrvZ));
waveoutdrvZ.Next = waveoutdrvZ.Prev = &waveoutdrvZ;
waveindrvZ.Next = waveindrvZ.Prev = &waveindrvZ;
ZeroMemory(&midioutdrvZ, sizeof(midioutdrvZ));
ZeroMemory(&midiindrvZ, sizeof(midiindrvZ));
midioutdrvZ.Next = midioutdrvZ.Prev = &midioutdrvZ;
midiindrvZ.Next = midiindrvZ.Prev = &midiindrvZ;
ZeroMemory(&auxdrvZ, sizeof(auxdrvZ));
auxdrvZ.Next = auxdrvZ.Prev = &auxdrvZ;
ZeroMemory(&mixerdrvZ, sizeof(mixerdrvZ));
mixerdrvZ.Next = mixerdrvZ.Prev = &mixerdrvZ;
// Now initialize different device classes
WaveInit();
//
// The server only needs wave to do message beeps.
//
if (!WinmmRunningInServer) {
MidiInit();
if (!TimeInit()) {
dprintf1(("Failed to initialize timer services"));
}
midiEmulatorInit();
AuxInit();
JoyInit();
MixerInit();
// IMixerLoadDrivers();
//
// Clear up any drivers which don't have any devices (we do it this
// way so we don't keep loading and unloading mmdrv.dll).
//
// Note - we only load the mappers if there are real devices so we
// don't need to worry about unloading them.
//
FreeUnusedDrivers(&waveindrvZ);
FreeUnusedDrivers(&midioutdrvZ);
FreeUnusedDrivers(&midiindrvZ);
FreeUnusedDrivers(&auxdrvZ);
}
FreeUnusedDrivers(&waveoutdrvZ);
}
/*****************************************************************************
* @doc EXTERNAL MMSYSTEM
*
* @api UINT | mmsystemGetVersion | This function returns the current
* version number of the Multimedia extensions system software.
*
* @rdesc The return value specifies the major and minor version numbers of
* the Multimedia extensions. The high-order byte specifies the major
* version number. The low-order byte specifies the minor version number.
*
****************************************************************************/
UINT APIENTRY mmsystemGetVersion(void)
{
return(MMSYSTEM_VERSION);
}
#define MAXDRIVERORDINAL 9
/****************************************************************************
strings
****************************************************************************/
STATICDT SZCODE szWodMessage[] = WOD_MESSAGE;
STATICDT SZCODE szWidMessage[] = WID_MESSAGE;
STATICDT SZCODE szModMessage[] = MOD_MESSAGE;
STATICDT SZCODE szMidMessage[] = MID_MESSAGE;
STATICDT SZCODE szAuxMessage[] = AUX_MESSAGE;
STATICDT SZCODE szMxdMessage[] = MXD_MESSAGE;
STATICDT WSZCODE wszWave[] = L"wave";
STATICDT WSZCODE wszMidi[] = L"midi";
STATICDT WSZCODE wszAux[] = L"aux";
STATICDT WSZCODE wszMixer[] = L"mixer";
STATICDT WSZCODE wszMidiMapper[] = L"midimapper";
STATICDT WSZCODE wszWaveMapper[] = L"wavemapper";
STATICDT WSZCODE wszAuxMapper[] = L"auxmapper";
STATICDT WSZCODE wszMixerMapper[] = L"mixermapper";
WSZCODE wszNull[] = L"";
WSZCODE wszSystemIni[] = L"system.ini";
WSZCODE wszDrivers[] = DRIVERS_SECTION;
/*
** WaveMapperInit
**
** Initialize the wave mapper if it's not already initialized.
**
*/
BOOL WaveMapperInitialized = FALSE;
void WaveMapperInit(void)
{
HDRVR h = NULL;
BOOL fLoadOutput = TRUE;
BOOL fLoadInput = TRUE;
EnterNumDevs("WaveMapperInit");
EnterCriticalSection(&MapperInitCritSec);
if (WaveMapperInitialized) {
LeaveCriticalSection(&MapperInitCritSec);
LeaveNumDevs("WaveMapperInit");
return;
}
/* The wave mapper.
*
* MMSYSTEM allows the user to install a special wave driver which is
* not visible to the application as a physical device (it is not
* included in the number returned from getnumdevs).
*
* An application opens the wave mapper when it does not care which
* physical device is used to input or output waveform data. Thus
* it is the wave mapper's task to select a physical device that can
* render the application-specified waveform format or to convert the
* data into a format that is renderable by an available physical
* device.
*/
if (wTotalWaveInDevs + wTotalWaveOutDevs > 0)
{
if (0 != (h = mmDrvOpen(wszWaveMapper)))
{
fLoadOutput = mmDrvInstall(h, wszWaveMapper, NULL, MMDRVI_MAPPER|MMDRVI_WAVEOUT|MMDRVI_HDRV);
if (!WinmmRunningInServer) {
h = mmDrvOpen(wszWaveMapper);
fLoadInput = mmDrvInstall(h, wszWaveMapper, NULL, MMDRVI_MAPPER|MMDRVI_WAVEIN |MMDRVI_HDRV);
}
}
WaveMapperInitialized |= ((0 != h) && (fLoadOutput) && (fLoadInput))?TRUE:FALSE;
}
LeaveCriticalSection(&MapperInitCritSec);
LeaveNumDevs("WaveMapperInit");
}
/*
** MidiMapperInit
**
** Initialize the MIDI mapper if it's not already initialized.
**
*/
BOOL MidiMapperInitialized = FALSE;
void MidiMapperInit(void)
{
HDRVR h;
EnterNumDevs("MidiMapperInit");
EnterCriticalSection(&MapperInitCritSec);
if (MidiMapperInitialized) {
LeaveCriticalSection(&MapperInitCritSec);
LeaveNumDevs("MidiMapperInit");
return;
}
/* The midi mapper.
*
* MMSYSTEM allows the user to install a special midi driver which is
* not visible to the application as a physical device (it is not
* included in the number returned from getnumdevs).
*
* An application opens the midi mapper when it does not care which
* physical device is used to input or output midi data. It
* is the midi mapper's task to modify the midi data so that it is
* suitable for playback on the connected synthesizer hardware.
*/
// EnterNumDevs("MidiMapperInit");
if (wTotalMidiInDevs + wTotalMidiOutDevs > 0)
{
if (0 != (h = mmDrvOpen(wszMidiMapper)))
{
mmDrvInstall(h, wszMidiMapper, NULL, MMDRVI_MAPPER|MMDRVI_MIDIOUT|MMDRVI_HDRV);
h = mmDrvOpen(wszMidiMapper);
mmDrvInstall(h, wszMidiMapper, NULL, MMDRVI_MAPPER|MMDRVI_MIDIIN |MMDRVI_HDRV);
}
MidiMapperInitialized = TRUE;
}
// LeaveNumDevs("MidiMapperInit");
LeaveCriticalSection(&MapperInitCritSec);
LeaveNumDevs("MidiMapperInit");
}
/*****************************************************************************
* @doc INTERNAL WAVE
*
* @api BOOL | WaveInit | This function initialises the wave services.
*
* @rdesc Returns TRUE if the services of all loaded wave drivers are
* correctly initialised, FALSE if an error occurs.
*
* @comm the wave devices are loaded in the following order
*
* \Device\WaveIn0
* \Device\WaveIn1
* \Device\WaveIn2
* \Device\WaveIn3
*
****************************************************************************/
BOOL WaveInit(void)
{
WCHAR szKey[ (sizeof(wszWave) + sizeof( WCHAR )) / sizeof( WCHAR ) ];
int i;
HDRVR h;
// Find the real WAVE drivers
lstrcpyW(szKey, wszWave);
szKey[ (sizeof(szKey) / sizeof( WCHAR )) - 1 ] = (WCHAR)'\0';
for (i=0; i<=MAXDRIVERORDINAL; i++)
{
h = mmDrvOpen(szKey);
if (h)
{
mmDrvInstall(h, szKey, NULL, MMDRVI_WAVEOUT|MMDRVI_HDRV);
if (!WinmmRunningInServer) {
h = mmDrvOpen(szKey);
mmDrvInstall(h, szKey, NULL, MMDRVI_WAVEIN |MMDRVI_HDRV);
}
}
szKey[ (sizeof(wszWave) / sizeof(WCHAR)) - 1] = (WCHAR)('1' + i);
}
return TRUE;
}
BOOL WaveReInit(void)
{
WCHAR szKey[ (sizeof(wszWave) + sizeof( WCHAR )) / sizeof( WCHAR ) ];
int i;
HDRVR h;
EnterCriticalSection(&NumDevsCritSec);
// Find the real WAVE drivers
lstrcpyW(szKey, wszWave);
szKey[ (sizeof(szKey) / sizeof( WCHAR )) - 1 ] = (WCHAR)'\0';
for (i=0; i<=MAXDRIVERORDINAL; i++)
{
h = mmDrvOpen(szKey);
if (h)
{
mmDrvInstall(h, szKey, NULL, MMDRVI_WAVEOUT|MMDRVI_HDRV);
if (!WinmmRunningInServer) {
h = mmDrvOpen(szKey);
mmDrvInstall(h, szKey, NULL, MMDRVI_WAVEIN |MMDRVI_HDRV);
}
}
szKey[ (sizeof(wszWave) / sizeof(WCHAR)) - 1] = (WCHAR)('1' + i);
}
FreeUnusedDrivers(&waveoutdrvZ);
LeaveCriticalSection(&NumDevsCritSec);
return TRUE;
}
/*****************************************************************************
* @doc INTERNAL MIDI
*
* @api BOOL | MidiInit | This function initialises the midi services.
*
* @rdesc The return value is TRUE if the services are initialised, FALSE if
* an error occurs
*
* @comm the midi devices are loaded from SYSTEM.INI in the following order
*
* midi
* midi1
* midi2
* midi3
*
****************************************************************************/
BOOL MidiInit(void)
{
WCHAR szKey[ (sizeof(wszMidi) + sizeof( WCHAR )) / sizeof( WCHAR ) ];
int i;
HDRVR h;
// Find the real MIDI drivers
lstrcpyW(szKey, wszMidi);
szKey[ (sizeof(szKey) / sizeof( WCHAR )) - 1 ] = (WCHAR)'\0';
for (i=0; i<=MAXDRIVERORDINAL; i++)
{
h = mmDrvOpen(szKey);
if (h)
{
mmDrvInstall(h, szKey, NULL, MMDRVI_MIDIOUT|MMDRVI_HDRV);
h = mmDrvOpen(szKey);
mmDrvInstall(h, szKey, NULL, MMDRVI_MIDIIN |MMDRVI_HDRV);
}
szKey[ (sizeof(wszMidi) / sizeof(WCHAR)) - 1] = (WCHAR)('1' + i);
}
return TRUE;
}
/*****************************************************************************
* @doc INTERNAL AUX
*
* @api BOOL | AuxInit | This function initialises the auxiliary output
* services.
*
* @rdesc The return value is TRUE if the services are initialised, FALSE if
* an error occurs
*
* @comm SYSTEM.INI is searched for auxn.drv=.... where n can be from 1 to 4.
* Each driver is loaded and the number of devices it supports is read
* from it.
*
* AUX devices are loaded from SYSTEM.INI in the following order
*
* aux
* aux1
* aux2
* aux3
*
****************************************************************************/
BOOL AuxInit(void)
{
WCHAR szKey[ (sizeof(wszAux) + sizeof( WCHAR )) / sizeof( WCHAR ) ];
int i;
HDRVR h;
// Find the real Aux drivers
lstrcpyW(szKey, wszAux);
szKey[ (sizeof(szKey) / sizeof( WCHAR )) - 1 ] = (WCHAR)'\0';
for (i=0; i<=MAXDRIVERORDINAL; i++)
{
h = mmDrvOpen(szKey);
if (h)
{
mmDrvInstall(h, szKey, NULL, MMDRVI_AUX|MMDRVI_HDRV);
}
// advance driver ordinal
szKey[ (sizeof(wszAux) / sizeof(WCHAR)) - 1] = (WCHAR)('1' + i);
}
/* The aux mapper.
*
* MMSYSTEM allows the user to install a special aux driver which is
* not visible to the application as a physical device (it is not
* included in the number returned from getnumdevs).
*
* I'm not sure why anyone would do this but I'll provide the
* capability for symmetry.
*
*/
if (wTotalAuxDevs > 0)
{
h = mmDrvOpen(wszAuxMapper);
if (h)
{
mmDrvInstall(h, wszAuxMapper, NULL, MMDRVI_MAPPER|MMDRVI_AUX|MMDRVI_HDRV);
}
}
return TRUE;
}
/*****************************************************************************
* @doc INTERNAL MIXER
*
* @api BOOL | MixerInit | This function initialises the mixer drivers
* services.
*
* @rdesc The return value is TRUE if the services are initialised, FALSE if
* an error occurs
*
* @comm SYSTEM.INI is searched for mixern.drv=.... where n can be from 1 to 4.
* Each driver is loaded and the number of devices it supports is read
* from it.
*
* MIXER devices are loaded from SYSTEM.INI in the following order
*
* mixer
* mixer1
* mixer2
* mixer3
*
****************************************************************************/
BOOL MixerInit(void)
{
WCHAR szKey[ (sizeof(wszMixer) + sizeof( WCHAR )) / sizeof( WCHAR ) ];
int i;
HDRVR h;
// Find the real Mixer drivers
lstrcpyW(szKey, wszMixer);
szKey[ (sizeof(szKey) / sizeof( WCHAR )) - 1 ] = (WCHAR)'\0';
for (i=0; i<=MAXDRIVERORDINAL; i++)
{
h = mmDrvOpen(szKey);
if (h)
{
mmDrvInstall(h, szKey, NULL, MMDRVI_MIXER|MMDRVI_HDRV);
}
// advance driver ordinal
szKey[ (sizeof(wszMixer) / sizeof(WCHAR)) - 1] = (WCHAR)('1' + i);
}
#ifdef MIXER_MAPPER
/* The Mixer mapper.
*
* MMSYSTEM allows the user to install a special aux driver which is
* not visible to the application as a physical device (it is not
* included in the number returned from getnumdevs).
*
* I'm not sure why anyone would do this but I'll provide the
* capability for symmetry.
*
*/
if (guTotalMixerDevs > 0)
{
h = mmDrvOpen(wszMixerMapper);
if (h)
{
mmDrvInstall(h, wszMixerMapper, NULL, MMDRVI_MAPPER|MMDRVI_MIXER|MMDRVI_HDRV);
}
}
#endif
return TRUE;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @api HANDLE | mmDrvOpen | This function load's an installable driver, but
* first checks weather it exists in the [Drivers] section.
*
* @parm LPSTR | szAlias | driver alias to load
*
* @rdesc The return value is return value from DrvOpen or NULL if the alias
* was not found in the [Drivers] section.
*
****************************************************************************/
HANDLE mmDrvOpen(LPWSTR szAlias)
{
WCHAR buf[300]; // Make this large to bypass GetPrivate... bug
if ( winmmGetPrivateProfileString( wszDrivers,
szAlias,
wszNull,
buf,
sizeof(buf) / sizeof(WCHAR),
wszSystemIni) ) {
return (HANDLE)DrvOpen(szAlias, NULL, 0L);
}
else {
return NULL;
}
}
/*****************************************************************************
* @doc INTERNAL
*
* @api HANDLE | mmDrvInstall | This function installs/removes a WAVE/MIDI driver
*
* @parm HANDLE | hDriver | Module handle or driver handle containing driver
*
* @parm WCHAR * | wszDrvEntry | String corresponding to hDriver to be stored for
* later use
*
* @parm DRIVERMSGPROC | drvMessage | driver message procedure, if NULL
* the standard name will be used (looked for with GetProcAddress)
*
* @parm UINT | wFlags | flags
*
* @flag MMDRVI_TYPE | driver type mask
* @flag MMDRVI_WAVEIN | install driver as a wave input driver
* @flag MMDRVI_WAVEOUT | install driver as a wave ouput driver
* @flag MMDRVI_MIDIIN | install driver as a midi input driver
* @flag MMDRVI_MIDIOUT | install driver as a midi output driver
* @flag MMDRVI_AUX | install driver as a aux driver
* @flag MMDRVI_MIXER | install driver as a mixer driver
*
* @flag MMDRVI_MAPPER | install this driver as the mapper
* @flag MMDRVI_HDRV | hDriver is a installable driver
* @flag MMDRVI_REMOVE | remove the driver
*
* @rdesc returns NULL if unable to install driver
*
****************************************************************************/
UINT APIENTRY mmDrvInstall(
HANDLE hDriver,
WCHAR * wszDrvEntry,
DRIVERMSGPROC drvMessage,
UINT wFlags
)
{
#define SZ_SIZE 128
int i;
DWORD dw;
PMMDRV pdrvZ;
PMMDRV pdrv;
SIZE_T cbdrv;
HANDLE hModule;
UINT msg_num_devs;
UINT *pTotalDevs;
CHAR *szMessage;
WCHAR sz[SZ_SIZE];
BOOL fMixerCritSec;
fMixerCritSec = FALSE;
pdrvZ = NULL;
pdrv = NULL;
if (hDriver && (wFlags & MMDRVI_HDRV))
{
hModule = DrvGetModuleHandle(hDriver);
}
else
{
hModule = hDriver;
hDriver = NULL;
}
switch (wFlags & MMDRVI_TYPE)
{
case MMDRVI_WAVEOUT:
pdrvZ = &waveoutdrvZ;
cbdrv = sizeof(WAVEDRV);
msg_num_devs = WODM_GETNUMDEVS;
pTotalDevs = &wTotalWaveOutDevs;
szMessage = szWodMessage;
break;
case MMDRVI_WAVEIN:
pdrvZ = &waveindrvZ;
cbdrv = sizeof(WAVEDRV);
msg_num_devs = WIDM_GETNUMDEVS;
pTotalDevs = &wTotalWaveInDevs;
szMessage = szWidMessage;
break;
case MMDRVI_MIDIOUT:
pdrvZ = &midioutdrvZ;
cbdrv = sizeof(MIDIDRV);
msg_num_devs = MODM_GETNUMDEVS;
pTotalDevs = &wTotalMidiOutDevs;
szMessage = szModMessage;
break;
case MMDRVI_MIDIIN:
pdrvZ = &midiindrvZ;
cbdrv = sizeof(MIDIDRV);
msg_num_devs = MIDM_GETNUMDEVS;
pTotalDevs = &wTotalMidiInDevs;
szMessage = szMidMessage;
break;
case MMDRVI_AUX:
pdrvZ = &auxdrvZ;
cbdrv = sizeof(AUXDRV);
msg_num_devs = AUXDM_GETNUMDEVS;
pTotalDevs = &wTotalAuxDevs;
szMessage = szAuxMessage;
break;
case MMDRVI_MIXER:
pdrvZ = &mixerdrvZ;
cbdrv = sizeof(MIXERDRV);
msg_num_devs = MXDM_GETNUMDEVS;
pTotalDevs = &guTotalMixerDevs;
szMessage = szMxdMessage;
break;
default:
goto error_exit;
}
if (drvMessage == NULL && hModule != NULL)
drvMessage = (DRIVERMSGPROC)GetProcAddress(hModule, szMessage);
if (drvMessage == NULL)
goto error_exit;
//
// try to find the driver already installed
//
pdrv = pdrvZ->Next;
while (pdrv != pdrvZ && pdrv->drvMessage != drvMessage) pdrv = pdrv->Next;
if (pdrv != pdrvZ)
{
pdrv = NULL;
goto error_exit; // we found it, don't reinstall it
}
//
// Make a new MMDRV for the device.
//
pdrv = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbdrv);
if (!pdrv) goto error_exit;
pdrv->hDriver = hDriver;
pdrv->Usage = 1;
pdrv->cookie = 0; // This is 0 for non-WDM drivers.
pdrv->fdwDriver = (wFlags & MMDRVI_MAPPER) ? MMDRV_MAPPER : 0;
pdrv->fdwDriver |= DrvIsPreXp(hDriver) ? MMDRV_PREXP : 0;
pdrv->drvMessage = drvMessage;
WinAssert(lstrlenA(szMessage) < sizeof(pdrv->wszMessage)/sizeof(WCHAR));
mbstowcs(pdrv->wszMessage, szMessage, sizeof(pdrv->wszMessage)/sizeof(WCHAR));
lstrcpyW( pdrv->wszSessProtocol, SessionProtocolName );
winmmGetPrivateProfileString(wszDrivers, // ini section
wszDrvEntry, // key name
wszDrvEntry, // default if no match
sz, // return buffer
SZ_SIZE, // sizeof of return buffer
wszSystemIni); // ini. file
lstrcpyW(pdrv->wszDrvEntry,sz);
if (!mmInitializeCriticalSection(&pdrv->MixerCritSec)) goto error_exit;
fMixerCritSec = TRUE;
//
// Mixer drivers get extra message?!
//
if (MMDRVI_MIXER == (wFlags & MMDRVI_TYPE))
{
//
// send the init message, if the driver returns a error, should we
// unload them???
//
dw = drvMessage(0, MXDM_INIT,0L,0L,0L);
}
//
// call driver to get num-devices it supports
//
dw = drvMessage(0,msg_num_devs,0L,0L,0L);
//
// the device returned a error, or has no devices
//
// if (HIWORD(dw) != 0 || LOWORD(dw) == 0)
if ((HIWORD(dw) != 0) || (0 == LOWORD(dw))) goto error_exit;
pdrv->NumDevs = LOWORD(dw);
//
// dont increment number of dev's for the mapper
//
if (!(pdrv->fdwDriver & MMDRV_MAPPER)) *pTotalDevs += pdrv->NumDevs;
//
// add to end of the driver list
//
mregAddDriver(pdrvZ, pdrv);
return TRUE; // return a non-zero value
error_exit:
if (hDriver && !(wFlags & MMDRVI_REMOVE))
DrvClose(hDriver, 0, 0);
if (fMixerCritSec) DeleteCriticalSection(&pdrv->MixerCritSec);
WinAssert(pdrv != pdrvZ);
if (pdrv) HeapFree(hHeap, 0, pdrv);
return FALSE;
#undef SZ_SIZE
}
/**************************************************************************
wdmDevInterfaceInstall
Notes:
Assumes that the NumDevsCritSec is owned as necessary
**************************************************************************/
HANDLE wdmDevInterfaceInstall
(
LPCWSTR pszDev,
LONG cPnpEvents
)
{
PWDMDEVICEINTERFACE pwdmDev;
EnterCriticalSection(&NumDevsCritSec);
//
// Look for device interface...
//
pwdmDev = wdmDevZ.Next;
while (pwdmDev)
{
WinAssert(pwdmDev->cUsage);
if (!lstrcmpiW(pwdmDev->szDeviceInterface, pszDev))
{
pwdmDev->cUsage++;
pwdmDev->cPnpEvents = cPnpEvents;
break;
}
pwdmDev = pwdmDev->Next;
}
if (!pwdmDev)
{
SIZE_T cbszDev;
//
// Device interface not found...
//
cbszDev = (lstrlen(pszDev) + 1) * sizeof(pszDev[0]);
pwdmDev = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(*pwdmDev) + cbszDev);
if (pwdmDev)
{
pwdmDev->cUsage = 1;
pwdmDev->cPnpEvents = cPnpEvents;
lstrcpyW(pwdmDev->szDeviceInterface, pszDev);
pwdmDev->Next = wdmDevZ.Next;
wdmDevZ.Next = pwdmDev;
}
}
LeaveCriticalSection(&NumDevsCritSec);
return (pwdmDev ? pwdmDev->szDeviceInterface : NULL);
}
/**************************************************************************
wdmDevInterfaceInc
Notes:
Enters/Leaves the NumDevsCritSec
**************************************************************************/
BOOL wdmDevInterfaceInc
(
PCWSTR dwCookie
)
{
PWDMDEVICEINTERFACE pwdmDev;
if (NULL == dwCookie)
{
return FALSE;
}
EnterCriticalSection(&NumDevsCritSec);
//
// Look for device interface...
//
pwdmDev = wdmDevZ.Next;
while (pwdmDev)
{
WinAssert(pwdmDev->cUsage);
if (dwCookie == pwdmDev->szDeviceInterface)
{
pwdmDev->cUsage++;
LeaveCriticalSection(&NumDevsCritSec);
return TRUE;
}
pwdmDev = pwdmDev->Next;
}
//
// If we get down here, it means that we're trying to increment the
// reference to a interface that doesn't exist anymore
//
WinAssert(FALSE);
LeaveCriticalSection(&NumDevsCritSec);
return FALSE;
}
/**************************************************************************
wdmDevInterfaceDec
Notes:
Enters/Leaves the NumDevsCritSec
**************************************************************************/
BOOL wdmDevInterfaceDec
(
PCWSTR dwCookie
)
{
PWDMDEVICEINTERFACE pwdmDevPrev;
if (NULL == dwCookie)
{
return FALSE;
}
EnterCriticalSection(&NumDevsCritSec);
//
// Look for device interface...
//
pwdmDevPrev = &wdmDevZ;
while (pwdmDevPrev->Next)
{
PWDMDEVICEINTERFACE pwdmDev = pwdmDevPrev->Next;
WinAssert(pwdmDev->cUsage);
if (dwCookie == pwdmDev->szDeviceInterface)
{
if (0 == --pwdmDev->cUsage)
{
pwdmDevPrev->Next = pwdmDev->Next;
HeapFree(hHeap, 0, pwdmDev);
}
LeaveCriticalSection(&NumDevsCritSec);
return TRUE;
}
pwdmDevPrev = pwdmDev;
}
//
// If we get down here it means that we are trying to decrement the
// reference to an interface that doesn't exist anymore.
//
WinAssert(FALSE);
LeaveCriticalSection(&NumDevsCritSec);
return FALSE;
}
//--------------------------------------------------------------------------;
//
// void CleanUpHandles
//
// Description:
// Given a particular subsystem and device interface, cleans up the
// handles.
//
// Arguments:
// UINT uFlags: Has one of MMDRVI_* flags to indictate which class of
// handle needs to be checked for desertion.
//
// HANDLE cookie: Device interface
//
// Return (void):
//
// History:
// 01/25/99 Fwong Adding Pnp Support.
//
//--------------------------------------------------------------------------;
void CleanUpHandles
(
UINT wFlags,
PCWSTR cookie
)
{
HANDLE hMM;
UINT uType;
PHNDL pSearch;
BOOL fFound;
// Convert MMDRVI_* type flags to TYPE_*
switch(wFlags & MMDRVI_TYPE)
{
case MMDRVI_WAVEOUT:
uType = TYPE_WAVEOUT;
break;
case MMDRVI_WAVEIN:
uType = TYPE_WAVEIN;
break;
case MMDRVI_MIDIOUT:
uType = TYPE_MIDIOUT;
break;
case MMDRVI_MIDIIN:
uType = TYPE_MIDIIN;
break;
case MMDRVI_MIXER:
uType = TYPE_MIXER;
break;
case MMDRVI_AUX:
uType = TYPE_AUX;
break;
default:
uType = TYPE_UNKNOWN;
WinAssert(TYPE_UNKNOWN != uType);
}
// Note: Since we are not freeing any handles (just marking them
// deserted), we don't have to mess with the HandleListCritSec
for (pSearch = pHandleList; NULL != pSearch; pSearch = pSearch->pNext)
{
if ((cookie != pSearch->cookie) || (uType != pSearch->uType))
{
continue;
}
// Both the cookie and type match...
hMM = PHtoH(pSearch);
switch (uType)
{
case TYPE_WAVEOUT:
waveOutDesertHandle((HWAVEOUT)hMM);
break;
case TYPE_WAVEIN:
waveInDesertHandle((HWAVEIN)hMM);
break;
case TYPE_MIDIOUT:
midiOutDesertHandle((HMIDIOUT)hMM);
break;
case TYPE_MIDIIN:
midiInDesertHandle((HMIDIIN)hMM);
break;
case TYPE_MIXER:
mixerDesertHandle((HMIXER)hMM);
break;
case TYPE_AUX:
// We don't expect open handles of this type
WinAssert(TYPE_AUX != uType);
break;
}
}
} // CleanUpHandles()
UINT APIENTRY wdmDrvInstall
(
HANDLE hDriver,
LPTSTR pszDriverFile,
HANDLE cookie,
UINT wFlags
)
{
int i;
DWORD dw;
PMMDRV pdrvZ;
PMMDRV pdrv;
SIZE_T cbdrv;
HANDLE hModule;
UINT msg_init;
UINT msg_num_devs;
UINT *pTotalDevs;
CHAR *szMessage;
DRIVERMSGPROC pfnDrvMessage;
WCHAR sz[MAX_PATH];
BOOL fMixerCritSec;
// Squirt("Entering wdmDrvInstall");
fMixerCritSec = FALSE;
pdrv = NULL;
pfnDrvMessage = NULL;
if (hDriver && (wFlags & MMDRVI_HDRV))
{
hModule = DrvGetModuleHandle(hDriver);
}
else
{
hModule = hDriver;
hDriver = NULL;
}
switch (wFlags & MMDRVI_TYPE)
{
case MMDRVI_WAVEOUT:
pdrvZ = &waveoutdrvZ;
cbdrv = sizeof(WAVEDRV);
msg_init = WODM_INIT;
msg_num_devs = WODM_GETNUMDEVS;
pTotalDevs = &wTotalWaveOutDevs;
szMessage = szWodMessage;
break;
case MMDRVI_WAVEIN:
pdrvZ = &waveindrvZ;
cbdrv = sizeof(WAVEDRV);
msg_init = WIDM_INIT;
msg_num_devs = WIDM_GETNUMDEVS;
pTotalDevs = &wTotalWaveInDevs;
szMessage = szWidMessage;
break;
case MMDRVI_MIDIOUT:
pdrvZ = &midioutdrvZ;
cbdrv = sizeof(MIDIDRV);
msg_init = MODM_INIT;
msg_num_devs = MODM_GETNUMDEVS;
pTotalDevs = &wTotalMidiOutDevs;
szMessage = szModMessage;
break;
case MMDRVI_MIDIIN:
pdrvZ = &midiindrvZ;
cbdrv = sizeof(MIDIDRV);
msg_init = MIDM_INIT;
msg_num_devs = MIDM_GETNUMDEVS;
pTotalDevs = &wTotalMidiInDevs;
szMessage = szMidMessage;
break;
case MMDRVI_AUX:
pdrvZ = &auxdrvZ;
cbdrv = sizeof(AUXDRV);
msg_init = AUXM_INIT;
msg_num_devs = AUXDM_GETNUMDEVS;
pTotalDevs = &wTotalAuxDevs;
szMessage = szAuxMessage;
break;
case MMDRVI_MIXER:
pdrvZ = &mixerdrvZ;
cbdrv = sizeof(MIXERDRV);
msg_init = MXDM_INIT;
msg_num_devs = MXDM_GETNUMDEVS;
pTotalDevs = &guTotalMixerDevs;
szMessage = szMxdMessage;
break;
default:
goto error_exit;
}
pfnDrvMessage = (DRIVERMSGPROC)GetProcAddress(hModule, szMessage);
if (NULL == pfnDrvMessage) goto error_exit;
//
// either install or remove the specified driver
//
if (wFlags & MMDRVI_REMOVE)
{
//
// try to find the driver already installed
//
for (pdrv = pdrvZ->Next; pdrv != pdrvZ; pdrv = pdrv->Next)
{
if (pdrv->fdwDriver & MMDRV_DESERTED) continue;
if (cookie) {
// This is a wdm driver so we're matching up with cookie.
if (pdrv->cookie == cookie) break;
} else {
// ISSUE-2001/01/14-FrankYe Will this ever be called
// on non WDM driver???
// Not WDM driver, so matching up with pfnDrvMessage.
if (pdrv->drvMessage == pfnDrvMessage) break;
}
}
//
// Driver not found.
//
if (pdrv == pdrvZ) pdrv = NULL;
if (NULL == pdrv) goto error_exit;
//
// don't decrement number of dev's for the mapper
//
// Note: Moved this to before the usage check...
//
if (!(pdrv->fdwDriver & MMDRV_MAPPER)) *pTotalDevs -= pdrv->NumDevs;
//
// Mark no devs otherwise the device mapping will be skewed.
//
pdrv->NumDevs = 0;
//
// Mark this driver as removed
//
pdrv->fdwDriver |= MMDRV_DESERTED;
CleanUpHandles(wFlags & MMDRVI_TYPE, pdrv->cookie);
mregDecUsagePtr(pdrv);
return TRUE;
}
else
{
//
// try to find the driver already installed
//
for (pdrv = pdrvZ->Next; pdrv != pdrvZ; pdrv = pdrv->Next)
{
if (pdrv->fdwDriver & MMDRV_DESERTED) continue;
if (cookie) {
// This is a wdm driver so we're matching up with cookie.
if (pdrv->cookie == cookie) break;
} else {
// ISSUE-2001/01/14-FrankYe Will this ever be called
// on non WDM driver???
// Not WDM driver, so matching up with pfnDrvMessage.
if (pdrv->drvMessage == pfnDrvMessage) break;
}
}
//
// If driver found, don't re-install.
//
if (pdrv != pdrvZ)
{
pdrv = NULL;
goto error_exit;
}
//
// Create a MMDRV for the device
//
pdrv = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbdrv);
if (!pdrv) goto error_exit;
//
// Initialize MMDRV structure
//
pdrv->hDriver = hDriver;
pdrv->NumDevs = 0;
pdrv->Usage = 1;
pdrv->cookie = cookie;
pdrv->fdwDriver = (wFlags & MMDRVI_MAPPER) ? MMDRV_MAPPER : 0;
pdrv->fdwDriver |= DrvIsPreXp(hDriver) ? MMDRV_PREXP : 0;
pdrv->drvMessage = pfnDrvMessage;
WinAssert(lstrlenA(szMessage) < sizeof(pdrv->wszMessage)/sizeof(WCHAR));
mbstowcs(pdrv->wszMessage, szMessage, sizeof(pdrv->wszMessage)/sizeof(WCHAR));
lstrcpyW(pdrv->wszDrvEntry, pszDriverFile);
if (!mmInitializeCriticalSection(&pdrv->MixerCritSec)) goto error_exit;
fMixerCritSec = TRUE;
//
// Sending init message
//
dw = pfnDrvMessage(0,msg_init,0L,0L,(DWORD_PTR)cookie);
//
// call driver to get num-devices it supports
//
dw = pfnDrvMessage(0,msg_num_devs,0L,(DWORD_PTR)cookie,0L);
//
// the device returned a error, or has no devices
//
if (0 != HIWORD(dw) || 0 == LOWORD(dw)) goto error_exit;
pdrv->NumDevs = LOWORD(dw);
wdmDevInterfaceInc(cookie);
// Squirt("Driver [%ls:0x%04x] supports %d devices", pszDriverFile, wFlags & MMDRVI_TYPE, dw);
//
// dont increment number of dev's for the mapper
//
if (!(pdrv->fdwDriver & MMDRV_MAPPER)) *pTotalDevs += pdrv->NumDevs;
//
// add to end of the driver list
//
mregAddDriver(pdrvZ, pdrv);
// Squirt("Installed driver");
return TRUE;
}
error_exit:
// ISSUE-2001/01/05-FrankYe On add, if msg_init was sent it might be good
// to also send DRVM_EXIT before closing the driver.
if (fMixerCritSec) DeleteCriticalSection(&pdrv->MixerCritSec);
if (pdrv) HeapFree(hHeap, 0, pdrv);
return FALSE;
}
void KickMapper
(
UINT uFlags
)
{
PMMDRV pmd;
DWORD dw;
DRIVERMSGPROC pfnDrvMessage = NULL;
MMRESULT mmr;
switch (uFlags & MMDRVI_TYPE)
{
case MMDRVI_WAVEOUT:
{
mmr = waveReferenceDriverById(&waveoutdrvZ, WAVE_MAPPER, &pmd, NULL);
break;
}
case MMDRVI_WAVEIN:
{
mmr = waveReferenceDriverById(&waveindrvZ, WAVE_MAPPER, &pmd, NULL);
break;
}
case MMDRVI_MIDIOUT:
{
mmr = midiReferenceDriverById(&midioutdrvZ, MIDI_MAPPER, &pmd, NULL);
break;
}
case MMDRVI_MIDIIN:
{
mmr = midiReferenceDriverById(&midiindrvZ, MIDI_MAPPER, &pmd, NULL);
break;
}
case MMDRVI_AUX:
{
mmr = auxReferenceDriverById(AUX_MAPPER, &pmd, NULL);
break;
}
case MMDRVI_MIXER:
{
#ifdef MIXER_MAPPER
mmr = mixerReferenceDriverById(MIXER_MAPPER, &pmd, NULL);
#else
mmr = MMSYSERR_NODRIVER;
#endif
break;
}
default:
WinAssert(FALSE);
mmr = MMSYSERR_NODRIVER;
return;
}
if (!mmr)
{
if (pmd->drvMessage)
{
pmd->drvMessage(0, DRVM_MAPPER_RECONFIGURE, 0L, 0L, 0L);
}
mregDecUsagePtr(pmd);
}
}
void wdmDriverLoadClass(
IN HKEY hkey,
IN PCTSTR DeviceInterface,
IN UINT uFlags,
IN OUT PTSTR *ppstrLeftOverDriver,
IN OUT HDRVR *phLeftOverDriver)
{
PTSTR pstrClass;
HKEY hkeyClass;
WinAssert((NULL == *ppstrLeftOverDriver) == (NULL == *phLeftOverDriver));
switch (uFlags & MMDRVI_TYPE) {
case MMDRVI_WAVEOUT:
case MMDRVI_WAVEIN:
pstrClass = TEXT("Drivers\\wave");
break;
case MMDRVI_MIDIOUT:
case MMDRVI_MIDIIN:
pstrClass = TEXT("Drivers\\midi");
break;
case MMDRVI_MIXER:
pstrClass = TEXT("Drivers\\mixer");
break;
case MMDRVI_AUX:
pstrClass = TEXT("Drivers\\aux");
break;
default:
pstrClass = NULL;
}
if (pstrClass && !RegOpenKeyEx(hkey, pstrClass, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkeyClass)) {
DWORD cSubkeys;
PTSTR pstrSubkeyNameBuffer;
DWORD cchSubkeyNameBuffer;
if (!RegPrepareEnum(hkeyClass, &cSubkeys, &pstrSubkeyNameBuffer, &cchSubkeyNameBuffer))
{
DWORD dwIndex;
for (dwIndex = 0; dwIndex < cSubkeys; dwIndex++) {
HKEY hkeyClassDriver;
if (!RegEnumOpenKey(hkeyClass, dwIndex, pstrSubkeyNameBuffer, cchSubkeyNameBuffer, KEY_QUERY_VALUE, &hkeyClassDriver))
{
PTSTR pstrDriver;
if (!RegQuerySzValue(hkeyClassDriver, TEXT("Driver"), &pstrDriver)) {
HDRVR h;
BOOL fLoaded = FALSE;
// dprintf(("wdmDriverLoadClass %s %ls on %ls", (uFlags & MMDRVI_REMOVE) ? "removing" : "installing", pstrClass, DeviceInterface));
EnterCriticalSection(&NumDevsCritSec);
if (!*phLeftOverDriver || lstrcmpi(pstrDriver, *ppstrLeftOverDriver))
{
if (*phLeftOverDriver)
{
DrvClose(*phLeftOverDriver, 0, 0);
HeapFree(hHeap, 0, *ppstrLeftOverDriver);
}
// dprintf(("wdmDriverLoadClass, opening driver %ls", pstrDriver));
h = mmDrvOpen(pstrDriver);
} else {
HeapFree(hHeap, 0, pstrDriver);
h = *phLeftOverDriver;
pstrDriver = *ppstrLeftOverDriver;
}
*phLeftOverDriver = NULL;
*ppstrLeftOverDriver = NULL;
if (h) {
fLoaded = wdmDrvInstall(h, pstrDriver, (HANDLE)DeviceInterface, uFlags | MMDRVI_HDRV);
} else {
HeapFree(hHeap, 0, pstrDriver);
pstrDriver = NULL;
}
// dprintf(("wdmDriverLoadClass, fLoaded = %s", fLoaded ? "TRUE" : "FALSE"));
if (!fLoaded)
{
*phLeftOverDriver = h;
*ppstrLeftOverDriver = pstrDriver;
}
LeaveCriticalSection(&NumDevsCritSec);
}
RegCloseKey(hkeyClassDriver);
}
}
HeapFree(hHeap, 0, pstrSubkeyNameBuffer);
}
RegCloseKey(hkeyClass);
}
}
void wdmDriverLoadAllClasses(IN PCTSTR DeviceInterface, UINT uFlags)
{
HKEY hkey = NULL;
LONG result;
// dprintf(("wdmDriverLoadAllClasses on %ls", DeviceInterface));
result = wdmDriverOpenDrvRegKey(DeviceInterface, KEY_ENUMERATE_SUB_KEYS, &hkey);
if (!result) {
HDRVR hUnusedDriver = NULL;
PTSTR pstrUnusedDriver = NULL;
WinAssert(hkey);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_WAVEOUT, &pstrUnusedDriver, &hUnusedDriver);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_WAVEIN, &pstrUnusedDriver, &hUnusedDriver);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_MIDIOUT, &pstrUnusedDriver, &hUnusedDriver);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_MIDIIN, &pstrUnusedDriver, &hUnusedDriver);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_AUX, &pstrUnusedDriver, &hUnusedDriver);
// wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_JOY);
wdmDriverLoadClass(hkey, DeviceInterface, uFlags | MMDRVI_MIXER, &pstrUnusedDriver, &hUnusedDriver);
if (hUnusedDriver) {
WinAssert(pstrUnusedDriver);
DrvClose(hUnusedDriver, 0, 0);
HeapFree(hHeap, 0, pstrUnusedDriver);
}
RegCloseKey(hkey);
} else {
dprintf(("wdmDriverLoadAllClasses: wdmDriverOpenDrvRegKey returned error %d", result));
}
return;
}
void wdmPnpUpdateDriver
(
DWORD dwType,
LPCWSTR pszID,
LONG cPnpEvents
)
{
HANDLE cookie;
cookie = wdmDevInterfaceInstall(pszID, cPnpEvents);
if(0 == cookie)
{
return;
}
if(WinmmRunningInServer)
{
Squirt("Running in CSRSS?!?!");
WinAssert(FALSE);
return;
}
// ISSUE-2001/01/16-FrankYe This violates the order in which locks should
// be acquired. The HandleListCritSec should be the last lock taken,
// but here it is held while calling other functions that will acquire
// NumDevsCritSec. I'm not sure why we need to acquire
// HandleListCritSec here.
EnterCriticalSection(&HandleListCritSec);
switch (dwType)
{
case DBT_DEVICEARRIVAL:
// Squirt("wdmPnpUpdateDriver:DBT_DEVICEARRIVAL [%ls]", pszID);
wdmDriverLoadAllClasses(cookie, 0);
break;
case DBT_DEVICEREMOVECOMPLETE:
// Squirt("wdmPnpUpdateDriver:DBT_DEVICEREMOVECOMPLETE [%ls]", pszID);
// ISSUE-2001/02/08-FrankYe I think we never DrvClose drivers anymore!
wdmDriverLoadAllClasses(cookie, MMDRVI_REMOVE);
break;
default:
break;
}
LeaveCriticalSection(&HandleListCritSec);
wdmDevInterfaceDec(cookie);
} // wdmPnpUpdateDriver()
void KickMappers
(
void
)
{
KickMapper(MMDRVI_WAVEOUT);
KickMapper(MMDRVI_WAVEIN);
KickMapper(MMDRVI_MIDIOUT);
KickMapper(MMDRVI_MIDIIN);
KickMapper(MMDRVI_AUX);
KickMapper(MMDRVI_MIXER);
}
BOOL ClientPnpChange(void)
{
BOOL fDeviceChange;
PMMPNPINFO pPnpInfo;
LONG cbPnpInfo;
PMMDEVICEINTERFACEINFO pdii;
UINT ii;
fDeviceChange = FALSE;
if (ERROR_SUCCESS != winmmGetPnpInfo(&cbPnpInfo, &pPnpInfo)) return fDeviceChange;
// Always grab NumDevsCriticalSection before DriverLoadFree CS
EnterCriticalSection(&NumDevsCritSec);
EnterCriticalSection(&DriverLoadFreeCritSec);
cPnpEvents = pPnpInfo->cPnpEvents;
// Adding new instances...
pdii = (PMMDEVICEINTERFACEINFO)&(pPnpInfo[1]);
pdii = PAD_POINTER(pdii);
for (ii = pPnpInfo->cDevInterfaces; ii; ii--)
{
PWDMDEVICEINTERFACE pwdmDev;
PWSTR pstr;
UINT jj;
pstr = &(pdii->szName[0]);
pwdmDev = wdmDevZ.Next;
while (pwdmDev)
{
WinAssert(pwdmDev->cUsage);
{
if (0 == lstrcmpi(pwdmDev->szDeviceInterface, pstr))
{
if (pdii->cPnpEvents > pwdmDev->cPnpEvents)
{
// if it has to be updated it must be removed first...
wdmPnpUpdateDriver(DBT_DEVICEREMOVECOMPLETE, pstr, 0);
if (0 == (pdii->fdwInfo & MMDEVICEINFO_REMOVED))
{
wdmPnpUpdateDriver(DBT_DEVICEARRIVAL, pstr, pdii->cPnpEvents);
}
fDeviceChange = TRUE;
}
break;
}
pwdmDev = pwdmDev->Next;
}
}
if (!pwdmDev)
{
// Device interface should be installed.
if (0 == (pdii->fdwInfo & MMDEVICEINFO_REMOVED))
{
wdmPnpUpdateDriver(DBT_DEVICEARRIVAL, pstr, pdii->cPnpEvents);
}
fDeviceChange = TRUE;
}
pdii = (PMMDEVICEINTERFACEINFO)(pstr + lstrlenW(pstr) + 1);
pdii = PAD_POINTER(pdii);
pstr = (PWSTR)(&pdii[1]);
}
LeaveCriticalSection(&DriverLoadFreeCritSec);
LeaveCriticalSection(&NumDevsCritSec);
HeapFree(hHeap, 0, pPnpInfo);
return fDeviceChange;
}
void ClientUpdatePnpInfo(void)
{
static BOOL fFirstCall = TRUE;
static BOOL InThisFunction = FALSE;
BOOL fWasFirstCall;
if (IsWinlogon() && !gfLogon)
{
dprintf(("ClientUpdatePnpInfo: warning: called in winlogon before logged on"));
return;
}
fWasFirstCall = InterlockedExchange(&fFirstCall, FALSE);
if (fWasFirstCall)
{
// Note AudioSrvBinding happens in WinmmLogon for winlogon
winmmWaitForService();
if (!IsWinlogon()) AudioSrvBinding();
if (NULL == pClientPnpInfo) {
hClientPnpInfo = OpenFileMapping(FILE_MAP_READ, FALSE, MMGLOBALPNPINFONAME);
if (hClientPnpInfo) {
pClientPnpInfo = MapViewOfFile(hClientPnpInfo, FILE_MAP_READ, 0, 0, 0);
if (!pClientPnpInfo) {
CloseHandle(hClientPnpInfo);
hClientPnpInfo = NULL;
}
}
if (!hClientPnpInfo) dprintf(("ClientUpdatePnpInfo: WARNING: Could not OpenFileMapping"));
}
SetEvent(hEventApiInit);
} else {
WaitForSingleObjectEx(hEventApiInit, INFINITE, FALSE);
}
EnterCriticalSection(&PnpCritSec);
if (!InterlockedExchange(&InThisFunction, TRUE))
{
BOOL fDeviceChange;
BOOL fPreferredDeviceChange;
fPreferredDeviceChange = CheckSessionChanged();
fDeviceChange = FALSE;
if (pClientPnpInfo && (cPnpEvents != pClientPnpInfo->cPnpEvents)) fDeviceChange = ClientPnpChange();
if (fDeviceChange) InvalidatePreferredDevices();
fPreferredDeviceChange |= (pClientPnpInfo && (cPreferredDeviceChanges != pClientPnpInfo->cPreferredDeviceChanges));
if (fPreferredDeviceChange && pClientPnpInfo) cPreferredDeviceChanges = pClientPnpInfo->cPreferredDeviceChanges;
if (fWasFirstCall || fDeviceChange || fPreferredDeviceChange) RefreshPreferredDevices();
if (fDeviceChange) KickMappers();
InterlockedExchange(&InThisFunction, FALSE);
}
LeaveCriticalSection(&PnpCritSec);
}
void WinmmLogon(BOOL fConsole)
{
// dprintf(("WinmmLogon (%s session)", fConsole ? "console" : "remote"));
WinAssert(IsWinlogon());
WinAssert(!gfLogon);
// WinAssert(fConsole ? !WinmmRunningInSession : WinmmRunningInSession);
if (!IsWinlogon()) return;
AudioSrvBinding();
gfLogon = TRUE;
// ISSUE-2001/05/04-FrankYe This is a NOP now, should remove this and
// implementation in audiosrv.
gfxLogon(GetCurrentProcessId());
return;
}
void WinmmLogoff(void)
{
HANDLE handle;
// dprintf(("WinmmLogoff"));
WinAssert(IsWinlogon());
WinAssert(gfLogon);
if (!IsWinlogon()) return;
gfxLogoff();
// It is very important to close this context handle now because it is associated
// with the logged on user. Otherwise the handle remains open, associated with the
// logged on user, even after he logs off.
if (ghSessionNotification)
{
WinAssert(ghSessionNotificationEvent);
winmmUnregisterSessionNotification(ghSessionNotification);
CloseHandle(ghSessionNotificationEvent);
ghSessionNotification = NULL;
ghSessionNotificationEvent = NULL;
}
else
{
WinAssert(!ghSessionNotificationEvent);
}
AudioSrvBindingFree();
gfLogon = FALSE;
return;
}
/*
*************************************************************************
* MigrateSoundEvents
*
* Description:
* Looks at the sounds section in win.ini for sound entries.
* Gets a current scheme name from the current section in control.ini
* Failing that it tries to find the current scheme in the registry
* Failing that it uses .default as the current scheme.
* Copies each of the entries in the win.ini sound section into the
* registry under the scheme name obtained
* If the scheme name came from control.ini, it creates a key from the
* scheme name. This key is created by removing all the existing spaces
* in the scheme name. This key and scheme name is added to the registry
*
*************************************************************************
*/
// ISSUE-2000/10/30-FrankYe Delete Winlogon's call to this function, then
// delete this function
void MigrateAllDrivers(void)
{
return;
}
void MigrateSoundEvents (void)
{
TCHAR aszEvent[SCH_TYPE_MAX_LENGTH];
// If a MediaPathUnexpanded key exists (it will be something
// like "%SystemRoot%\Media"), expand it into a fully-qualified
// path and write out a matching MediaPath key (which will look
// like "c:\win\media"). This is done every time we enter the
// migration path, whether or not there's anything else to do.
//
// Setup would like to write the MediaPath key with the
// "%SystemRoot%" stuff still in it--but while we could touch
// our apps to understand expansion, any made-for-Win95 apps
// probably wouldn't think to expand the string, and so wouldn't
// work properly. Instead, it writes the MediaPathUnexpanded
// key, and we make sure that the MediaPath key is kept up-to-date
// in the event that the Windows drive gets remapped (isn't
// NT cool that way?).
//
if (mmRegQueryMachineValue (aszSetup, aszValMediaUnexpanded,
cchLENGTH(aszEvent), aszEvent))
{
WCHAR szExpanded[MAX_PATH];
ExpandEnvironmentStrings (aszEvent, szExpanded, cchLENGTH(szExpanded));
mmRegSetMachineValue (aszSetup, aszValMedia, szExpanded);
}
}
int lstrncmpi (LPTSTR pszA, LPTSTR pszB, size_t cch)
{
#ifdef UNICODE
size_t cchA, cchB;
TCHAR *pch;
for (cchA = 1, pch = pszA; cchA < cch; cchA++, pch++)
{
if (*pch == TEXT('\0'))
break;
}
for (cchB = 1, pch = pszB; cchB < cch; cchB++, pch++)
{
if (*pch == TEXT('\0'))
break;
}
return (CompareStringW (GetThreadLocale(), NORM_IGNORECASE,
pszA, cchA, pszB, cchB)
)-2; // CompareStringW returns {1,2,3} instead of {-1,0,1}.
#else
return strnicmp (pszA, pszB, cch);
#endif
}
#if DBG
void Squirt(LPSTR lpszFormat, ...)
{
char buf[512];
UINT n;
va_list va;
n = wsprintfA(buf, "WINMM: (pid %x) ", GetCurrentProcessId());
va_start(va, lpszFormat);
n += vsprintf(buf+n, lpszFormat, va);
va_end(va);
buf[n++] = '\n';
buf[n] = 0;
OutputDebugStringA(buf);
Sleep(0); // let terminal catch up
}
#else
void Squirt(LPSTR lpszFormat, ...)
{
}
#endif