Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1818 lines
52 KiB

  1. //--------------------------------------------------------------------------;
  2. //
  3. // File: volopt.cpp
  4. //
  5. // Copyright (c) 1998 Microsoft Corporation. All rights reserved
  6. //
  7. //--------------------------------------------------------------------------;
  8. #include "precomp.h"
  9. #include <regstr.h>
  10. #include <mmsystem.h>
  11. #include <mmddk.h>
  12. #include "optres.h"
  13. #include "cdopti.h"
  14. #include "cdoptimp.h"
  15. #include "helpids.h"
  16. #include "winbase.h"
  17. //////////////
  18. // Help ID's
  19. //////////////
  20. #pragma data_seg(".text")
  21. const static DWORD aVolOptsHelp[] =
  22. {
  23. IDC_VOLCONFIG_ICON, IDH_VOL_MSG,
  24. IDC_VOL_MSG_TEXT, IDH_VOL_MSG,
  25. IDC_VOL_CONFIG_GROUP, IDH_VOL_MSG,
  26. IDC_DEFAULTMIXER, IDH_USEMIXERDEFAULTS,
  27. IDC_SELECTPLAYER_TEXT, IDH_SELECTCDPLAYER,
  28. IDC_CDDRIVE, IDH_SELECTCDPLAYER,
  29. IDC_SELECTMIXER_TEXT, IDH_SELECTCDMIXER,
  30. IDC_AUDIOMIXER, IDH_SELECTCDMIXER,
  31. IDC_SELECTCONTROL_TEXT, IDH_SELECTCDCONTROL,
  32. IDC_AUDIOCONTROL, IDH_SELECTCDCONTROL,
  33. 0, 0
  34. };
  35. #pragma data_seg()
  36. ////////////
  37. // Types
  38. ////////////
  39. typedef struct CDCTL // Used to write to reg (don't change)
  40. {
  41. DWORD dwVolID;
  42. DWORD dwMuteID;
  43. DWORD dwDestID;
  44. } CDCTL, *LPCDCTL;
  45. ////////////
  46. // Globals
  47. ////////////
  48. #define MYREGSTR_PATH_MEDIA TEXT("SYSTEM\\CurrentControlSet\\Control\\MediaResources")
  49. const TCHAR gszRegstrCDAPath[] = MYREGSTR_PATH_MEDIA TEXT("\\mci\\cdaudio");
  50. const TCHAR gszDefaultCDA[] = TEXT("Default Drive");
  51. const TCHAR szRegstrCDROMPath[] = TEXT("System\\CurrentControlSet\\Services\\Class\\");
  52. const TCHAR szPrefMixer[] = TEXT("Preferred Mixer");
  53. const TCHAR szPrefControls[] = TEXT("Preferred Controls");
  54. const TCHAR szSelected[] = TEXT("Selected");
  55. const TCHAR szMapperPath[] = TEXT("Software\\Microsoft\\Multimedia\\Sound Mapper");
  56. const TCHAR szPlayback[] = TEXT("Playback");
  57. const TCHAR szPreferredOnly[] = TEXT("PreferredOnly");
  58. const TCHAR szNTCDROMPath[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\DeluxeCD\\Settings\\");
  59. ////////////
  60. // Functions
  61. ////////////
  62. /////////////
  63. // Uses new winmm feature to get the preferred wave ID, much cleaner.
  64. //
  65. STDMETHODIMP_(MMRESULT) CCDOpt::GetDefaultMixID(DWORD *pdwMixID)
  66. {
  67. MMRESULT mmr;
  68. DWORD dwWaveID;
  69. DWORD dwFlags = 0;
  70. UINT dwMixID = 0;
  71. mmr = waveOutMessage((HWAVEOUT)(UINT_PTR)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &dwWaveID, (DWORD_PTR) &dwFlags);
  72. if (!mmr && pdwMixID)
  73. {
  74. mmr = mixerGetID((HMIXEROBJ)ULongToPtr(dwWaveID), &dwMixID, MIXER_OBJECTF_WAVEOUT);
  75. if(MMSYSERR_NOERROR == mmr)
  76. {
  77. *pdwMixID = dwMixID;
  78. }
  79. }
  80. return(mmr);
  81. }
  82. ///////////
  83. // A mixer line has been found with controls, this function is called to either 1) verify
  84. // that passed in VolID and MuteID's can be found on this mixer line and are of the right
  85. // control type. or 2) to find the Volume Slider and Mute ID controls that do exist on
  86. // this mixer line.
  87. //
  88. STDMETHODIMP_(void) CCDOpt::SearchControls(int mxid, LPMIXERLINE pml, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL *pfFound, BOOL fVerify)
  89. {
  90. MIXERLINECONTROLS mlc;
  91. DWORD dwControl;
  92. memset(&mlc, 0, sizeof(mlc));
  93. mlc.cbStruct = sizeof(mlc);
  94. mlc.dwLineID = pml->dwLineID;
  95. mlc.cControls = pml->cControls;
  96. mlc.cbmxctrl = sizeof(MIXERCONTROL);
  97. mlc.pamxctrl = (LPMIXERCONTROL) new(MIXERCONTROL[pml->cControls]);
  98. if (mlc.pamxctrl)
  99. {
  100. if (mixerGetLineControls((HMIXEROBJ)IntToPtr(mxid), &mlc, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
  101. {
  102. for (dwControl = 0; dwControl < pml->cControls && !(*pfFound); dwControl++)
  103. {
  104. if (mlc.pamxctrl[dwControl].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_VOLUME)
  105. {
  106. DWORD dwIndex;
  107. DWORD dwVolID = DWORD(-1);
  108. DWORD dwMuteID = DWORD(-1);
  109. dwVolID = mlc.pamxctrl[dwControl].dwControlID;
  110. for (dwIndex = 0; dwIndex < pml->cControls; dwIndex++)
  111. {
  112. if (mlc.pamxctrl[dwIndex].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_MUTE)
  113. {
  114. dwMuteID = mlc.pamxctrl[dwIndex].dwControlID;
  115. break;
  116. }
  117. }
  118. if (fVerify)
  119. {
  120. if (*pdwVolID == dwVolID && *pdwMuteID == dwMuteID)
  121. {
  122. if (szName)
  123. {
  124. lstrcpy(szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
  125. }
  126. *pfFound = TRUE;
  127. }
  128. }
  129. else
  130. {
  131. if (szName)
  132. {
  133. lstrcpy(szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
  134. }
  135. *pfFound = TRUE;
  136. *pdwVolID = dwVolID;
  137. *pdwMuteID = dwMuteID;
  138. }
  139. }
  140. }
  141. }
  142. delete mlc.pamxctrl;
  143. }
  144. }
  145. ///////////////
  146. // If a mixer line has connects, this function is called to enumerate all lines that have controls
  147. // that meet our criteria and then seek out the controls on those connections using the above SearchControls
  148. // function.
  149. //
  150. // NOTE: This function makes two scans over the connections, first looking for CompactDisc lines, and then
  151. // if unsuccessful, it makes a second scan looking for other lines that might have a CD connected, like line-in
  152. // and aux lines.
  153. //
  154. STDMETHODIMP_(void) CCDOpt::SearchConnections(int mxid, DWORD dwDestination, DWORD dwConnections, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL *pfFound, BOOL fVerify)
  155. {
  156. MIXERLINE mlDst;
  157. DWORD dwConnection;
  158. DWORD dwScan;
  159. for (dwScan = 0; dwScan < 2 && !(*pfFound); dwScan++) // On first scan look for CD, on second scan, look for anything else.
  160. {
  161. for (dwConnection = 0; dwConnection < dwConnections && !(*pfFound); dwConnection++)
  162. {
  163. mlDst.cbStruct = sizeof ( mlDst );
  164. mlDst.dwDestination = dwDestination;
  165. mlDst.dwSource = dwConnection;
  166. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
  167. {
  168. if (mlDst.cControls) // Make sure this source has controls on it
  169. {
  170. if (((dwScan == 0) && (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC)) ||
  171. ((dwScan == 1) && (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_LINE ||
  172. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY ||
  173. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_DIGITAL ||
  174. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_ANALOG)))
  175. {
  176. SearchControls(mxid, &mlDst, pdwVolID, pdwMuteID, szName, pfFound, fVerify);
  177. }
  178. }
  179. }
  180. }
  181. }
  182. }
  183. /////////////////
  184. // Used in two modes, fVerify is TRUE, the VolID and MuteID are inputs and will return TRUE if valid
  185. // if not in verification mode, this function is used to compute the default vol and mute ID's for this device.
  186. // It scans all the destinations on the Mixer looking for output destinations (speakers, headphones, etc)
  187. // Once it finds them, it then Searchs for controls on itself and/or any connections itself.
  188. //
  189. // NOTE: The current default behavior is to locate CD type connections, and then, if not finding any that
  190. // work, to attempt to use the destination master volume. To reverse this, look for master volume first, and then
  191. // look for CD lines if master can't be found, Switch the two intermost If conditions and calls so that
  192. // SearchControls on the line happens before the connections are searched.
  193. STDMETHODIMP_(BOOL) CCDOpt::SearchDevice(DWORD dwMixID, LPCDUNIT pCDUnit, LPDWORD pdwDestID, LPDWORD pdwVolID, LPDWORD pdwMuteID, TCHAR *szName, BOOL fVerify)
  194. {
  195. MIXERCAPS mc;
  196. MMRESULT mmr;
  197. BOOL fFound = FALSE;
  198. mmr = mixerGetDevCaps(dwMixID, &mc, sizeof(mc));
  199. if (mmr == MMSYSERR_NOERROR)
  200. {
  201. MIXERLINE mlDst;
  202. DWORD dwDestination;
  203. DWORD cDestinations;
  204. if (pCDUnit)
  205. {
  206. lstrcpy(pCDUnit->szMixerName, mc.szPname);
  207. }
  208. dwDestination = 0; // Setup loop to check all destinations
  209. cDestinations = mc.cDestinations;
  210. if (fVerify) // If in Verify mode, only check the specified destination ID
  211. {
  212. dwDestination = *pdwDestID;
  213. cDestinations = dwDestination + 1;
  214. }
  215. for ( ; dwDestination < cDestinations && !fFound; dwDestination++)
  216. {
  217. mlDst.cbStruct = sizeof ( mlDst );
  218. mlDst.dwDestination = dwDestination;
  219. if (mixerGetLineInfo((HMIXEROBJ)ULongToPtr(dwMixID), &mlDst, MIXER_GETLINEINFOF_DESTINATION ) == MMSYSERR_NOERROR)
  220. {
  221. if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || // needs to be a likely output destination
  222. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES ||
  223. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT)
  224. {
  225. // Note: To default to Master Volume (instead of CD volume) just SearchControls first.
  226. if (!fFound && mlDst.cConnections) // only look at connections if there is no master slider control.
  227. {
  228. SearchConnections(dwMixID, dwDestination, mlDst.cConnections, pdwVolID, pdwMuteID, szName, &fFound, fVerify);
  229. }
  230. if (!fFound && mlDst.cControls) // If there are controls, we'll take the master
  231. {
  232. SearchControls(dwMixID, &mlDst, pdwVolID, pdwMuteID, szName, &fFound, fVerify);
  233. }
  234. *pdwDestID = dwDestination;
  235. }
  236. }
  237. }
  238. }
  239. return(fFound);
  240. }
  241. ////////////////
  242. // When a new CD is found, and there are no valid enteries in the registry for it, we compute
  243. // defaults for that unit. This function uses the above function SearchDevice to do just that.
  244. // First it finds the preferred MixerID and assumes this CD is connected to it, this it finds
  245. // the default controls on that mixer and assignes them.
  246. //
  247. STDMETHODIMP_(void) CCDOpt::GetUnitDefaults(LPCDUNIT pCDUnit)
  248. {
  249. if (pCDUnit)
  250. {
  251. pCDUnit->dwMixID = DWORD(-1);
  252. pCDUnit->dwVolID = DWORD(-1);
  253. pCDUnit->dwMuteID = DWORD(-1);
  254. pCDUnit->dwDestID = DWORD(-1);
  255. if (GetDefaultMixID(&pCDUnit->dwMixID) == MMSYSERR_NOERROR)
  256. {
  257. SearchDevice(pCDUnit->dwMixID, pCDUnit, &pCDUnit->dwDestID, &pCDUnit->dwVolID, &pCDUnit->dwMuteID, pCDUnit->szVolName, FALSE);
  258. }
  259. }
  260. }
  261. //////////////
  262. // This function enumerates installed disk devices that are of type CDROM and are installed
  263. // It seeks out the one that matches the specified drive letter and collects information about it
  264. // It returns the class driver path and the descriptive name of the drive.
  265. //
  266. // NOTE This function assumes both szDriver and szDevDesc are the dwSize bytes each.
  267. //
  268. STDMETHODIMP_(BOOL) CCDOpt::MapLetterToDevice(TCHAR DriveLetter, TCHAR *szDriver, TCHAR *szDevDesc, DWORD dwSize)
  269. {
  270. HKEY hkEnum;
  271. BOOL fResult = FALSE;
  272. if (!RegOpenKey(HKEY_DYN_DATA, REGSTR_PATH_DYNA_ENUM, &hkEnum))
  273. {
  274. HKEY hkDev;
  275. DWORD dwEnumDevCnt;
  276. TCHAR aszCMKey[MAX_PATH];
  277. BOOLEAN found = FALSE;
  278. for (dwEnumDevCnt = 0; !found && !RegEnumKey(hkEnum, dwEnumDevCnt, aszCMKey, sizeof(aszCMKey)/sizeof(TCHAR)); dwEnumDevCnt++)
  279. {
  280. if (!RegOpenKey(hkEnum, aszCMKey, &hkDev))
  281. {
  282. TCHAR aszDrvKey[MAX_PATH];
  283. TCHAR tmp;
  284. DWORD cb = sizeof(aszDrvKey);
  285. RegQueryValueEx(hkDev, REGSTR_VAL_HARDWARE_KEY, NULL, NULL, (LPBYTE)&aszDrvKey, &cb);
  286. tmp = aszDrvKey[5];
  287. aszDrvKey[5] = TEXT('\0');
  288. if ( !lstrcmpi( REGSTR_VAL_SCSI, aszDrvKey ) )
  289. {
  290. HKEY hkDrv;
  291. TCHAR aszEnumKey[MAX_PATH];
  292. aszDrvKey[5] = tmp;
  293. wsprintf(aszEnumKey, TEXT("Enum\\%s"), aszDrvKey);
  294. if (!RegOpenKey(HKEY_LOCAL_MACHINE, aszEnumKey, &hkDrv))
  295. {
  296. TCHAR DrvLet[3];
  297. cb = sizeof( DrvLet );
  298. RegQueryValueEx(hkDrv, REGSTR_VAL_CURDRVLET, NULL, NULL, (LPBYTE)&DrvLet, &cb);
  299. if ( DrvLet[0] == DriveLetter )
  300. {
  301. DWORD cb2 = dwSize;
  302. cb = dwSize;
  303. if ((RegQueryValueEx(hkDrv, REGSTR_VAL_DEVDESC, NULL, NULL, (LPBYTE)szDevDesc, &cb) == NO_ERROR) &&
  304. (RegQueryValueEx(hkDrv, REGSTR_VAL_DRIVER, NULL, NULL, (LPBYTE)szDriver, &cb2) == NO_ERROR))
  305. {
  306. fResult = TRUE;
  307. }
  308. found = TRUE;
  309. }
  310. RegCloseKey(hkDrv);
  311. }
  312. }
  313. RegCloseKey(hkDev);
  314. }
  315. }
  316. RegCloseKey(hkEnum);
  317. }
  318. if (!fResult)
  319. {
  320. //check to see if we're on NT
  321. OSVERSIONINFO os;
  322. os.dwOSVersionInfoSize = sizeof(os);
  323. GetVersionEx(&os);
  324. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  325. {
  326. TCHAR szDrive[MAX_PATH];
  327. TCHAR szDriverTemp[MAX_PATH*2];
  328. TCHAR* szGUID = NULL;
  329. ZeroMemory(szDriverTemp,sizeof(szDriverTemp));
  330. ZeroMemory(szDriver,dwSize);
  331. wsprintf(szDrive,TEXT("%c:\\"),DriveLetter);
  332. if (GetVolumeNameForVolumeMountPoint(szDrive,szDriverTemp,dwSize/sizeof(TCHAR)))
  333. {
  334. //szDriverTemp now has a string like \\?\Volume{GUID}\ ... we just want
  335. //the {GUID} part, as that is the drive's unique ID.
  336. szGUID = _tcschr(szDriverTemp,TEXT('{'));
  337. if (szGUID!=NULL)
  338. {
  339. fResult = TRUE;
  340. _tcscpy(szDriver,szGUID);
  341. if (szDriver[_tcslen(szDriver)-1] != TEXT('}'))
  342. {
  343. szDriver[_tcslen(szDriver)-1] = TEXT('\0');
  344. }
  345. }
  346. }
  347. } //end if NT
  348. }
  349. return(fResult);
  350. }
  351. ///////////////
  352. // Since audio devices can come and go, the names of the devices can change since there is
  353. // number appended by the system inclosed in brackets at the end of the string.
  354. // This function attempts to truncate this appended number after copying the original into
  355. // the destination buffer.
  356. //
  357. STDMETHODIMP_(BOOL) CCDOpt::TruncName(TCHAR *pDest, TCHAR *pSrc)
  358. {
  359. BOOL fSuccess = TRUE;
  360. TCHAR *pTrunc;
  361. lstrcpy(pDest, pSrc);
  362. pTrunc = pDest;
  363. while (*pTrunc++);
  364. while (pTrunc-- > pDest)
  365. {
  366. if (*pTrunc == TEXT('['))
  367. {
  368. *pTrunc = TEXT('\0');
  369. break;
  370. }
  371. }
  372. if (pTrunc == pDest)
  373. {
  374. fSuccess = FALSE;
  375. }
  376. return(fSuccess);
  377. }
  378. /////////////////
  379. // After reading the preferred mixer device name from the registry, we have to locate that
  380. // device in the system by mixer ID. To do this, we scan thru all available mixers looking
  381. // for an exact match of the name. If an exact match can not be found, we then scan again
  382. // looking for a match using truncated strings where the system appended instance number is
  383. // removed. If we can't find it at that point, we are in trouble and have to give up.
  384. //
  385. STDMETHODIMP CCDOpt::ComputeMixID(LPDWORD pdwMixID, TCHAR *szMixerName)
  386. {
  387. HRESULT hr = E_FAIL;
  388. DWORD dwNumMixers;
  389. DWORD dwID;
  390. MMRESULT mmr;
  391. BOOL fFound = FALSE;
  392. TCHAR szTruncName[MAXPNAMELEN];
  393. TCHAR szTruncSeek[MAXPNAMELEN];
  394. MIXERCAPS mc;
  395. dwNumMixers = mixerGetNumDevs();
  396. for (dwID = 0; dwID < dwNumMixers && !fFound; dwID++) // First look for EXACT match.
  397. {
  398. mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
  399. if (mmr == MMSYSERR_NOERROR)
  400. {
  401. if (!lstrcmp(mc.szPname, szMixerName))
  402. {
  403. hr = S_OK;
  404. fFound = TRUE;
  405. *pdwMixID = dwID;
  406. }
  407. }
  408. }
  409. if (!fFound) // Exact match isn't found, strip off (#) and look again
  410. {
  411. if (TruncName(szTruncName, szMixerName))
  412. {
  413. for (dwID = 0; dwID < dwNumMixers && !fFound; dwID++)
  414. {
  415. mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
  416. if (mmr == MMSYSERR_NOERROR)
  417. {
  418. TruncName(szTruncSeek, mc.szPname);
  419. if (!lstrcmp(szTruncSeek, szTruncName))
  420. {
  421. lstrcpy(szMixerName, mc.szPname); // repair the name we matched
  422. hr = S_OK;
  423. fFound = TRUE;
  424. *pdwMixID = dwID;
  425. }
  426. }
  427. }
  428. }
  429. }
  430. return(hr);
  431. }
  432. ///////////////
  433. // This function looks up this devices registry information using the driver information that
  434. // was mapped from the drive letter. It attempts to read in the preferred mixer ID and then
  435. // calculate the mixerID from it, if that fails, the function fails and the unit will use default
  436. // information calcuated by this program. If it succeeds, it then reads in the control ID's for
  437. // the volume and mute controls for this mixer. If it can't find them or the ones it does find
  438. // can not be located on the specified device, then we compute defaults. If we can't compute defaults
  439. // the entire function fails and the entire drive is re-computed.
  440. //
  441. STDMETHODIMP CCDOpt::GetUnitRegData(LPCDUNIT pCDUnit)
  442. {
  443. HRESULT hr = E_FAIL;
  444. TCHAR szRegDriverStr[MAX_PATH];
  445. HKEY hKey;
  446. DWORD dwMixID;
  447. CDCTL cdCtl;
  448. HKEY hKeyRoot = HKEY_LOCAL_MACHINE;
  449. if (pCDUnit)
  450. {
  451. OSVERSIONINFO os;
  452. os.dwOSVersionInfoSize = sizeof(os);
  453. GetVersionEx(&os);
  454. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  455. {
  456. hKeyRoot = HKEY_CURRENT_USER;
  457. wsprintf(szRegDriverStr,TEXT("%s%s"),szNTCDROMPath, pCDUnit->szDriver);
  458. }
  459. else
  460. {
  461. wsprintf(szRegDriverStr,TEXT("%s%s"),szRegstrCDROMPath, pCDUnit->szDriver);
  462. }
  463. if (RegOpenKeyEx(hKeyRoot , szRegDriverStr , 0 , KEY_READ , &hKey) == ERROR_SUCCESS)
  464. {
  465. DWORD dwSize = sizeof(BOOL);
  466. RegQueryValueEx(hKey, szSelected, NULL, NULL, (LPBYTE) &(pCDUnit->fSelected), &dwSize);
  467. dwSize = MAXPNAMELEN*sizeof(TCHAR);
  468. if (RegQueryValueEx(hKey, szPrefMixer, NULL, NULL, (LPBYTE) pCDUnit->szMixerName, &dwSize) == NO_ERROR)
  469. {
  470. hr = ComputeMixID(&dwMixID, pCDUnit->szMixerName);
  471. if (SUCCEEDED(hr))
  472. {
  473. BOOL fGotControls = FALSE;
  474. TCHAR szVolName[MIXER_LONG_NAME_CHARS] = TEXT("\0");
  475. dwSize = sizeof(cdCtl);
  476. cdCtl.dwDestID = DWORD(-1);
  477. cdCtl.dwMuteID = DWORD(-1);
  478. cdCtl.dwVolID = DWORD(-1);
  479. if (RegQueryValueEx(hKey, szPrefControls, NULL, NULL, (LPBYTE) &cdCtl, &dwSize) == NO_ERROR)
  480. {
  481. fGotControls = SearchDevice(dwMixID, NULL, &cdCtl.dwDestID, &cdCtl.dwVolID, &cdCtl.dwMuteID, szVolName, TRUE); // Verify Controls
  482. }
  483. if (!fGotControls) // Either were not in reg or fail verification, compute defaults for this device
  484. {
  485. fGotControls = SearchDevice(dwMixID, NULL, &cdCtl.dwDestID, &cdCtl.dwVolID, &cdCtl.dwMuteID, szVolName, FALSE);
  486. }
  487. if (!fGotControls) // If we don't have them by now, we are in trouble.
  488. {
  489. hr = E_FAIL;
  490. }
  491. else
  492. {
  493. pCDUnit->dwMixID = dwMixID;
  494. pCDUnit->dwDestID = cdCtl.dwDestID;
  495. pCDUnit->dwVolID = cdCtl.dwVolID;
  496. pCDUnit->dwMuteID = cdCtl.dwMuteID;
  497. lstrcpy(pCDUnit->szVolName, szVolName);
  498. }
  499. }
  500. }
  501. RegCloseKey(hKey);
  502. }
  503. }
  504. return(hr);
  505. }
  506. ////////////////
  507. // This function writes out the preferred mixer device name and the preferred control ID's into the
  508. // cdrom driver registry for safe keeping.
  509. //
  510. STDMETHODIMP_(void) CCDOpt::SetUnitRegData(LPCDUNIT pCDUnit)
  511. {
  512. HRESULT hr = E_FAIL;
  513. TCHAR szRegDriverStr[MAX_PATH];
  514. HKEY hKey;
  515. CDCTL cdCtl;
  516. //this function is very different on NT
  517. OSVERSIONINFO os;
  518. os.dwOSVersionInfoSize = sizeof(os);
  519. GetVersionEx(&os);
  520. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  521. {
  522. if (pCDUnit)
  523. {
  524. wsprintf(szRegDriverStr,TEXT("%s%s"),szNTCDROMPath, pCDUnit->szDriver);
  525. if (RegCreateKeyEx(HKEY_CURRENT_USER , szRegDriverStr , 0 , NULL, 0, KEY_WRITE , NULL, &hKey, NULL) == ERROR_SUCCESS)
  526. {
  527. cdCtl.dwVolID = pCDUnit->dwVolID;
  528. cdCtl.dwMuteID = pCDUnit->dwMuteID;
  529. cdCtl.dwDestID = pCDUnit->dwDestID;
  530. RegSetValueEx(hKey, szPrefMixer, NULL, REG_SZ, (LPBYTE) pCDUnit->szMixerName, (sizeof(TCHAR) * lstrlen(pCDUnit->szMixerName))+sizeof(TCHAR));
  531. RegSetValueEx(hKey, szPrefControls, NULL, REG_BINARY, (LPBYTE) &cdCtl, sizeof(cdCtl));
  532. RegSetValueEx(hKey, szSelected, NULL, REG_BINARY, (LPBYTE) &(pCDUnit->fSelected), sizeof(BOOL));
  533. RegCloseKey(hKey);
  534. }
  535. }
  536. } //end if NT
  537. else
  538. {
  539. if (pCDUnit)
  540. {
  541. wsprintf(szRegDriverStr,TEXT("%s%s"),szRegstrCDROMPath, pCDUnit->szDriver);
  542. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE , szRegDriverStr , 0 , KEY_WRITE , &hKey) == ERROR_SUCCESS)
  543. {
  544. cdCtl.dwVolID = pCDUnit->dwVolID;
  545. cdCtl.dwMuteID = pCDUnit->dwMuteID;
  546. cdCtl.dwDestID = pCDUnit->dwDestID;
  547. RegSetValueEx(hKey, szPrefMixer, NULL, REG_SZ, (LPBYTE) pCDUnit->szMixerName, (sizeof(TCHAR) * lstrlen(pCDUnit->szMixerName))+sizeof(TCHAR));
  548. RegSetValueEx(hKey, szPrefControls, NULL, REG_BINARY, (LPBYTE) &cdCtl, sizeof(cdCtl));
  549. RegSetValueEx(hKey, szSelected, NULL, REG_BINARY, (LPBYTE) &(pCDUnit->fSelected), sizeof(BOOL));
  550. RegCloseKey(hKey);
  551. }
  552. }
  553. } //end else Win9x
  554. }
  555. //////////////////
  556. // Given just the drive letter of a CDROM, this function will obtain all information needed by the
  557. // program on this drive. Some is calcuated via the PnP registry for this drive's driver, other information
  558. // is obtained from the registry or defaults are computed based on the installed audio components
  559. //
  560. STDMETHODIMP_(void) CCDOpt::GetUnitValues(LPCDUNIT pCDUnit)
  561. {
  562. if (pCDUnit)
  563. {
  564. TCHAR cDriveLetter = pCDUnit->szDriveName[0];
  565. if (MapLetterToDevice(cDriveLetter, pCDUnit->szDriver, pCDUnit->szDeviceDesc, sizeof(pCDUnit->szDriver)))
  566. {
  567. if (FAILED(GetUnitRegData(pCDUnit))) // Can fail if reg is empty or mixer ID can't be computed
  568. {
  569. GetUnitDefaults(pCDUnit);
  570. }
  571. }
  572. else
  573. {
  574. GetUnitDefaults(pCDUnit);
  575. }
  576. }
  577. }
  578. //////////////////
  579. // This method is called in response to a PnP notify or a WinMM Device Change message. It will verify
  580. // the existance of all assigned MixerID's and the corresponding Volume and Mute ID on that device.
  581. // In the event the device no-longer exists a default will be computed.
  582. //
  583. //
  584. STDMETHODIMP_(void) CCDOpt::MMDeviceChanged(void)
  585. {
  586. LPCDUNIT pCDUnit = m_pCDOpts->pCDUnitList;
  587. while (pCDUnit)
  588. {
  589. GetUnitValues(pCDUnit);
  590. pCDUnit = pCDUnit->pNext;
  591. }
  592. }
  593. //////////////////
  594. // This function will save all the information for all the CD drives in the system out to the registry
  595. // it does not modify or delete any of this information.
  596. //
  597. STDMETHODIMP_(void) CCDOpt::WriteCDList(LPCDUNIT pCDList)
  598. {
  599. if (pCDList)
  600. {
  601. LPCDUNIT pUnit = pCDList;
  602. while (pUnit)
  603. {
  604. SetUnitRegData(pUnit);
  605. pUnit = pUnit->pNext;
  606. }
  607. }
  608. }
  609. ////////////////
  610. // This function will destory the list containing all the drive information in the system
  611. // freeing up all memory, this function does not save any information.
  612. //
  613. STDMETHODIMP_(void) CCDOpt::DestroyCDList(LPCDUNIT *ppCDList)
  614. {
  615. if (ppCDList && *ppCDList)
  616. {
  617. while(*ppCDList)
  618. {
  619. LPCDUNIT pTemp = *ppCDList;
  620. *ppCDList = (*ppCDList)->pNext;
  621. delete pTemp;
  622. }
  623. }
  624. }
  625. STDMETHODIMP_(UINT) CCDOpt::GetDefDrive(void)
  626. {
  627. HKEY hkTmp;
  628. UINT uDrive = 0;
  629. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, gszRegstrCDAPath, 0, KEY_READ, &hkTmp ) == ERROR_SUCCESS)
  630. {
  631. DWORD cb = sizeof(UINT);
  632. RegQueryValueEx(hkTmp, gszDefaultCDA, NULL, NULL, (LPBYTE)&uDrive, &cb);
  633. RegCloseKey(hkTmp);
  634. }
  635. return uDrive;
  636. }
  637. /////////////
  638. // This function builds a list of all the CD drives in the system, the list
  639. // is a linked list with each node containing information that is specific
  640. // to that CD player as well as the user options for each player.
  641. //
  642. STDMETHODIMP CCDOpt::CreateCDList(LPCDUNIT *ppCDList)
  643. {
  644. DWORD dwBytes = 0;
  645. TCHAR *szDriveNames = NULL;
  646. TCHAR *szDriveNamesHead = NULL;
  647. HRESULT hr = S_OK;
  648. UINT uDefDrive = GetDefDrive();
  649. if (ppCDList == NULL)
  650. {
  651. hr = E_INVALIDARG;
  652. }
  653. else
  654. {
  655. LPCDUNIT *ppUnit = NULL;
  656. *ppCDList = NULL;
  657. ppUnit = ppCDList;
  658. dwBytes = GetLogicalDriveStrings(0, NULL);
  659. if (dwBytes)
  660. {
  661. szDriveNames = new(TCHAR[dwBytes]);
  662. szDriveNamesHead = szDriveNames;
  663. if (szDriveNames == NULL)
  664. {
  665. hr = E_OUTOFMEMORY;
  666. }
  667. else
  668. {
  669. dwBytes = GetLogicalDriveStrings(dwBytes, szDriveNames);
  670. if (dwBytes)
  671. {
  672. UINT uDrive = 0;
  673. while (*szDriveNames)
  674. {
  675. if (GetDriveType(szDriveNames) == DRIVE_CDROM)
  676. {
  677. *ppUnit = new(CDUNIT);
  678. if (*ppUnit == NULL)
  679. {
  680. hr = E_OUTOFMEMORY;
  681. break;
  682. }
  683. else
  684. {
  685. memset(*ppUnit,0,sizeof(CDUNIT));
  686. _tcsncpy((*ppUnit)->szDriveName,szDriveNames,min(sizeof((*ppUnit)->szDriveName)/sizeof(TCHAR),(UINT)lstrlen(szDriveNames)));
  687. (*ppUnit)->dwTitleID = (DWORD)CDTITLE_NODISC;
  688. CharUpper((*ppUnit)->szDriveName);
  689. GetUnitValues(*ppUnit);
  690. if (uDrive == uDefDrive)
  691. {
  692. (*ppUnit)->fDefaultDrive = TRUE;
  693. }
  694. ppUnit = &((*ppUnit)->pNext);
  695. uDrive++;
  696. }
  697. }
  698. while(*szDriveNames++);
  699. }
  700. }
  701. }
  702. if (szDriveNamesHead)
  703. {
  704. delete [] szDriveNamesHead;
  705. }
  706. }
  707. }
  708. if (FAILED(hr))
  709. {
  710. DestroyCDList(ppCDList);
  711. }
  712. return hr;
  713. }
  714. /////////////
  715. // Traverse the UI Tree, destroying it as it goes.
  716. //
  717. STDMETHODIMP_(void) CCDOpt::DestroyUITree(LPCDDRIVE *ppCDRoot)
  718. {
  719. if (ppCDRoot)
  720. {
  721. LPCDDRIVE pDriveList;
  722. pDriveList = *ppCDRoot;
  723. *ppCDRoot = NULL;
  724. while (pDriveList)
  725. {
  726. LPCDDRIVE pNextDrive = pDriveList->pNext;
  727. while (pDriveList->pMixerList)
  728. {
  729. LPCDMIXER pNextMixer = pDriveList->pMixerList->pNext;
  730. while(pDriveList->pMixerList->pControlList)
  731. {
  732. LPCDCONTROL pNextControl = pDriveList->pMixerList->pControlList->pNext;
  733. delete pDriveList->pMixerList->pControlList;
  734. pDriveList->pMixerList->pControlList = pNextControl;
  735. }
  736. delete pDriveList->pMixerList;
  737. pDriveList->pMixerList = pNextMixer;
  738. }
  739. delete pDriveList;
  740. pDriveList = pNextDrive;
  741. }
  742. }
  743. }
  744. ///////////////////
  745. // Add line controls to internal tree data structure for UI
  746. //
  747. STDMETHODIMP CCDOpt::AddLineControls(LPCDMIXER pMixer, LPCDCONTROL *ppLastControl, int mxid, LPMIXERLINE pml)
  748. {
  749. MIXERLINECONTROLS mlc;
  750. DWORD dwControl;
  751. HRESULT hr = S_FALSE;
  752. memset(&mlc, 0, sizeof(mlc));
  753. mlc.cbStruct = sizeof(mlc);
  754. mlc.dwLineID = pml->dwLineID;
  755. mlc.cControls = pml->cControls;
  756. mlc.cbmxctrl = sizeof(MIXERCONTROL);
  757. mlc.pamxctrl = (LPMIXERCONTROL) new(MIXERCONTROL[pml->cControls]);
  758. if (mlc.pamxctrl)
  759. {
  760. if (mixerGetLineControls((HMIXEROBJ)IntToPtr(mxid), &mlc, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
  761. {
  762. for (dwControl = 0; dwControl < pml->cControls; dwControl++)
  763. {
  764. if (mlc.pamxctrl[dwControl].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_VOLUME)
  765. {
  766. DWORD dwIndex;
  767. DWORD dwVolID = mlc.pamxctrl[dwControl].dwControlID;
  768. DWORD dwMuteID = DWORD(-1);
  769. LPCDCONTROL pControl;
  770. for (dwIndex = 0; dwIndex < pml->cControls; dwIndex++)
  771. {
  772. if (mlc.pamxctrl[dwIndex].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_MUTE)
  773. {
  774. dwMuteID = mlc.pamxctrl[dwIndex].dwControlID;
  775. break;
  776. }
  777. }
  778. pControl = new (CDCONTROL);
  779. if (pControl == NULL)
  780. {
  781. hr = E_OUTOFMEMORY;
  782. }
  783. else
  784. {
  785. memset(pControl, 0, sizeof(CDCONTROL));
  786. if (pMixer->pControlList == NULL)
  787. {
  788. pMixer->pControlList = pControl;
  789. }
  790. if (*ppLastControl)
  791. {
  792. (*ppLastControl)->pNext = pControl;
  793. }
  794. lstrcpy(pControl->szName, pml->szName); // mlc.pamxctrl[dwControl].szName);
  795. pControl->dwVolID = dwVolID;
  796. pControl->dwMuteID = dwMuteID;
  797. *ppLastControl = pControl;
  798. hr = S_OK;
  799. }
  800. break;
  801. }
  802. }
  803. }
  804. delete mlc.pamxctrl;
  805. }
  806. return(hr);
  807. }
  808. /////////////////
  809. // Searchs connections for controls to add to UI tree
  810. //
  811. STDMETHODIMP CCDOpt::AddConnections(LPCDMIXER pMixer, LPCDCONTROL *ppLastControl, int mxid, DWORD dwDestination, DWORD dwConnections)
  812. {
  813. MIXERLINE mlDst;
  814. DWORD dwConnection;
  815. HRESULT hr = S_FALSE;
  816. BOOL bGotSomething=FALSE; //TRUE if any controls were found.
  817. for (dwConnection = 0; dwConnection < dwConnections; dwConnection++)
  818. {
  819. mlDst.cbStruct = sizeof ( mlDst );
  820. mlDst.dwDestination = dwDestination;
  821. mlDst.dwSource = dwConnection;
  822. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
  823. {
  824. if (mlDst.cControls) // Make sure this source has controls on it
  825. {
  826. if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC ||
  827. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_LINE ||
  828. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY ||
  829. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_DIGITAL ||
  830. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_ANALOG)
  831. {
  832. hr = AddLineControls(pMixer, ppLastControl, mxid, &mlDst);
  833. if (FAILED(hr))
  834. {
  835. break;
  836. }
  837. if(S_FALSE != hr)
  838. {
  839. bGotSomething = TRUE;
  840. }
  841. }
  842. }
  843. }
  844. }
  845. if(SUCCEEDED(hr) && bGotSomething)
  846. {
  847. return S_OK;
  848. }
  849. return(hr);
  850. }
  851. //////////////
  852. // Adds control nodes to the UI tree for the specified device
  853. //
  854. // Returns S_FALSE if no lines were added for this mixer.
  855. STDMETHODIMP CCDOpt::AddControls(LPCDMIXER pMixer)
  856. {
  857. MIXERLINE mlDst;
  858. DWORD dwDestination;
  859. MIXERCAPS mc;
  860. LPCDCONTROL pLastControl = NULL;
  861. HRESULT hr = S_FALSE;
  862. BOOL bGotSomething=FALSE; //TRUE if any valid lines were found on the mixer.
  863. if (mixerGetDevCaps(pMixer->dwMixID, &mc, sizeof(mc)) == MMSYSERR_NOERROR)
  864. {
  865. for (dwDestination = 0; dwDestination < mc.cDestinations; dwDestination++)
  866. {
  867. mlDst.cbStruct = sizeof ( mlDst );
  868. mlDst.dwDestination = dwDestination;
  869. if (mixerGetLineInfo((HMIXEROBJ)ULongToPtr(pMixer->dwMixID), &mlDst, MIXER_GETLINEINFOF_DESTINATION ) == MMSYSERR_NOERROR)
  870. {
  871. if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || // needs to be a likely output destination
  872. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES ||
  873. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT)
  874. {
  875. if (mlDst.cControls) // If there are no controls, we won't present it to the user as an option
  876. {
  877. hr = AddLineControls(pMixer, &pLastControl, pMixer->dwMixID, &mlDst);
  878. if (FAILED(hr))
  879. {
  880. break;
  881. }
  882. if(S_FALSE != hr)
  883. {
  884. bGotSomething = TRUE;
  885. }
  886. }
  887. if (mlDst.cConnections) // If there are connections to this line, lets add thier controls
  888. {
  889. hr = AddConnections(pMixer, &pLastControl, pMixer->dwMixID, dwDestination, mlDst.cConnections);
  890. if (FAILED(hr))
  891. {
  892. break;
  893. }
  894. if(S_FALSE != hr)
  895. {
  896. bGotSomething = TRUE;
  897. }
  898. }
  899. }
  900. }
  901. }
  902. }
  903. if(SUCCEEDED(hr) && bGotSomething)
  904. {
  905. return S_OK;
  906. }
  907. return(hr);
  908. }
  909. ///////////////////
  910. // Adds audio mixers to UI tree for the cd player unit specified
  911. //
  912. STDMETHODIMP CCDOpt::AddMixers(LPCDDRIVE pDevice)
  913. {
  914. HRESULT hr = S_OK;
  915. DWORD dwNumMixers;
  916. DWORD dwID;
  917. MMRESULT mmr;
  918. MIXERCAPS mc;
  919. LPCDMIXER pMixer;
  920. LPCDMIXER pLastMixer = NULL;
  921. if (pDevice)
  922. {
  923. dwNumMixers = mixerGetNumDevs();
  924. for (dwID = 0; dwID < dwNumMixers; dwID++)
  925. {
  926. mmr = mixerGetDevCaps(dwID, &mc, sizeof(mc));
  927. if (mmr == MMSYSERR_NOERROR && mc.cDestinations)
  928. {
  929. pMixer = new (CDMIXER);
  930. if (pMixer == NULL)
  931. {
  932. hr = E_OUTOFMEMORY;
  933. break;
  934. }
  935. else
  936. {
  937. memset(pMixer, 0, sizeof(CDMIXER));
  938. if (pDevice->pMixerList == NULL)
  939. {
  940. pDevice->pMixerList = pMixer;
  941. }
  942. lstrcpy(pMixer->szPname, mc.szPname);
  943. pMixer->dwMixID = dwID;
  944. hr = AddControls(pMixer);
  945. if (FAILED(hr))
  946. {
  947. break;
  948. }
  949. if(S_FALSE == hr)
  950. {
  951. //No valid line was found for this mixer.
  952. //Example: The mixer device has only a WaveIn (USB microphone). Bug #398736.
  953. delete pMixer;
  954. pMixer = NULL;
  955. continue;
  956. }
  957. if (pLastMixer)
  958. {
  959. pLastMixer->pNext = pMixer;
  960. }
  961. pLastMixer = pMixer;
  962. }
  963. }
  964. }
  965. }
  966. return(hr);
  967. }
  968. //////////////
  969. // Takes the UI tree and updates it's current and default setting links based on data
  970. // from the CDINFO tree which was created from the registry
  971. //
  972. STDMETHODIMP_(void) CCDOpt::SetUIDefaults(LPCDDRIVE pCDTree, LPCDUNIT pCDList)
  973. {
  974. LPCDDRIVE pDriveList;
  975. LPCDUNIT pCDUnit = pCDList;
  976. pDriveList = pCDTree;
  977. while(pDriveList && pCDUnit)
  978. {
  979. LPCDMIXER pMixer;
  980. pMixer = pDriveList->pMixerList;
  981. while (pMixer)
  982. {
  983. LPCDCONTROL pControl;
  984. DWORD dwVolID = DWORD(-1);
  985. DWORD dwMuteID = DWORD(-1);
  986. DWORD dwDestID = DWORD(-1);
  987. if (pMixer->dwMixID == pCDUnit->dwMixID)
  988. {
  989. pDriveList->pCurrentMixer = pMixer;
  990. pDriveList->pOriginalMixer = pMixer;
  991. }
  992. pControl = pMixer->pControlList;
  993. SearchDevice(pMixer->dwMixID, NULL, &dwDestID, &dwVolID, &dwMuteID, NULL, FALSE);
  994. while (pControl)
  995. {
  996. if (pControl->dwVolID == dwVolID && pControl->dwMuteID == dwMuteID)
  997. {
  998. pMixer->pDefaultControl = pControl;
  999. }
  1000. if (pMixer->dwMixID == pCDUnit->dwMixID && pControl->dwVolID == pCDUnit->dwVolID)
  1001. {
  1002. pMixer->pCurrentControl = pControl;
  1003. pMixer->pOriginalControl = pControl;
  1004. }
  1005. pControl = pControl->pNext;
  1006. }
  1007. pMixer = pMixer->pNext;
  1008. }
  1009. pDriveList = pDriveList->pNext;
  1010. pCDUnit = pCDUnit->pNext;
  1011. }
  1012. }
  1013. //////////////
  1014. // Takes the UI tree and updates it's current and default setting links based on data
  1015. // from the CDINFO tree which was created from the registry
  1016. //
  1017. STDMETHODIMP_(void) CCDOpt::RestoreOriginals(void)
  1018. {
  1019. LPCDDRIVE pDriveList;
  1020. pDriveList = m_pCDTree;
  1021. while(pDriveList)
  1022. {
  1023. LPCDMIXER pMixer;
  1024. pMixer = pDriveList->pMixerList;
  1025. while (pMixer)
  1026. {
  1027. LPCDCONTROL pControl;
  1028. pDriveList->pCurrentMixer = pDriveList->pOriginalMixer;
  1029. pControl = pMixer->pControlList;
  1030. while (pControl)
  1031. {
  1032. pMixer->pCurrentControl = pMixer->pOriginalControl;
  1033. pControl = pControl->pNext;
  1034. }
  1035. pMixer = pMixer->pNext;
  1036. }
  1037. pDriveList = pDriveList->pNext;
  1038. }
  1039. }
  1040. //////////////
  1041. // Updates the CD Tree from the UI Tree after the dialog closes (on OK), if there
  1042. // are any changes it returns true. The caller is expected to write these changes out to reg
  1043. //
  1044. STDMETHODIMP_(BOOL) CCDOpt::UpdateCDList(LPCDDRIVE pCDTree, LPCDUNIT pCDList)
  1045. {
  1046. BOOL fChanged = FALSE;
  1047. LPCDDRIVE pDriveList;
  1048. LPCDUNIT pCDUnit = pCDList;
  1049. pDriveList = pCDTree;
  1050. while(pDriveList && pCDUnit)
  1051. {
  1052. if (pCDUnit->fSelected != pDriveList->fSelected) // Selected drive has changed
  1053. {
  1054. pCDUnit->fSelected = pDriveList->fSelected;
  1055. fChanged = TRUE;
  1056. }
  1057. if (pDriveList->pCurrentMixer && pDriveList->pCurrentMixer->pCurrentControl)
  1058. {
  1059. LPCDMIXER pMixer = pDriveList->pCurrentMixer;
  1060. if (pMixer->dwMixID != pCDUnit->dwMixID) // Mixer has changed
  1061. {
  1062. pCDUnit->dwMixID = pMixer->dwMixID;
  1063. lstrcpy(pCDUnit->szMixerName, pMixer->szPname);
  1064. fChanged = TRUE;
  1065. }
  1066. LPCDCONTROL pControl = pDriveList->pCurrentMixer->pCurrentControl;
  1067. if (pControl->dwVolID != pCDUnit->dwVolID) // Control has changed
  1068. {
  1069. pCDUnit->dwVolID = pControl->dwVolID;
  1070. pCDUnit->dwMuteID = pControl->dwMuteID;
  1071. lstrcpy(pCDUnit->szVolName, pControl->szName);
  1072. fChanged = TRUE;
  1073. }
  1074. }
  1075. pDriveList = pDriveList->pNext;
  1076. pCDUnit = pCDUnit->pNext;
  1077. }
  1078. return(fChanged);
  1079. }
  1080. ////////////
  1081. // Contructs UI tree for all devices/mixers/controls
  1082. //
  1083. STDMETHODIMP CCDOpt::BuildUITree(LPCDDRIVE *ppCDRoot, LPCDUNIT pCDList)
  1084. {
  1085. LPCDUNIT pCDUnit = pCDList;
  1086. LPCDDRIVE pDevice;
  1087. LPCDDRIVE pLastDevice;
  1088. HRESULT hr = S_OK;
  1089. m_pCDSelected = NULL;
  1090. if (ppCDRoot)
  1091. {
  1092. *ppCDRoot = NULL;
  1093. pLastDevice = NULL;
  1094. while (pCDUnit)
  1095. {
  1096. pDevice = new (CDDRIVE);
  1097. if (pDevice == NULL)
  1098. {
  1099. hr = E_OUTOFMEMORY;
  1100. break;
  1101. }
  1102. else
  1103. {
  1104. memset(pDevice, 0, sizeof(CDDRIVE));
  1105. if (*ppCDRoot == NULL)
  1106. {
  1107. *ppCDRoot = pDevice;
  1108. }
  1109. if (pLastDevice)
  1110. {
  1111. pLastDevice->pNext = pDevice;
  1112. }
  1113. lstrcpy(pDevice->szDriveName, pCDUnit->szDriveName);
  1114. lstrcpy(pDevice->szDeviceDesc, pCDUnit->szDeviceDesc);
  1115. pDevice->fSelected = pCDUnit->fSelected;
  1116. hr = AddMixers(pDevice);
  1117. if (FAILED(hr))
  1118. {
  1119. break;
  1120. }
  1121. pCDUnit = pCDUnit->pNext;
  1122. pLastDevice = pDevice;
  1123. }
  1124. }
  1125. if (FAILED(hr))
  1126. {
  1127. if (*ppCDRoot)
  1128. {
  1129. DestroyUITree(ppCDRoot);
  1130. }
  1131. }
  1132. else
  1133. {
  1134. SetUIDefaults(*ppCDRoot, pCDList);
  1135. }
  1136. }
  1137. return(hr);
  1138. }
  1139. /////////////////
  1140. // Updates the control combo box using the mixer node of the UI tree
  1141. //
  1142. STDMETHODIMP_(void) CCDOpt::InitControlUI(HWND hDlg, LPCDMIXER pMixer)
  1143. {
  1144. LPCDCONTROL pControl;
  1145. LRESULT dwIndex;
  1146. SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_RESETCONTENT,0,0);
  1147. pControl = pMixer->pControlList;
  1148. if (pMixer->pCurrentControl == NULL)
  1149. {
  1150. pMixer->pCurrentControl = pMixer->pDefaultControl;
  1151. }
  1152. while(pControl)
  1153. {
  1154. dwIndex = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) pControl->szName);
  1155. if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
  1156. {
  1157. SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pControl);
  1158. if (pMixer->pCurrentControl == pControl)
  1159. {
  1160. SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_SETCURSEL, (WPARAM) dwIndex, 0);
  1161. }
  1162. }
  1163. pControl = pControl->pNext;
  1164. }
  1165. }
  1166. ////////////////
  1167. // Sets up the mixer combobox using the specified device
  1168. //
  1169. STDMETHODIMP_(void) CCDOpt::InitMixerUI(HWND hDlg, LPCDDRIVE pDevice)
  1170. {
  1171. LPCDMIXER pMixer;
  1172. LRESULT dwIndex;
  1173. SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_RESETCONTENT,0,0);
  1174. pMixer = pDevice->pMixerList;
  1175. while (pMixer)
  1176. {
  1177. dwIndex = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) pMixer->szPname);
  1178. if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
  1179. {
  1180. SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pMixer);
  1181. if (pDevice->pCurrentMixer == pMixer)
  1182. {
  1183. SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_SETCURSEL, (WPARAM) dwIndex, 0);
  1184. InitControlUI(hDlg, pMixer);
  1185. }
  1186. }
  1187. pMixer = pMixer->pNext;
  1188. }
  1189. }
  1190. ////////////////
  1191. // Sets up the cd device combo box
  1192. //
  1193. STDMETHODIMP_(void) CCDOpt::InitDeviceUI(HWND hDlg, LPCDDRIVE pCDTree, LPCDDRIVE pCurrentDevice)
  1194. {
  1195. LPCDDRIVE pDevice;
  1196. LRESULT dwIndex;
  1197. SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_RESETCONTENT,0,0);
  1198. pDevice = pCDTree;
  1199. while (pDevice)
  1200. {
  1201. TCHAR str[MAX_PATH];
  1202. wsprintf(str, TEXT("( %s ) %s"), pDevice->szDriveName, pDevice->szDeviceDesc);
  1203. dwIndex = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_INSERTSTRING, (WPARAM) -1, (LPARAM) str);
  1204. if (dwIndex != CB_ERR && dwIndex != CB_ERRSPACE)
  1205. {
  1206. SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) pDevice);
  1207. if (pDevice == pCurrentDevice)
  1208. {
  1209. SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_SETCURSEL, (WPARAM) dwIndex, 0);
  1210. InitMixerUI(hDlg, pDevice);
  1211. }
  1212. }
  1213. pDevice = pDevice->pNext;
  1214. }
  1215. }
  1216. ///////////////////
  1217. // Called to init the dialog when it first appears. Given a list of CD devices, this
  1218. // function fills out the dialog using the first device in the list.
  1219. //
  1220. STDMETHODIMP_(BOOL) CCDOpt::InitMixerConfig(HWND hDlg)
  1221. {
  1222. LPCDDRIVE pDevice = m_pCDTree;
  1223. while (pDevice)
  1224. {
  1225. if (pDevice->fSelected)
  1226. {
  1227. break;
  1228. }
  1229. pDevice = pDevice->pNext;
  1230. }
  1231. if (pDevice == NULL)
  1232. {
  1233. pDevice = m_pCDTree;
  1234. }
  1235. InitDeviceUI(hDlg, m_pCDTree, pDevice); // Init to the selected device
  1236. return(TRUE);
  1237. }
  1238. //////////////
  1239. // This function pulls the CD Drive node out of the combo box and returns it. It returns a
  1240. // reference into the main Drive tree.
  1241. //
  1242. STDMETHODIMP_(LPCDDRIVE) CCDOpt::GetCurrentDevice(HWND hDlg)
  1243. {
  1244. LRESULT dwResult;
  1245. LPCDDRIVE pDevice = NULL;
  1246. dwResult = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_GETCURSEL, 0, 0);
  1247. if (dwResult != CB_ERR)
  1248. {
  1249. dwResult = SendDlgItemMessage(hDlg, IDC_CDDRIVE, CB_GETITEMDATA, (WPARAM) dwResult, 0);
  1250. if (dwResult != CB_ERR)
  1251. {
  1252. pDevice = (LPCDDRIVE) dwResult;
  1253. }
  1254. }
  1255. return(pDevice);
  1256. }
  1257. //////////////
  1258. // This function pulls the CD Drive mixer out of the combo box and returns it. It returns a
  1259. // reference into the main Drive tree.
  1260. //
  1261. STDMETHODIMP_(LPCDMIXER) CCDOpt::GetCurrentMixer(HWND hDlg)
  1262. {
  1263. LRESULT dwResult;
  1264. LPCDMIXER pMixer = NULL;
  1265. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETCURSEL, 0, 0);
  1266. if (dwResult != CB_ERR)
  1267. {
  1268. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETITEMDATA, (WPARAM) dwResult, 0);
  1269. if (dwResult != CB_ERR)
  1270. {
  1271. pMixer = (LPCDMIXER) dwResult;
  1272. }
  1273. }
  1274. return(pMixer);
  1275. }
  1276. //////////////
  1277. // Called when the user changes the CD Device. it pulls the CD Drive node out of the combo box and
  1278. // then resets the UI to reflect that drives current settings.
  1279. //
  1280. STDMETHODIMP_(void) CCDOpt::ChangeCDDrives(HWND hDlg)
  1281. {
  1282. LPCDDRIVE pDevice = GetCurrentDevice(hDlg);
  1283. if (pDevice)
  1284. {
  1285. LPCDDRIVE pDev = m_pCDTree;
  1286. InitDeviceUI(hDlg, m_pCDTree, pDevice);
  1287. while (pDev)
  1288. {
  1289. pDev->fSelected = (BOOL) (pDev == pDevice);
  1290. pDev = pDev->pNext;
  1291. }
  1292. }
  1293. }
  1294. //////////
  1295. // Called when the user changes the current mixer for the drive, it updates the
  1296. // display and the info for the drive.
  1297. //
  1298. STDMETHODIMP_(void) CCDOpt::ChangeCDMixer(HWND hDlg)
  1299. {
  1300. LRESULT dwResult;
  1301. LPCDDRIVE pDevice = GetCurrentDevice(hDlg);
  1302. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETCURSEL, 0, 0);
  1303. if (dwResult != CB_ERR)
  1304. {
  1305. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOMIXER, CB_GETITEMDATA, (WPARAM) dwResult, 0);
  1306. if (dwResult != CB_ERR)
  1307. {
  1308. pDevice->pCurrentMixer = (LPCDMIXER) dwResult;
  1309. InitMixerUI(hDlg, pDevice);
  1310. }
  1311. }
  1312. }
  1313. /////////////////
  1314. // Called when the user changes the current control for the mixer on the current device.
  1315. // It updates the internal data structure for that drive.
  1316. //
  1317. STDMETHODIMP_(void) CCDOpt::ChangeCDControl(HWND hDlg)
  1318. {
  1319. LPCDMIXER pMixer = GetCurrentMixer(hDlg);
  1320. LRESULT dwResult;
  1321. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_GETCURSEL, 0, 0);
  1322. if (dwResult != CB_ERR)
  1323. {
  1324. dwResult = SendDlgItemMessage(hDlg, IDC_AUDIOCONTROL, CB_GETITEMDATA, (WPARAM) dwResult, 0);
  1325. if (dwResult != CB_ERR)
  1326. {
  1327. pMixer->pCurrentControl = (LPCDCONTROL) dwResult;
  1328. InitControlUI(hDlg, pMixer);
  1329. }
  1330. }
  1331. }
  1332. //////////////////
  1333. // Called to set the default control for the current mixer (lets the system pick)
  1334. //
  1335. STDMETHODIMP_(void) CCDOpt::SetMixerDefaults(HWND hDlg)
  1336. {
  1337. LPCDMIXER pMixer = GetCurrentMixer(hDlg);
  1338. if (pMixer)
  1339. {
  1340. pMixer->pCurrentControl = NULL;
  1341. InitControlUI(hDlg, pMixer);
  1342. }
  1343. }
  1344. ///////////////////
  1345. // Dialog handler for the mixer configuration dialog
  1346. //
  1347. STDMETHODIMP_(BOOL) CCDOpt::MixerConfig(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  1348. {
  1349. BOOL fResult = TRUE;
  1350. switch (msg)
  1351. {
  1352. default:
  1353. fResult = FALSE;
  1354. break;
  1355. case WM_CONTEXTMENU:
  1356. {
  1357. WinHelp((HWND)wParam, gszHelpFile, HELP_CONTEXTMENU, (ULONG_PTR)(LPSTR)aVolOptsHelp);
  1358. }
  1359. break;
  1360. case WM_HELP:
  1361. {
  1362. WinHelp((HWND) ((LPHELPINFO)lParam)->hItemHandle, gszHelpFile, HELP_WM_HELP, (ULONG_PTR)(LPSTR)aVolOptsHelp);
  1363. }
  1364. break;
  1365. case WM_INITDIALOG:
  1366. {
  1367. fResult = InitMixerConfig(hDlg);
  1368. }
  1369. break;
  1370. case WM_COMMAND:
  1371. {
  1372. switch (LOWORD(wParam))
  1373. {
  1374. case IDOK:
  1375. EndDialog(hDlg, TRUE);
  1376. break;
  1377. case IDCANCEL:
  1378. RestoreOriginals();
  1379. EndDialog(hDlg,FALSE);
  1380. break;
  1381. case IDC_DEFAULTMIXER:
  1382. SetMixerDefaults(hDlg);
  1383. break;
  1384. case IDC_CDDRIVE:
  1385. if (HIWORD(wParam) == CBN_SELCHANGE)
  1386. {
  1387. ChangeCDDrives(hDlg);
  1388. }
  1389. break;
  1390. case IDC_AUDIOMIXER:
  1391. if (HIWORD(wParam) == CBN_SELCHANGE)
  1392. {
  1393. ChangeCDMixer(hDlg);
  1394. }
  1395. break;
  1396. case IDC_AUDIOCONTROL:
  1397. if (HIWORD(wParam) == CBN_SELCHANGE)
  1398. {
  1399. ChangeCDControl(hDlg);
  1400. }
  1401. break;
  1402. default:
  1403. fResult = FALSE;
  1404. break;
  1405. }
  1406. }
  1407. break;
  1408. }
  1409. return fResult;
  1410. }
  1411. ///////////////////
  1412. // Dialog handler
  1413. //
  1414. INT_PTR CALLBACK CCDOpt::MixerConfigProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  1415. {
  1416. BOOL fResult = TRUE;
  1417. CCDOpt *pCDOpt = (CCDOpt *) GetWindowLongPtr(hDlg, DWLP_USER);
  1418. if (msg == WM_INITDIALOG)
  1419. {
  1420. pCDOpt = (CCDOpt *) lParam;
  1421. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) pCDOpt);
  1422. }
  1423. if (pCDOpt)
  1424. {
  1425. fResult = pCDOpt->MixerConfig(hDlg, msg, wParam, lParam);
  1426. }
  1427. if (msg == WM_DESTROY)
  1428. {
  1429. pCDOpt = NULL;
  1430. }
  1431. return(fResult);
  1432. }
  1433. ////////////
  1434. // Called to put up the UI to allow the user to change the CD Volume Configuration
  1435. //
  1436. STDMETHODIMP_(BOOL) CCDOpt::VolumeDialog(HWND hDlg)
  1437. {
  1438. BOOL fChanged = FALSE;
  1439. if (m_pCDOpts && m_pCDOpts->pCDUnitList)
  1440. {
  1441. if (SUCCEEDED(BuildUITree(&m_pCDTree, m_pCDOpts->pCDUnitList)))
  1442. {
  1443. if(DialogBoxParam( m_hInst, MAKEINTRESOURCE(IDD_MIXERPICKER), hDlg, CCDOpt::MixerConfigProc, (LPARAM) this) == TRUE)
  1444. {
  1445. fChanged = UpdateCDList(m_pCDTree, m_pCDOpts->pCDUnitList);
  1446. if (fChanged)
  1447. {
  1448. WriteCDList(m_pCDOpts->pCDUnitList);
  1449. }
  1450. }
  1451. DestroyUITree(&m_pCDTree);
  1452. }
  1453. }
  1454. return(fChanged);
  1455. }
  1456.