1819 lines
52 KiB
C++
1819 lines
52 KiB
C++
//--------------------------------------------------------------------------;
|
||
//
|
||
// File: volopt.cpp
|
||
//
|
||
// Copyright (c) 1998 Microsoft Corporation. All rights reserved
|
||
//
|
||
//--------------------------------------------------------------------------;
|
||
|
||
#include "precomp.h"
|
||
#include <regstr.h>
|
||
#include <mmsystem.h>
|
||
#include <mmddk.h>
|
||
#include "optres.h"
|
||
#include "cdopti.h"
|
||
#include "cdoptimp.h"
|
||
#include "helpids.h"
|
||
#include "winbase.h"
|
||
|
||
//////////////
|
||
// Help ID's
|
||
//////////////
|
||
|
||
#pragma data_seg(".text")
|
||
const static DWORD aVolOptsHelp[] =
|
||
{
|
||
IDC_VOLCONFIG_ICON, IDH_VOL_MSG,
|
||
IDC_VOL_MSG_TEXT, IDH_VOL_MSG,
|
||
IDC_VOL_CONFIG_GROUP, IDH_VOL_MSG,
|
||
IDC_DEFAULTMIXER, IDH_USEMIXERDEFAULTS,
|
||
IDC_SELECTPLAYER_TEXT, IDH_SELECTCDPLAYER,
|
||
IDC_CDDRIVE, IDH_SELECTCDPLAYER,
|
||
IDC_SELECTMIXER_TEXT, IDH_SELECTCDMIXER,
|
||
IDC_AUDIOMIXER, IDH_SELECTCDMIXER,
|
||
IDC_SELECTCONTROL_TEXT, IDH_SELECTCDCONTROL,
|
||
IDC_AUDIOCONTROL, IDH_SELECTCDCONTROL,
|
||
0, 0
|
||
};
|
||
#pragma data_seg()
|
||
|
||
////////////
|
||
// Types
|
||
////////////
|
||
|
||
typedef struct CDCTL // Used to write to reg (don't change)
|
||
{
|
||
DWORD dwVolID;
|
||
DWORD dwMuteID;
|
||
DWORD dwDestID;
|
||
|
||
} CDCTL, *LPCDCTL;
|
||
|
||
|
||
|
||
////////////
|
||
// Globals
|
||
////////////
|
||
#define MYREGSTR_PATH_MEDIA TEXT("SYSTEM\\CurrentControlSet\\Control\\MediaResources")
|
||
const TCHAR gszRegstrCDAPath[] = MYREGSTR_PATH_MEDIA TEXT("\\mci\\cdaudio");
|
||
const TCHAR gszDefaultCDA[] = TEXT("Default Drive");
|
||
|
||
const TCHAR szRegstrCDROMPath[] = TEXT("System\\CurrentControlSet\\Services\\Class\\");
|
||
const TCHAR szPrefMixer[] = TEXT("Preferred Mixer");
|
||
const TCHAR szPrefControls[] = TEXT("Preferred Controls");
|
||
const TCHAR szSelected[] = TEXT("Selected");
|
||
|
||
const TCHAR szMapperPath[] = TEXT("Software\\Microsoft\\Multimedia\\Sound Mapper");
|
||
const TCHAR szPlayback[] = TEXT("Playback");
|
||
const TCHAR szPreferredOnly[] = TEXT("PreferredOnly");
|
||
|
||
const TCHAR szNTCDROMPath[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\DeluxeCD\\Settings\\");
|
||
|
||
|
||
|
||
////////////
|
||
// Functions
|
||
////////////
|
||
|
||
|
||
/////////////
|
||
// Uses new winmm feature to get the preferred wave ID, much cleaner.
|
||
//
|
||
STDMETHODIMP_(MMRESULT) CCDOpt::GetDefaultMixID(DWORD *pdwMixID)
|
||
{
|
||
MMRESULT mmr;
|
||
DWORD dwWaveID;
|
||
DWORD dwFlags = 0;
|
||
UINT dwMixID = 0;
|
||
|
||
mmr = waveOutMessage((HWAVEOUT)(UINT_PTR)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &dwWaveID, (DWORD_PTR) &dwFlags);
|
||
|
||
if (!mmr && pdwMixID)
|
||
{
|
||
mmr = mixerGetID((HMIXEROBJ)ULongToPtr(dwWaveID), &dwMixID, MIXER_OBJECTF_WAVEOUT);
|
||
|
||
if(MMSYSERR_NOERROR == mmr)
|
||
{
|
||
*pdwMixID = dwMixID;
|
||
}
|
||
}
|
||
|
||
return(mmr);
|
||
}
|
||
|
||
|
||
///////////
|
||
// A mixer line has been found with controls, this function is called to either 1) verify
|
||
// that passed in VolID and MuteID's can be found on this mixer line and are of the right
|
||
// control type. or 2) to find the Volume Slider and Mute ID controls that do exist on
|
||
// this mixer line.
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::SearchControls(int mxid, LPMIXERLINE pml, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL *pfFound, BOOL fVerify)
|
||
{
|
||
MIXERLINECONTROLS mlc;
|
||
DWORD dwControl;
|
||
|
||
memset(&mlc, 0, sizeof(mlc));
|
||
mlc.cbStruct = sizeof(mlc);
|
||
mlc.dwLineID = pml->dwLineID;
|
||
mlc.cControls = pml->cControls;
|
||
mlc.cbmxctrl = sizeof(MIXERCONTROL);
|
||
mlc.pamxctrl = (LPMIXERCONTROL) new(MIXERCONTROL[pml->cControls]);
|
||
|
||
if (mlc.pamxctrl)
|
||
{
|
||
if (mixerGetLineControls((HMIXEROBJ)IntToPtr(mxid), &mlc, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
|
||
{
|
||
for (dwControl = 0; dwControl < pml->cControls && !(*pfFound); dwControl++)
|
||
{
|
||
if (mlc.pamxctrl[dwControl].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_VOLUME)
|
||
{
|
||
DWORD dwIndex;
|
||
DWORD dwVolID = DWORD(-1);
|
||
DWORD dwMuteID = DWORD(-1);
|
||
|
||
dwVolID = mlc.pamxctrl[dwControl].dwControlID;
|
||
|
||
for (dwIndex = 0; dwIndex < pml->cControls; dwIndex++)
|
||
{
|
||
if (mlc.pamxctrl[dwIndex].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_MUTE)
|
||
{
|
||
dwMuteID = mlc.pamxctrl[dwIndex].dwControlID;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (fVerify)
|
||
{
|
||
if (*pdwVolID == dwVolID && *pdwMuteID == dwMuteID)
|
||
{
|
||
if (szName)
|
||
{
|
||
lstrcpy(szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
|
||
}
|
||
|
||
*pfFound = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (szName)
|
||
{
|
||
lstrcpy(szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
|
||
}
|
||
|
||
*pfFound = TRUE;
|
||
*pdwVolID = dwVolID;
|
||
*pdwMuteID = dwMuteID;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
delete mlc.pamxctrl;
|
||
}
|
||
}
|
||
|
||
///////////////
|
||
// If a mixer line has connects, this function is called to enumerate all lines that have controls
|
||
// that meet our criteria and then seek out the controls on those connections using the above SearchControls
|
||
// function.
|
||
//
|
||
// NOTE: This function makes two scans over the connections, first looking for CompactDisc lines, and then
|
||
// if unsuccessful, it makes a second scan looking for other lines that might have a CD connected, like line-in
|
||
// and aux lines.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::SearchConnections(int mxid, DWORD dwDestination, DWORD dwConnections, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL *pfFound, BOOL fVerify)
|
||
{
|
||
MIXERLINE mlDst;
|
||
DWORD dwConnection;
|
||
DWORD dwScan;
|
||
|
||
for (dwScan = 0; dwScan < 2 && !(*pfFound); dwScan++) // On first scan look for CD, on second scan, look for anything else.
|
||
{
|
||
for (dwConnection = 0; dwConnection < dwConnections && !(*pfFound); dwConnection++)
|
||
{
|
||
mlDst.cbStruct = sizeof ( mlDst );
|
||
mlDst.dwDestination = dwDestination;
|
||
mlDst.dwSource = dwConnection;
|
||
|
||
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
|
||
{
|
||
if (mlDst.cControls) // Make sure this source has controls on it
|
||
{
|
||
if (((dwScan == 0) && (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC)) ||
|
||
((dwScan == 1) && (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_LINE ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_DIGITAL ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_ANALOG)))
|
||
{
|
||
SearchControls(mxid, &mlDst, pdwVolID, pdwMuteID, szName, pfFound, fVerify);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/////////////////
|
||
// Used in two modes, fVerify is TRUE, the VolID and MuteID are inputs and will return TRUE if valid
|
||
// if not in verification mode, this function is used to compute the default vol and mute ID's for this device.
|
||
// It scans all the destinations on the Mixer looking for output destinations (speakers, headphones, etc)
|
||
// Once it finds them, it then Searchs for controls on itself and/or any connections itself.
|
||
//
|
||
// NOTE: The current default behavior is to locate CD type connections, and then, if not finding any that
|
||
// work, to attempt to use the destination master volume. To reverse this, look for master volume first, and then
|
||
// look for CD lines if master can't be found, Switch the two intermost If conditions and calls so that
|
||
// SearchControls on the line happens before the connections are searched.
|
||
|
||
STDMETHODIMP_(BOOL) CCDOpt::SearchDevice(DWORD dwMixID, LPCDUNIT pCDUnit, LPDWORD pdwDestID, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL fVerify)
|
||
{
|
||
MIXERCAPS mc;
|
||
MMRESULT mmr;
|
||
BOOL fFound = FALSE;
|
||
|
||
mmr = mixerGetDevCaps(dwMixID, &mc, sizeof(mc));
|
||
|
||
if (mmr == MMSYSERR_NOERROR)
|
||
{
|
||
MIXERLINE mlDst;
|
||
DWORD dwDestination;
|
||
DWORD cDestinations;
|
||
|
||
if (pCDUnit)
|
||
{
|
||
lstrcpy(pCDUnit->szMixerName, mc.szPname);
|
||
}
|
||
|
||
dwDestination = 0; // Setup loop to check all destinations
|
||
cDestinations = mc.cDestinations;
|
||
|
||
if (fVerify) // If in Verify mode, only check the specified destination ID
|
||
{
|
||
dwDestination = *pdwDestID;
|
||
cDestinations = dwDestination + 1;
|
||
}
|
||
|
||
for ( ; dwDestination < cDestinations && !fFound; dwDestination++)
|
||
{
|
||
mlDst.cbStruct = sizeof ( mlDst );
|
||
mlDst.dwDestination = dwDestination;
|
||
|
||
if (mixerGetLineInfo((HMIXEROBJ)ULongToPtr(dwMixID), &mlDst, MIXER_GETLINEINFOF_DESTINATION ) == MMSYSERR_NOERROR)
|
||
{
|
||
if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || // needs to be a likely output destination
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT)
|
||
{
|
||
// Note: To default to Master Volume (instead of CD volume) just SearchControls first.
|
||
|
||
if (!fFound && mlDst.cConnections) // only look at connections if there is no master slider control.
|
||
{
|
||
SearchConnections(dwMixID, dwDestination, mlDst.cConnections, pdwVolID, pdwMuteID, szName, &fFound, fVerify);
|
||
}
|
||
|
||
if (!fFound && mlDst.cControls) // If there are controls, we'll take the master
|
||
{
|
||
SearchControls(dwMixID, &mlDst, pdwVolID, pdwMuteID, szName, &fFound, fVerify);
|
||
}
|
||
|
||
*pdwDestID = dwDestination;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return(fFound);
|
||
}
|
||
|
||
|
||
////////////////
|
||
// When a new CD is found, and there are no valid enteries in the registry for it, we compute
|
||
// defaults for that unit. This function uses the above function SearchDevice to do just that.
|
||
// First it finds the preferred MixerID and assumes this CD is connected to it, this it finds
|
||
// the default controls on that mixer and assignes them.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::GetUnitDefaults(LPCDUNIT pCDUnit)
|
||
{
|
||
if (pCDUnit)
|
||
{
|
||
pCDUnit->dwMixID = DWORD(-1);
|
||
pCDUnit->dwVolID = DWORD(-1);
|
||
pCDUnit->dwMuteID = DWORD(-1);
|
||
pCDUnit->dwDestID = DWORD(-1);
|
||
|
||
if (GetDefaultMixID(&pCDUnit->dwMixID) == MMSYSERR_NOERROR)
|
||
{
|
||
SearchDevice(pCDUnit->dwMixID, pCDUnit, &pCDUnit->dwDestID, &pCDUnit->dwVolID, &pCDUnit->dwMuteID, pCDUnit->szVolName, FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//////////////
|
||
// This function enumerates installed disk devices that are of type CDROM and are installed
|
||
// It seeks out the one that matches the specified drive letter and collects information about it
|
||
// It returns the class driver path and the descriptive name of the drive.
|
||
//
|
||
// NOTE This function assumes both szDriver and szDevDesc are the dwSize bytes each.
|
||
//
|
||
STDMETHODIMP_(BOOL) CCDOpt::MapLetterToDevice(TCHAR DriveLetter, TCHAR *szDriver, TCHAR *szDevDesc, DWORD dwSize)
|
||
{
|
||
HKEY hkEnum;
|
||
BOOL fResult = FALSE;
|
||
|
||
if (!RegOpenKey(HKEY_DYN_DATA, REGSTR_PATH_DYNA_ENUM, &hkEnum))
|
||
{
|
||
HKEY hkDev;
|
||
DWORD dwEnumDevCnt;
|
||
TCHAR aszCMKey[MAX_PATH];
|
||
BOOLEAN found = FALSE;
|
||
|
||
for (dwEnumDevCnt = 0; !found && !RegEnumKey(hkEnum, dwEnumDevCnt, aszCMKey, sizeof(aszCMKey)/sizeof(TCHAR)); dwEnumDevCnt++)
|
||
{
|
||
if (!RegOpenKey(hkEnum, aszCMKey, &hkDev))
|
||
{
|
||
TCHAR aszDrvKey[MAX_PATH];
|
||
TCHAR tmp;
|
||
DWORD cb = sizeof(aszDrvKey);
|
||
|
||
RegQueryValueEx(hkDev, REGSTR_VAL_HARDWARE_KEY, NULL, NULL, (LPBYTE)&aszDrvKey, &cb);
|
||
|
||
tmp = aszDrvKey[5];
|
||
aszDrvKey[5] = TEXT('\0');
|
||
|
||
if ( !lstrcmpi( REGSTR_VAL_SCSI, aszDrvKey ) )
|
||
{
|
||
HKEY hkDrv;
|
||
TCHAR aszEnumKey[MAX_PATH];
|
||
aszDrvKey[5] = tmp;
|
||
|
||
wsprintf(aszEnumKey, TEXT("Enum\\%s"), aszDrvKey);
|
||
|
||
if (!RegOpenKey(HKEY_LOCAL_MACHINE, aszEnumKey, &hkDrv))
|
||
{
|
||
TCHAR DrvLet[3];
|
||
|
||
cb = sizeof( DrvLet );
|
||
|
||
RegQueryValueEx(hkDrv, REGSTR_VAL_CURDRVLET, NULL, NULL, (LPBYTE)&DrvLet, &cb);
|
||
|
||
if ( DrvLet[0] == DriveLetter )
|
||
{
|
||
DWORD cb2 = dwSize;
|
||
cb = dwSize;
|
||
|
||
if ((RegQueryValueEx(hkDrv, REGSTR_VAL_DEVDESC, NULL, NULL, (LPBYTE)szDevDesc, &cb) == NO_ERROR) &&
|
||
(RegQueryValueEx(hkDrv, REGSTR_VAL_DRIVER, NULL, NULL, (LPBYTE)szDriver, &cb2) == NO_ERROR))
|
||
{
|
||
fResult = TRUE;
|
||
}
|
||
|
||
found = TRUE;
|
||
}
|
||
|
||
RegCloseKey(hkDrv);
|
||
}
|
||
}
|
||
|
||
RegCloseKey(hkDev);
|
||
}
|
||
}
|
||
|
||
RegCloseKey(hkEnum);
|
||
}
|
||
|
||
if (!fResult)
|
||
{
|
||
//check to see if we're on NT
|
||
OSVERSIONINFO os;
|
||
os.dwOSVersionInfoSize = sizeof(os);
|
||
GetVersionEx(&os);
|
||
if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||
{
|
||
TCHAR szDrive[MAX_PATH];
|
||
TCHAR szDriverTemp[MAX_PATH*2];
|
||
TCHAR* szGUID = NULL;
|
||
ZeroMemory(szDriverTemp,sizeof(szDriverTemp));
|
||
ZeroMemory(szDriver,dwSize);
|
||
|
||
wsprintf(szDrive,TEXT("%c:\\"),DriveLetter);
|
||
if (GetVolumeNameForVolumeMountPoint(szDrive,szDriverTemp,dwSize/sizeof(TCHAR)))
|
||
{
|
||
//szDriverTemp now has a string like \\?\Volume{GUID}\ ... we just want
|
||
//the {GUID} part, as that is the drive's unique ID.
|
||
szGUID = _tcschr(szDriverTemp,TEXT('{'));
|
||
if (szGUID!=NULL)
|
||
{
|
||
fResult = TRUE;
|
||
_tcscpy(szDriver,szGUID);
|
||
if (szDriver[_tcslen(szDriver)-1] != TEXT('}'))
|
||
{
|
||
szDriver[_tcslen(szDriver)-1] = TEXT('\0');
|
||
}
|
||
}
|
||
}
|
||
} //end if NT
|
||
}
|
||
|
||
return(fResult);
|
||
}
|
||
|
||
|
||
///////////////
|
||
// Since audio devices can come and go, the names of the devices can change since there is
|
||
// number appended by the system inclosed in brackets at the end of the string.
|
||
// This function attempts to truncate this appended number after copying the original into
|
||
// the destination buffer.
|
||
//
|
||
STDMETHODIMP_(BOOL) CCDOpt::TruncName(TCHAR *pDest, TCHAR *pSrc)
|
||
{
|
||
BOOL fSuccess = TRUE;
|
||
|
||
TCHAR *pTrunc;
|
||
|
||
lstrcpy(pDest, pSrc);
|
||
|
||
pTrunc = pDest;
|
||
while (*pTrunc++);
|
||
|
||
while (pTrunc-- > pDest)
|
||
{
|
||
if (*pTrunc == TEXT('['))
|
||
{
|
||
*pTrunc = TEXT('\0');
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (pTrunc == pDest)
|
||
{
|
||
fSuccess = FALSE;
|
||
}
|
||
|
||
return(fSuccess);
|
||
}
|
||
|
||
|
||
/////////////////
|
||
// After reading the preferred mixer device name from the registry, we have to locate that
|
||
// device in the system by mixer ID. To do this, we scan thru all available mixers looking
|
||
// for an exact match of the name. If an exact match can not be found, we then scan again
|
||
// looking for a match using truncated strings where the system appended instance number is
|
||
// removed. If we can't find it at that point, we are in trouble and have to give up.
|
||
//
|
||
STDMETHODIMP CCDOpt::ComputeMixID(LPDWORD pdwMixID, TCHAR *szMixerName)
|
||
{
|
||
HRESULT hr = E_FAIL;
|
||
DWORD dwNumMixers;
|
||
DWORD dwID;
|
||
MMRESULT mmr;
|
||
BOOL fFound = FALSE;
|
||
TCHAR szTruncName[MAXPNAMELEN];
|
||
TCHAR szTruncSeek[MAXPNAMELEN];
|
||
MIXERCAPS mc;
|
||
|
||
dwNumMixers = mixerGetNumDevs();
|
||
|
||
for (dwID = 0; dwID < dwNumMixers && !fFound; dwID++) // First look for EXACT match.
|
||
{
|
||
mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
|
||
|
||
if (mmr == MMSYSERR_NOERROR)
|
||
{
|
||
if (!lstrcmp(mc.szPname, szMixerName))
|
||
{
|
||
hr = S_OK;
|
||
fFound = TRUE;
|
||
*pdwMixID = dwID;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!fFound) // Exact match isn't found, strip off (#) and look again
|
||
{
|
||
if (TruncName(szTruncName, szMixerName))
|
||
{
|
||
for (dwID = 0; dwID < dwNumMixers && !fFound; dwID++)
|
||
{
|
||
mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
|
||
|
||
if (mmr == MMSYSERR_NOERROR)
|
||
{
|
||
TruncName(szTruncSeek, mc.szPname);
|
||
|
||
if (!lstrcmp(szTruncSeek, szTruncName))
|
||
{
|
||
lstrcpy(szMixerName, mc.szPname); // repair the name we matched
|
||
hr = S_OK;
|
||
fFound = TRUE;
|
||
*pdwMixID = dwID;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
///////////////
|
||
// This function looks up this devices registry information using the driver information that
|
||
// was mapped from the drive letter. It attempts to read in the preferred mixer ID and then
|
||
// calculate the mixerID from it, if that fails, the function fails and the unit will use default
|
||
// information calcuated by this program. If it succeeds, it then reads in the control ID's for
|
||
// the volume and mute controls for this mixer. If it can't find them or the ones it does find
|
||
// can not be located on the specified device, then we compute defaults. If we can't compute defaults
|
||
// the entire function fails and the entire drive is re-computed.
|
||
//
|
||
STDMETHODIMP CCDOpt::GetUnitRegData(LPCDUNIT pCDUnit)
|
||
{
|
||
HRESULT hr = E_FAIL;
|
||
TCHAR szRegDriverStr[MAX_PATH];
|
||
HKEY hKey;
|
||
DWORD dwMixID;
|
||
CDCTL cdCtl;
|
||
HKEY hKeyRoot = HKEY_LOCAL_MACHINE;
|
||
|
||
if (pCDUnit)
|
||
{
|
||
OSVERSIONINFO os;
|
||
os.dwOSVersionInfoSize = sizeof(os);
|
||
GetVersionEx(&os);
|
||
if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||
{
|
||
hKeyRoot = HKEY_CURRENT_USER;
|
||
wsprintf(szRegDriverStr,TEXT("%s%s"),szNTCDROMPath, pCDUnit->szDriver);
|
||
}
|
||
else
|
||
{
|
||
wsprintf(szRegDriverStr,TEXT("%s%s"),szRegstrCDROMPath, pCDUnit->szDriver);
|
||
}
|
||
|
||
if (RegOpenKeyEx(hKeyRoot , szRegDriverStr , 0 , KEY_READ , &hKey) == ERROR_SUCCESS)
|
||
{
|
||
DWORD dwSize = sizeof(BOOL);
|
||
|
||
RegQueryValueEx(hKey, szSelected, NULL, NULL, (LPBYTE) &(pCDUnit->fSelected), &dwSize);
|
||
|
||
dwSize = MAXPNAMELEN*sizeof(TCHAR);
|
||
|
||
if (RegQueryValueEx(hKey, szPrefMixer, NULL, NULL, (LPBYTE) pCDUnit->szMixerName, &dwSize) == NO_ERROR)
|
||
{
|
||
hr = ComputeMixID(&dwMixID, pCDUnit->szMixerName);
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
BOOL fGotControls = FALSE;
|
||
TCHAR szVolName[MIXER_LONG_NAME_CHARS] = TEXT("\0");
|
||
|
||
dwSize = sizeof(cdCtl);
|
||
|
||
cdCtl.dwDestID = DWORD(-1);
|
||
cdCtl.dwMuteID = DWORD(-1);
|
||
cdCtl.dwVolID = DWORD(-1);
|
||
|
||
if (RegQueryValueEx(hKey, szPrefControls, NULL, NULL, (LPBYTE) &cdCtl, &dwSize) == NO_ERROR)
|
||
{
|
||
fGotControls = SearchDevice(dwMixID, NULL, &cdCtl.dwDestID, &cdCtl.dwVolID, &cdCtl.dwMuteID, szVolName, TRUE); // Verify Controls
|
||
}
|
||
|
||
if (!fGotControls) // Either were not in reg or fail verification, compute defaults for this device
|
||
{
|
||
fGotControls = SearchDevice(dwMixID, NULL, &cdCtl.dwDestID, &cdCtl.dwVolID, &cdCtl.dwMuteID, szVolName, FALSE);
|
||
}
|
||
|
||
if (!fGotControls) // If we don't have them by now, we are in trouble.
|
||
{
|
||
hr = E_FAIL;
|
||
}
|
||
else
|
||
{
|
||
pCDUnit->dwMixID = dwMixID;
|
||
pCDUnit->dwDestID = cdCtl.dwDestID;
|
||
pCDUnit->dwVolID = cdCtl.dwVolID;
|
||
pCDUnit->dwMuteID = cdCtl.dwMuteID;
|
||
lstrcpy(pCDUnit->szVolName, szVolName);
|
||
}
|
||
}
|
||
}
|
||
|
||
RegCloseKey(hKey);
|
||
}
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
////////////////
|
||
// This function writes out the preferred mixer device name and the preferred control ID's into the
|
||
// cdrom driver registry for safe keeping.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::SetUnitRegData(LPCDUNIT pCDUnit)
|
||
{
|
||
HRESULT hr = E_FAIL;
|
||
TCHAR szRegDriverStr[MAX_PATH];
|
||
HKEY hKey;
|
||
CDCTL cdCtl;
|
||
|
||
//this function is very different on NT
|
||
OSVERSIONINFO os;
|
||
os.dwOSVersionInfoSize = sizeof(os);
|
||
GetVersionEx(&os);
|
||
if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||
{
|
||
if (pCDUnit)
|
||
{
|
||
wsprintf(szRegDriverStr,TEXT("%s%s"),szNTCDROMPath, pCDUnit->szDriver);
|
||
|
||
if (RegCreateKeyEx(HKEY_CURRENT_USER , szRegDriverStr , 0 , NULL, 0, KEY_WRITE , NULL, &hKey, NULL) == ERROR_SUCCESS)
|
||
{
|
||
cdCtl.dwVolID = pCDUnit->dwVolID;
|
||
cdCtl.dwMuteID = pCDUnit->dwMuteID;
|
||
cdCtl.dwDestID = pCDUnit->dwDestID;
|
||
|
||
RegSetValueEx(hKey, szPrefMixer, NULL, REG_SZ, (LPBYTE) pCDUnit->szMixerName, (sizeof(TCHAR) * lstrlen(pCDUnit->szMixerName))+sizeof(TCHAR));
|
||
RegSetValueEx(hKey, szPrefControls, NULL, REG_BINARY, (LPBYTE) &cdCtl, sizeof(cdCtl));
|
||
RegSetValueEx(hKey, szSelected, NULL, REG_BINARY, (LPBYTE) &(pCDUnit->fSelected), sizeof(BOOL));
|
||
|
||
RegCloseKey(hKey);
|
||
}
|
||
}
|
||
} //end if NT
|
||
else
|
||
{
|
||
if (pCDUnit)
|
||
{
|
||
wsprintf(szRegDriverStr,TEXT("%s%s"),szRegstrCDROMPath, pCDUnit->szDriver);
|
||
|
||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE , szRegDriverStr , 0 , KEY_WRITE , &hKey) == ERROR_SUCCESS)
|
||
{
|
||
cdCtl.dwVolID = pCDUnit->dwVolID;
|
||
cdCtl.dwMuteID = pCDUnit->dwMuteID;
|
||
cdCtl.dwDestID = pCDUnit->dwDestID;
|
||
|
||
RegSetValueEx(hKey, szPrefMixer, NULL, REG_SZ, (LPBYTE) pCDUnit->szMixerName, (sizeof(TCHAR) * lstrlen(pCDUnit->szMixerName))+sizeof(TCHAR));
|
||
RegSetValueEx(hKey, szPrefControls, NULL, REG_BINARY, (LPBYTE) &cdCtl, sizeof(cdCtl));
|
||
RegSetValueEx(hKey, szSelected, NULL, REG_BINARY, (LPBYTE) &(pCDUnit->fSelected), sizeof(BOOL));
|
||
|
||
RegCloseKey(hKey);
|
||
}
|
||
}
|
||
} //end else Win9x
|
||
}
|
||
|
||
|
||
//////////////////
|
||
// Given just the drive letter of a CDROM, this function will obtain all information needed by the
|
||
// program on this drive. Some is calcuated via the PnP registry for this drive's driver, other information
|
||
// is obtained from the registry or defaults are computed based on the installed audio components
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::GetUnitValues(LPCDUNIT pCDUnit)
|
||
{
|
||
if (pCDUnit)
|
||
{
|
||
TCHAR cDriveLetter = pCDUnit->szDriveName[0];
|
||
|
||
if (MapLetterToDevice(cDriveLetter, pCDUnit->szDriver, pCDUnit->szDeviceDesc, sizeof(pCDUnit->szDriver)))
|
||
{
|
||
if (FAILED(GetUnitRegData(pCDUnit))) // Can fail if reg is empty or mixer ID can't be computed
|
||
{
|
||
GetUnitDefaults(pCDUnit);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GetUnitDefaults(pCDUnit);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//////////////////
|
||
// This method is called in response to a PnP notify or a WinMM Device Change message. It will verify
|
||
// the existance of all assigned MixerID's and the corresponding Volume and Mute ID on that device.
|
||
// In the event the device no-longer exists a default will be computed.
|
||
//
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::MMDeviceChanged(void)
|
||
{
|
||
LPCDUNIT pCDUnit = m_pCDOpts->pCDUnitList;
|
||
|
||
while (pCDUnit)
|
||
{
|
||
GetUnitValues(pCDUnit);
|
||
pCDUnit = pCDUnit->pNext;
|
||
}
|
||
}
|
||
|
||
|
||
//////////////////
|
||
// This function will save all the information for all the CD drives in the system out to the registry
|
||
// it does not modify or delete any of this information.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::WriteCDList(LPCDUNIT pCDList)
|
||
{
|
||
if (pCDList)
|
||
{
|
||
LPCDUNIT pUnit = pCDList;
|
||
|
||
while (pUnit)
|
||
{
|
||
SetUnitRegData(pUnit);
|
||
|
||
pUnit = pUnit->pNext;
|
||
}
|
||
}
|
||
}
|
||
|
||
////////////////
|
||
// This function will destory the list containing all the drive information in the system
|
||
// freeing up all memory, this function does not save any information.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::DestroyCDList(LPCDUNIT *ppCDList)
|
||
{
|
||
|
||
if (ppCDList && *ppCDList)
|
||
{
|
||
while(*ppCDList)
|
||
{
|
||
LPCDUNIT pTemp = *ppCDList;
|
||
*ppCDList = (*ppCDList)->pNext;
|
||
delete pTemp;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
STDMETHODIMP_(UINT) CCDOpt::GetDefDrive(void)
|
||
{
|
||
HKEY hkTmp;
|
||
UINT uDrive = 0;
|
||
|
||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, gszRegstrCDAPath, 0, KEY_READ, &hkTmp ) == ERROR_SUCCESS)
|
||
{
|
||
DWORD cb = sizeof(UINT);
|
||
RegQueryValueEx(hkTmp, gszDefaultCDA, NULL, NULL, (LPBYTE)&uDrive, &cb);
|
||
RegCloseKey(hkTmp);
|
||
}
|
||
|
||
return uDrive;
|
||
}
|
||
|
||
/////////////
|
||
// This function builds a list of all the CD drives in the system, the list
|
||
// is a linked list with each node containing information that is specific
|
||
// to that CD player as well as the user options for each player.
|
||
//
|
||
|
||
STDMETHODIMP CCDOpt::CreateCDList(LPCDUNIT *ppCDList)
|
||
{
|
||
DWORD dwBytes = 0;
|
||
TCHAR *szDriveNames = NULL;
|
||
TCHAR *szDriveNamesHead = NULL;
|
||
HRESULT hr = S_OK;
|
||
UINT uDefDrive = GetDefDrive();
|
||
|
||
if (ppCDList == NULL)
|
||
{
|
||
hr = E_INVALIDARG;
|
||
}
|
||
else
|
||
{
|
||
LPCDUNIT *ppUnit = NULL;
|
||
|
||
*ppCDList = NULL;
|
||
ppUnit = ppCDList;
|
||
|
||
dwBytes = GetLogicalDriveStrings(0, NULL);
|
||
|
||
if (dwBytes)
|
||
{
|
||
szDriveNames = new(TCHAR[dwBytes]);
|
||
szDriveNamesHead = szDriveNames;
|
||
|
||
if (szDriveNames == NULL)
|
||
{
|
||
hr = E_OUTOFMEMORY;
|
||
}
|
||
else
|
||
{
|
||
dwBytes = GetLogicalDriveStrings(dwBytes, szDriveNames);
|
||
|
||
if (dwBytes)
|
||
{
|
||
UINT uDrive = 0;
|
||
|
||
while (*szDriveNames)
|
||
{
|
||
if (GetDriveType(szDriveNames) == DRIVE_CDROM)
|
||
{
|
||
*ppUnit = new(CDUNIT);
|
||
|
||
if (*ppUnit == NULL)
|
||
{
|
||
hr = E_OUTOFMEMORY;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
memset(*ppUnit,0,sizeof(CDUNIT));
|
||
_tcsncpy((*ppUnit)->szDriveName,szDriveNames,min(sizeof((*ppUnit)->szDriveName)/sizeof(TCHAR),(UINT)lstrlen(szDriveNames)));
|
||
|
||
(*ppUnit)->dwTitleID = (DWORD)CDTITLE_NODISC;
|
||
|
||
CharUpper((*ppUnit)->szDriveName);
|
||
|
||
GetUnitValues(*ppUnit);
|
||
|
||
if (uDrive == uDefDrive)
|
||
{
|
||
(*ppUnit)->fDefaultDrive = TRUE;
|
||
}
|
||
|
||
ppUnit = &((*ppUnit)->pNext);
|
||
uDrive++;
|
||
}
|
||
}
|
||
|
||
while(*szDriveNames++);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (szDriveNamesHead)
|
||
{
|
||
delete [] szDriveNamesHead;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
DestroyCDList(ppCDList);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
|
||
|
||
/////////////
|
||
// Traverse the UI Tree, destroying it as it goes.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::DestroyUITree(LPCDDRIVE *ppCDRoot)
|
||
{
|
||
if (ppCDRoot)
|
||
{
|
||
LPCDDRIVE pDriveList;
|
||
|
||
pDriveList = *ppCDRoot;
|
||
*ppCDRoot = NULL;
|
||
|
||
while (pDriveList)
|
||
{
|
||
LPCDDRIVE pNextDrive = pDriveList->pNext;
|
||
|
||
while (pDriveList->pMixerList)
|
||
{
|
||
LPCDMIXER pNextMixer = pDriveList->pMixerList->pNext;
|
||
|
||
while(pDriveList->pMixerList->pControlList)
|
||
{
|
||
LPCDCONTROL pNextControl = pDriveList->pMixerList->pControlList->pNext;
|
||
delete pDriveList->pMixerList->pControlList;
|
||
pDriveList->pMixerList->pControlList = pNextControl;
|
||
}
|
||
|
||
delete pDriveList->pMixerList;
|
||
pDriveList->pMixerList = pNextMixer;
|
||
}
|
||
|
||
delete pDriveList;
|
||
pDriveList = pNextDrive;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
///////////////////
|
||
// Add line controls to internal tree data structure for UI
|
||
//
|
||
|
||
STDMETHODIMP CCDOpt::AddLineControls(LPCDMIXER pMixer, LPCDCONTROL *ppLastControl, int mxid, LPMIXERLINE pml)
|
||
{
|
||
MIXERLINECONTROLS mlc;
|
||
DWORD dwControl;
|
||
HRESULT hr = S_FALSE;
|
||
|
||
memset(&mlc, 0, sizeof(mlc));
|
||
mlc.cbStruct = sizeof(mlc);
|
||
mlc.dwLineID = pml->dwLineID;
|
||
mlc.cControls = pml->cControls;
|
||
mlc.cbmxctrl = sizeof(MIXERCONTROL);
|
||
mlc.pamxctrl = (LPMIXERCONTROL) new(MIXERCONTROL[pml->cControls]);
|
||
|
||
if (mlc.pamxctrl)
|
||
{
|
||
if (mixerGetLineControls((HMIXEROBJ)IntToPtr(mxid), &mlc, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
|
||
{
|
||
for (dwControl = 0; dwControl < pml->cControls; dwControl++)
|
||
{
|
||
if (mlc.pamxctrl[dwControl].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_VOLUME)
|
||
{
|
||
DWORD dwIndex;
|
||
DWORD dwVolID = mlc.pamxctrl[dwControl].dwControlID;
|
||
DWORD dwMuteID = DWORD(-1);
|
||
LPCDCONTROL pControl;
|
||
|
||
for (dwIndex = 0; dwIndex < pml->cControls; dwIndex++)
|
||
{
|
||
if (mlc.pamxctrl[dwIndex].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_MUTE)
|
||
{
|
||
dwMuteID = mlc.pamxctrl[dwIndex].dwControlID;
|
||
break;
|
||
}
|
||
}
|
||
|
||
pControl = new (CDCONTROL);
|
||
|
||
if (pControl == NULL)
|
||
{
|
||
hr = E_OUTOFMEMORY;
|
||
}
|
||
else
|
||
{
|
||
memset(pControl, 0, sizeof(CDCONTROL));
|
||
|
||
if (pMixer->pControlList == NULL)
|
||
{
|
||
pMixer->pControlList = pControl;
|
||
}
|
||
|
||
if (*ppLastControl)
|
||
{
|
||
(*ppLastControl)->pNext = pControl;
|
||
}
|
||
|
||
lstrcpy(pControl->szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
|
||
pControl->dwVolID = dwVolID;
|
||
pControl->dwMuteID = dwMuteID;
|
||
|
||
*ppLastControl = pControl;
|
||
hr = S_OK;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
delete mlc.pamxctrl;
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
/////////////////
|
||
// Searchs connections for controls to add to UI tree
|
||
//
|
||
|
||
STDMETHODIMP CCDOpt::AddConnections(LPCDMIXER pMixer, LPCDCONTROL *ppLastControl, int mxid, DWORD dwDestination, DWORD dwConnections)
|
||
{
|
||
MIXERLINE mlDst;
|
||
DWORD dwConnection;
|
||
HRESULT hr = S_FALSE;
|
||
BOOL bGotSomething=FALSE; //TRUE if any controls were found.
|
||
|
||
for (dwConnection = 0; dwConnection < dwConnections; dwConnection++)
|
||
{
|
||
mlDst.cbStruct = sizeof ( mlDst );
|
||
mlDst.dwDestination = dwDestination;
|
||
mlDst.dwSource = dwConnection;
|
||
|
||
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
|
||
{
|
||
if (mlDst.cControls) // Make sure this source has controls on it
|
||
{
|
||
if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_LINE ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_DIGITAL ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_ANALOG)
|
||
{
|
||
hr = AddLineControls(pMixer, ppLastControl, mxid, &mlDst);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
if(S_FALSE != hr)
|
||
{
|
||
bGotSomething = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if(SUCCEEDED(hr) && bGotSomething)
|
||
{
|
||
return S_OK;
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
//////////////
|
||
// Adds control nodes to the UI tree for the specified device
|
||
//
|
||
// Returns S_FALSE if no lines were added for this mixer.
|
||
|
||
STDMETHODIMP CCDOpt::AddControls(LPCDMIXER pMixer)
|
||
{
|
||
MIXERLINE mlDst;
|
||
DWORD dwDestination;
|
||
MIXERCAPS mc;
|
||
LPCDCONTROL pLastControl = NULL;
|
||
HRESULT hr = S_FALSE;
|
||
BOOL bGotSomething=FALSE; //TRUE if any valid lines were found on the mixer.
|
||
|
||
if (mixerGetDevCaps(pMixer->dwMixID, &mc, sizeof(mc)) == MMSYSERR_NOERROR)
|
||
{
|
||
for (dwDestination = 0; dwDestination < mc.cDestinations; dwDestination++)
|
||
{
|
||
mlDst.cbStruct = sizeof ( mlDst );
|
||
mlDst.dwDestination = dwDestination;
|
||
|
||
if (mixerGetLineInfo((HMIXEROBJ)ULongToPtr(pMixer->dwMixID), &mlDst, MIXER_GETLINEINFOF_DESTINATION ) == MMSYSERR_NOERROR)
|
||
{
|
||
if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || // needs to be a likely output destination
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES ||
|
||
mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT)
|
||
{
|
||
if (mlDst.cControls) // If there are no controls, we won't present it to the user as an option
|
||
{
|
||
hr = AddLineControls(pMixer, &pLastControl, pMixer->dwMixID, &mlDst);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
if(S_FALSE != hr)
|
||
{
|
||
bGotSomething = TRUE;
|
||
}
|
||
}
|
||
|
||
if (mlDst.cConnections) // If there are connections to this line, lets add thier controls
|
||
{
|
||
hr = AddConnections(pMixer, &pLastControl, pMixer->dwMixID, dwDestination, mlDst.cConnections);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
if(S_FALSE != hr)
|
||
{
|
||
bGotSomething = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if(SUCCEEDED(hr) && bGotSomething)
|
||
{
|
||
return S_OK;
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
///////////////////
|
||
// Adds audio mixers to UI tree for the cd player unit specified
|
||
//
|
||
|
||
STDMETHODIMP CCDOpt::AddMixers(LPCDDRIVE pDevice)
|
||
{
|
||
HRESULT hr = S_OK;
|
||
DWORD dwNumMixers;
|
||
DWORD dwID;
|
||
MMRESULT mmr;
|
||
MIXERCAPS mc;
|
||
LPCDMIXER pMixer;
|
||
LPCDMIXER pLastMixer = NULL;
|
||
|
||
if (pDevice)
|
||
{
|
||
dwNumMixers = mixerGetNumDevs();
|
||
|
||
for (dwID = 0; dwID < dwNumMixers; dwID++)
|
||
{
|
||
mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
|
||
|
||
if (mmr == MMSYSERR_NOERROR && mc.cDestinations)
|
||
{
|
||
pMixer = new (CDMIXER);
|
||
|
||
if (pMixer == NULL)
|
||
{
|
||
hr = E_OUTOFMEMORY;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
memset(pMixer, 0, sizeof(CDMIXER));
|
||
|
||
if (pDevice->pMixerList == NULL)
|
||
{
|
||
pDevice->pMixerList = pMixer;
|
||
}
|
||
|
||
lstrcpy(pMixer->szPname, mc.szPname);
|
||
pMixer->dwMixID = dwID;
|
||
|
||
hr = AddControls(pMixer);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
if(S_FALSE == hr)
|
||
{
|
||
//No valid line was found for this mixer.
|
||
//Example: The mixer device has only a WaveIn (USB microphone). Bug #398736.
|
||
delete pMixer;
|
||
pMixer = NULL;
|
||
continue;
|
||
}
|
||
|
||
if (pLastMixer)
|
||
{
|
||
pLastMixer->pNext = pMixer;
|
||
}
|
||
|
||
pLastMixer = pMixer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
//////////////
|
||
// Takes the UI tree and updates it's current and default setting links based on data
|
||
// from the CDINFO tree which was created from the registry
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::SetUIDefaults(LPCDDRIVE pCDTree, LPCDUNIT pCDList)
|
||
{
|
||
LPCDDRIVE pDriveList;
|
||
LPCDUNIT pCDUnit = pCDList;
|
||
|
||
pDriveList = pCDTree;
|
||
|
||
while(pDriveList && pCDUnit)
|
||
{
|
||
LPCDMIXER pMixer;
|
||
|
||
pMixer = pDriveList->pMixerList;
|
||
|
||
while (pMixer)
|
||
{
|
||
LPCDCONTROL pControl;
|
||
DWORD dwVolID = DWORD(-1);
|
||
DWORD dwMuteID = DWORD(-1);
|
||
DWORD dwDestID = DWORD(-1);
|
||
|
||
if (pMixer->dwMixID == pCDUnit->dwMixID)
|
||
{
|
||
pDriveList->pCurrentMixer = pMixer;
|
||
pDriveList->pOriginalMixer = pMixer;
|
||
}
|
||
|
||
pControl = pMixer->pControlList;
|
||
|
||
SearchDevice(pMixer->dwMixID, NULL, &dwDestID, &dwVolID, &dwMuteID, NULL, FALSE);
|
||
|
||
while (pControl)
|
||
{
|
||
if (pControl->dwVolID == dwVolID && pControl->dwMuteID == dwMuteID)
|
||
{
|
||
pMixer->pDefaultControl = pControl;
|
||
}
|
||
|
||
if (pMixer->dwMixID == pCDUnit->dwMixID && pControl->dwVolID == pCDUnit->dwVolID)
|
||
{
|
||
pMixer->pCurrentControl = pControl;
|
||
pMixer->pOriginalControl = pControl;
|
||
}
|
||
|
||
pControl = pControl->pNext;
|
||
}
|
||
|
||
pMixer = pMixer->pNext;
|
||
}
|
||
|
||
pDriveList = pDriveList->pNext;
|
||
pCDUnit = pCDUnit->pNext;
|
||
}
|
||
|
||
}
|
||
|
||
//////////////
|
||
// Takes the UI tree and updates it's current and default setting links based on data
|
||
// from the CDINFO tree which was created from the registry
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::RestoreOriginals(void)
|
||
{
|
||
LPCDDRIVE pDriveList;
|
||
|
||
pDriveList = m_pCDTree;
|
||
|
||
while(pDriveList)
|
||
{
|
||
LPCDMIXER pMixer;
|
||
|
||
pMixer = pDriveList->pMixerList;
|
||
|
||
while (pMixer)
|
||
{
|
||
LPCDCONTROL pControl;
|
||
|
||
pDriveList->pCurrentMixer = pDriveList->pOriginalMixer;
|
||
|
||
pControl = pMixer->pControlList;
|
||
|
||
while (pControl)
|
||
{
|
||
pMixer->pCurrentControl = pMixer->pOriginalControl;
|
||
pControl = pControl->pNext;
|
||
}
|
||
|
||
pMixer = pMixer->pNext;
|
||
}
|
||
|
||
pDriveList = pDriveList->pNext;
|
||
}
|
||
}
|
||
|
||
//////////////
|
||
// Updates the CD Tree from the UI Tree after the dialog closes (on OK), if there
|
||
// are any changes it returns true. The caller is expected to write these changes out to reg
|
||
//
|
||
|
||
STDMETHODIMP_(BOOL) CCDOpt::UpdateCDList(LPCDDRIVE pCDTree, LPCDUNIT pCDList)
|
||
{
|
||
BOOL fChanged = FALSE;
|
||
LPCDDRIVE pDriveList;
|
||
LPCDUNIT pCDUnit = pCDList;
|
||
|
||
pDriveList = pCDTree;
|
||
|
||
while(pDriveList && pCDUnit)
|
||
{
|
||
if (pCDUnit->fSelected != pDriveList->fSelected) // Selected drive has changed
|
||
{
|
||
pCDUnit->fSelected = pDriveList->fSelected;
|
||
fChanged = TRUE;
|
||
}
|
||
|
||
if (pDriveList->pCurrentMixer && pDriveList->pCurrentMixer->pCurrentControl)
|
||
{
|
||
LPCDMIXER pMixer = pDriveList->pCurrentMixer;
|
||
|
||
if (pMixer->dwMixID != pCDUnit->dwMixID) // Mixer has changed
|
||
{
|
||
pCDUnit->dwMixID = pMixer->dwMixID;
|
||
lstrcpy(pCDUnit->szMixerName, pMixer->szPname);
|
||
|
||
fChanged = TRUE;
|
||
}
|
||
|
||
LPCDCONTROL pControl = pDriveList->pCurrentMixer->pCurrentControl;
|
||
|
||
if (pControl->dwVolID != pCDUnit->dwVolID) // Control has changed
|
||
{
|
||
pCDUnit->dwVolID = pControl->dwVolID;
|
||
pCDUnit->dwMuteID = pControl->dwMuteID;
|
||
lstrcpy(pCDUnit->szVolName, pControl->szName);
|
||
|
||
fChanged = TRUE;
|
||
}
|
||
}
|
||
|
||
pDriveList = pDriveList->pNext;
|
||
pCDUnit = pCDUnit->pNext;
|
||
}
|
||
|
||
return(fChanged);
|
||
}
|
||
|
||
|
||
|
||
////////////
|
||
// Contructs UI tree for all devices/mixers/controls
|
||
//
|
||
|
||
STDMETHODIMP CCDOpt::BuildUITree(LPCDDRIVE *ppCDRoot, LPCDUNIT pCDList)
|
||
{
|
||
LPCDUNIT pCDUnit = pCDList;
|
||
LPCDDRIVE pDevice;
|
||
LPCDDRIVE pLastDevice;
|
||
HRESULT hr = S_OK;
|
||
|
||
m_pCDSelected = NULL;
|
||
|
||
if (ppCDRoot)
|
||
{
|
||
*ppCDRoot = NULL;
|
||
pLastDevice = NULL;
|
||
|
||
while (pCDUnit)
|
||
{
|
||
pDevice = new (CDDRIVE);
|
||
|
||
if (pDevice == NULL)
|
||
{
|
||
hr = E_OUTOFMEMORY;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
memset(pDevice, 0, sizeof(CDDRIVE));
|
||
|
||
if (*ppCDRoot == NULL)
|
||
{
|
||
*ppCDRoot = pDevice;
|
||
}
|
||
|
||
if (pLastDevice)
|
||
{
|
||
pLastDevice->pNext = pDevice;
|
||
}
|
||
|
||
lstrcpy(pDevice->szDriveName, pCDUnit->szDriveName);
|
||
lstrcpy(pDevice->szDeviceDesc, pCDUnit->szDeviceDesc);
|
||
pDevice->fSelected = pCDUnit->fSelected;
|
||
|
||
hr = AddMixers(pDevice);
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
pCDUnit = pCDUnit->pNext;
|
||
pLastDevice = pDevice;
|
||
}
|
||
}
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
if (*ppCDRoot)
|
||
{
|
||
DestroyUITree(ppCDRoot);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetUIDefaults(*ppCDRoot, pCDList);
|
||
}
|
||
}
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
/////////////////
|
||
// Updates the control combo box using the mixer node of the UI tree
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::InitControlUI(HWND hDlg, LPCDMIXER pMixer)
|
||
{
|
||
LPCDCONTROL pControl;
|
||
LRESULT dwIndex;
|
||
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_RESETCONTENT,0,0);
|
||
|
||
pControl = pMixer->pControlList;
|
||
|
||
if (pMixer->pCurrentControl == NULL)
|
||
{
|
||
pMixer->pCurrentControl = pMixer->pDefaultControl;
|
||
}
|
||
|
||
while(pControl)
|
||
{
|
||
dwIndex = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) pControl->szName);
|
||
|
||
if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pControl);
|
||
|
||
if (pMixer->pCurrentControl == pControl)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_SETCURSEL, (WPARAM) dwIndex, 0);
|
||
}
|
||
}
|
||
|
||
pControl = pControl->pNext;
|
||
}
|
||
}
|
||
|
||
|
||
////////////////
|
||
// Sets up the mixer combobox using the specified device
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::InitMixerUI(HWND hDlg, LPCDDRIVE pDevice)
|
||
{
|
||
LPCDMIXER pMixer;
|
||
LRESULT dwIndex;
|
||
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_RESETCONTENT,0,0);
|
||
|
||
pMixer = pDevice->pMixerList;
|
||
|
||
while (pMixer)
|
||
{
|
||
dwIndex = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) pMixer->szPname);
|
||
|
||
if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pMixer);
|
||
|
||
if (pDevice->pCurrentMixer == pMixer)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_SETCURSEL, (WPARAM) dwIndex, 0);
|
||
|
||
InitControlUI(hDlg, pMixer);
|
||
}
|
||
}
|
||
|
||
pMixer = pMixer->pNext;
|
||
}
|
||
}
|
||
|
||
|
||
////////////////
|
||
// Sets up the cd device combo box
|
||
//
|
||
|
||
STDMETHODIMP_(void) CCDOpt::InitDeviceUI(HWND hDlg, LPCDDRIVE pCDTree, LPCDDRIVE pCurrentDevice)
|
||
{
|
||
LPCDDRIVE pDevice;
|
||
LRESULT dwIndex;
|
||
|
||
SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_RESETCONTENT,0,0);
|
||
|
||
pDevice = pCDTree;
|
||
|
||
while (pDevice)
|
||
{
|
||
TCHAR str[MAX_PATH];
|
||
|
||
wsprintf(str, TEXT("( %s ) %s"), pDevice->szDriveName, pDevice->szDeviceDesc);
|
||
|
||
dwIndex = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) str);
|
||
|
||
if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pDevice);
|
||
|
||
if (pDevice == pCurrentDevice)
|
||
{
|
||
SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_SETCURSEL, (WPARAM) dwIndex, 0);
|
||
|
||
InitMixerUI(hDlg, pDevice);
|
||
}
|
||
}
|
||
|
||
pDevice = pDevice->pNext;
|
||
}
|
||
}
|
||
|
||
///////////////////
|
||
// Called to init the dialog when it first appears. Given a list of CD devices, this
|
||
// function fills out the dialog using the first device in the list.
|
||
//
|
||
STDMETHODIMP_(BOOL) CCDOpt::InitMixerConfig(HWND hDlg)
|
||
{
|
||
LPCDDRIVE pDevice = m_pCDTree;
|
||
|
||
while (pDevice)
|
||
{
|
||
if (pDevice->fSelected)
|
||
{
|
||
break;
|
||
}
|
||
|
||
pDevice = pDevice->pNext;
|
||
}
|
||
|
||
if (pDevice == NULL)
|
||
{
|
||
pDevice = m_pCDTree;
|
||
}
|
||
|
||
InitDeviceUI(hDlg, m_pCDTree, pDevice); // Init to the selected device
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
//////////////
|
||
// This function pulls the CD Drive node out of the combo box and returns it. It returns a
|
||
// reference into the main Drive tree.
|
||
//
|
||
|
||
STDMETHODIMP_(LPCDDRIVE) CCDOpt::GetCurrentDevice(HWND hDlg)
|
||
{
|
||
LRESULT dwResult;
|
||
LPCDDRIVE pDevice = NULL;
|
||
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_GETCURSEL, 0, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_GETITEMDATA, (WPARAM) dwResult, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
pDevice = (LPCDDRIVE) dwResult;
|
||
}
|
||
}
|
||
|
||
return(pDevice);
|
||
}
|
||
|
||
//////////////
|
||
// This function pulls the CD Drive mixer out of the combo box and returns it. It returns a
|
||
// reference into the main Drive tree.
|
||
//
|
||
|
||
STDMETHODIMP_(LPCDMIXER) CCDOpt::GetCurrentMixer(HWND hDlg)
|
||
{
|
||
LRESULT dwResult;
|
||
LPCDMIXER pMixer = NULL;
|
||
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETCURSEL, 0, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETITEMDATA, (WPARAM) dwResult, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
pMixer = (LPCDMIXER) dwResult;
|
||
}
|
||
}
|
||
|
||
return(pMixer);
|
||
}
|
||
|
||
|
||
//////////////
|
||
// Called when the user changes the CD Device. it pulls the CD Drive node out of the combo box and
|
||
// then resets the UI to reflect that drives current settings.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::ChangeCDDrives(HWND hDlg)
|
||
{
|
||
LPCDDRIVE pDevice = GetCurrentDevice(hDlg);
|
||
|
||
if (pDevice)
|
||
{
|
||
LPCDDRIVE pDev = m_pCDTree;
|
||
|
||
InitDeviceUI(hDlg, m_pCDTree, pDevice);
|
||
|
||
while (pDev)
|
||
{
|
||
pDev->fSelected = (BOOL) (pDev == pDevice);
|
||
pDev = pDev->pNext;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//////////
|
||
// Called when the user changes the current mixer for the drive, it updates the
|
||
// display and the info for the drive.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::ChangeCDMixer(HWND hDlg)
|
||
{
|
||
LRESULT dwResult;
|
||
LPCDDRIVE pDevice = GetCurrentDevice(hDlg);
|
||
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETCURSEL, 0, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETITEMDATA, (WPARAM) dwResult, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
pDevice->pCurrentMixer = (LPCDMIXER) dwResult;
|
||
|
||
InitMixerUI(hDlg, pDevice);
|
||
}
|
||
}
|
||
}
|
||
|
||
/////////////////
|
||
// Called when the user changes the current control for the mixer on the current device.
|
||
// It updates the internal data structure for that drive.
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::ChangeCDControl(HWND hDlg)
|
||
{
|
||
LPCDMIXER pMixer = GetCurrentMixer(hDlg);
|
||
LRESULT dwResult;
|
||
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_GETCURSEL, 0, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_GETITEMDATA, (WPARAM) dwResult, 0);
|
||
|
||
if (dwResult != CB_ERR)
|
||
{
|
||
pMixer->pCurrentControl = (LPCDCONTROL) dwResult;
|
||
|
||
InitControlUI(hDlg, pMixer);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//////////////////
|
||
// Called to set the default control for the current mixer (lets the system pick)
|
||
//
|
||
STDMETHODIMP_(void) CCDOpt::SetMixerDefaults(HWND hDlg)
|
||
{
|
||
LPCDMIXER pMixer = GetCurrentMixer(hDlg);
|
||
|
||
if (pMixer)
|
||
{
|
||
pMixer->pCurrentControl = NULL;
|
||
|
||
InitControlUI(hDlg, pMixer);
|
||
}
|
||
}
|
||
|
||
|
||
///////////////////
|
||
// Dialog handler for the mixer configuration dialog
|
||
//
|
||
|
||
STDMETHODIMP_(BOOL) CCDOpt::MixerConfig(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
BOOL fResult = TRUE;
|
||
|
||
switch (msg)
|
||
{
|
||
default:
|
||
fResult = FALSE;
|
||
break;
|
||
|
||
|
||
case WM_CONTEXTMENU:
|
||
{
|
||
WinHelp((HWND)wParam, gszHelpFile, HELP_CONTEXTMENU, (ULONG_PTR)(LPSTR)aVolOptsHelp);
|
||
}
|
||
break;
|
||
|
||
case WM_HELP:
|
||
{
|
||
WinHelp((HWND) ((LPHELPINFO)lParam)->hItemHandle, gszHelpFile, HELP_WM_HELP, (ULONG_PTR)(LPSTR)aVolOptsHelp);
|
||
}
|
||
break;
|
||
|
||
case WM_INITDIALOG:
|
||
{
|
||
fResult = InitMixerConfig(hDlg);
|
||
}
|
||
break;
|
||
|
||
case WM_COMMAND:
|
||
{
|
||
switch (LOWORD(wParam))
|
||
{
|
||
case IDOK:
|
||
EndDialog(hDlg, TRUE);
|
||
break;
|
||
|
||
case IDCANCEL:
|
||
RestoreOriginals();
|
||
EndDialog(hDlg,FALSE);
|
||
break;
|
||
|
||
case IDC_DEFAULTMIXER:
|
||
SetMixerDefaults(hDlg);
|
||
break;
|
||
|
||
case IDC_CDDRIVE:
|
||
if (HIWORD(wParam) == CBN_SELCHANGE)
|
||
{
|
||
ChangeCDDrives(hDlg);
|
||
}
|
||
break;
|
||
|
||
case IDC_AUDIOMIXER:
|
||
if (HIWORD(wParam) == CBN_SELCHANGE)
|
||
{
|
||
ChangeCDMixer(hDlg);
|
||
}
|
||
break;
|
||
|
||
case IDC_AUDIOCONTROL:
|
||
if (HIWORD(wParam) == CBN_SELCHANGE)
|
||
{
|
||
ChangeCDControl(hDlg);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
fResult = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
return fResult;
|
||
}
|
||
|
||
///////////////////
|
||
// Dialog handler
|
||
//
|
||
INT_PTR CALLBACK CCDOpt::MixerConfigProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
BOOL fResult = TRUE;
|
||
CCDOpt *pCDOpt = (CCDOpt *) GetWindowLongPtr(hDlg, DWLP_USER);
|
||
|
||
if (msg == WM_INITDIALOG)
|
||
{
|
||
pCDOpt = (CCDOpt *) lParam;
|
||
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) pCDOpt);
|
||
}
|
||
|
||
if (pCDOpt)
|
||
{
|
||
fResult = pCDOpt->MixerConfig(hDlg, msg, wParam, lParam);
|
||
}
|
||
|
||
if (msg == WM_DESTROY)
|
||
{
|
||
pCDOpt = NULL;
|
||
}
|
||
|
||
return(fResult);
|
||
}
|
||
|
||
////////////
|
||
// Called to put up the UI to allow the user to change the CD Volume Configuration
|
||
//
|
||
STDMETHODIMP_(BOOL) CCDOpt::VolumeDialog(HWND hDlg)
|
||
{
|
||
BOOL fChanged = FALSE;
|
||
|
||
if (m_pCDOpts && m_pCDOpts->pCDUnitList)
|
||
{
|
||
if (SUCCEEDED(BuildUITree(&m_pCDTree, m_pCDOpts->pCDUnitList)))
|
||
{
|
||
if(DialogBoxParam( m_hInst, MAKEINTRESOURCE(IDD_MIXERPICKER), hDlg, CCDOpt::MixerConfigProc, (LPARAM) this) == TRUE)
|
||
{
|
||
fChanged = UpdateCDList(m_pCDTree, m_pCDOpts->pCDUnitList);
|
||
|
||
if (fChanged)
|
||
{
|
||
WriteCDList(m_pCDOpts->pCDUnitList);
|
||
}
|
||
}
|
||
|
||
DestroyUITree(&m_pCDTree);
|
||
}
|
||
}
|
||
|
||
return(fChanged);
|
||
}
|
||
|
||
|
||
|
||
|
||
|