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.

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