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.

1388 lines
41 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // File: volume.c
  4. //
  5. // This file defines the functions that drive the volume
  6. // tab of the Sounds & Multimedia control panel.
  7. //
  8. // History:
  9. // 06 March 2000 RogerW
  10. // Created.
  11. //
  12. // Copyright (C) 2000 Microsoft Corporation All Rights Reserved.
  13. //
  14. // Microsoft Confidential
  15. //
  16. ///////////////////////////////////////////////////////////////////////////////
  17. //=============================================================================
  18. // Include files
  19. //=============================================================================
  20. #include <windows.h>
  21. #include <windowsx.h>
  22. #include <tchar.h>
  23. #include <regstr.h>
  24. #include <dbt.h>
  25. #include <mmsystem.h>
  26. #include <mmddkp.h>
  27. #include <shlwapi.h>
  28. #include "volume.h"
  29. #include "mmcpl.h"
  30. #include "trayvol.h"
  31. #include "advaudio.h"
  32. #include "medhelp.h"
  33. #include "multchan.h"
  34. // Constants
  35. const SIZE ksizeBrandMax = { 32, 32 }; // Max Branding Bitmap Size
  36. static SZCODE aszSndVolOptionKey[] = REGSTR_PATH_SETUP TEXT("\\SETUP\\OptionalComponents\\Vol");
  37. static SZCODE aszInstalled[] = TEXT("Installed");
  38. static const char aszSndVol32[] = "sndvol32.exe";
  39. #define VOLUME_TICS (500)
  40. static INTCODE aKeyWordIds[] =
  41. {
  42. IDC_VOLUME_BRAND, IDH_VOLUME_BRAND,
  43. IDC_VOLUME_MIXER, IDH_VOLUME_MIXER,
  44. IDC_GROUPBOX, IDH_SOUNDS_SYS_VOL_CONTROL,
  45. IDC_VOLUME_ICON, IDH_COMM_GROUPBOX,
  46. IDC_VOLUME_LOW, IDH_SOUNDS_SYS_VOL_CONTROL,
  47. IDC_MASTERVOLUME, IDH_SOUNDS_SYS_VOL_CONTROL,
  48. IDC_VOLUME_HIGH, IDH_SOUNDS_SYS_VOL_CONTROL,
  49. IDC_VOLUME_MUTE, IDH_SOUNDS_VOL_MUTE_BUTTON,
  50. IDC_TASKBAR_VOLUME, IDH_AUDIO_SHOW_INDICATOR,
  51. IDC_LAUNCH_SNDVOL, IDH_AUDIO_PLAY_VOL,
  52. IDC_GROUPBOX_2, IDH_COMM_GROUPBOX,
  53. IDC_VOLUME_SPEAKER_BITMAP, IDH_COMM_GROUPBOX,
  54. IDC_LAUNCH_MULTICHANNEL, IDH_LAUNCH_MULTICHANNEL,
  55. IDC_PLAYBACK_ADVSETUP, IDH_ADV_AUDIO_PLAY_PROP,
  56. IDC_TEXT_31, NO_HELP,
  57. 0,0
  58. };
  59. // TODO: Move to "regstr.h"
  60. #define REGSTR_KEY_BRANDING TEXT("Branding")
  61. #define REGSTR_VAL_AUDIO_BITMAP TEXT("bitmap")
  62. #define REGSTR_VAL_AUDIO_ICON TEXT("icon")
  63. #define REGSTR_VAL_AUDIO_URL TEXT("url")
  64. HBITMAP ghSpkBitmap;
  65. ///////////////////////////////////////////////////////////////////////////////
  66. //
  67. // %%Function: VolumeTabProc
  68. //
  69. // Parameters: hDlg = window handle of dialog window.
  70. // uiMessage = message ID.
  71. // wParam = message-dependent.
  72. // lParam = message-dependent.
  73. //
  74. // Returns: TRUE if message has been processed, else FALSE
  75. //
  76. // Description: Dialog proc for volume control panel page device change
  77. // message.
  78. //
  79. //
  80. ///////////////////////////////////////////////////////////////////////////////
  81. LRESULT CALLBACK VolumeTabProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  82. {
  83. if (iMsg == g_uiVolDevChange)
  84. {
  85. InitVolume (g_hWnd);
  86. }
  87. return CallWindowProc (g_fnVolPSProc, hwnd, iMsg, wParam, lParam);
  88. }
  89. ///////////////////////////////////////////////////////////////////////////////
  90. //
  91. // %%Function: VolumeDlg
  92. //
  93. // Parameters: hDlg = window handle of dialog window.
  94. // uiMessage = message ID.
  95. // wParam = message-dependent.
  96. // lParam = message-dependent.
  97. //
  98. // Returns: TRUE if message has been processed, else FALSE
  99. //
  100. // Description: Dialog proc for volume control panel page.
  101. //
  102. //
  103. ///////////////////////////////////////////////////////////////////////////////
  104. BOOL CALLBACK VolumeDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  105. {
  106. switch (uMsg)
  107. {
  108. case WM_NOTIFY:
  109. {
  110. OnNotify (hDlg, (LPNMHDR) lParam);
  111. }
  112. break;
  113. case WM_INITDIALOG:
  114. {
  115. HANDLE_WM_INITDIALOG (hDlg, wParam, lParam, OnInitDialog);
  116. }
  117. break;
  118. case WM_DESTROY:
  119. {
  120. HANDLE_WM_DESTROY (hDlg, wParam, lParam, OnDestroy);
  121. }
  122. break;
  123. case WM_COMMAND:
  124. {
  125. HANDLE_WM_COMMAND (hDlg, wParam, lParam, OnCommand);
  126. }
  127. break;
  128. case WM_POWERBROADCAST:
  129. {
  130. HandlePowerBroadcast (hDlg, wParam, lParam);
  131. }
  132. break;
  133. case MM_MIXM_LINE_CHANGE:
  134. case MM_MIXM_CONTROL_CHANGE:
  135. {
  136. if (!g_fInternalGenerated)
  137. {
  138. RefreshMixCache ();
  139. DisplayVolumeControl(hDlg);
  140. }
  141. g_fInternalGenerated = FALSE;
  142. }
  143. break;
  144. case WM_HSCROLL:
  145. {
  146. HANDLE_WM_HSCROLL (hDlg, wParam, lParam, MasterVolumeScroll);
  147. }
  148. break;
  149. case WM_CONTEXTMENU:
  150. {
  151. WinHelp ((HWND)wParam, NULL, HELP_CONTEXTMENU,
  152. (UINT_PTR)(LPTSTR)aKeyWordIds);
  153. }
  154. break;
  155. case WM_HELP:
  156. {
  157. WinHelp (((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP
  158. , (UINT_PTR)(LPTSTR)aKeyWordIds);
  159. }
  160. break;
  161. case WM_DEVICECHANGE:
  162. {
  163. DeviceChange_Change (hDlg, wParam, lParam);
  164. }
  165. break;
  166. case WM_SYSCOLORCHANGE:
  167. {
  168. if (ghSpkBitmap)
  169. {
  170. DeleteObject(ghSpkBitmap);
  171. ghSpkBitmap = NULL;
  172. }
  173. ghSpkBitmap = (HBITMAP) LoadImage(ghInstance,MAKEINTATOM(IDB_MULTICHANNEL_SPKR), IMAGE_BITMAP,
  174. 0, 0, LR_LOADMAP3DCOLORS);
  175. SendDlgItemMessage(hDlg, IDC_VOLUME_SPEAKER_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) ghSpkBitmap);
  176. }
  177. break;
  178. case WM_WININICHANGE:
  179. case WM_DISPLAYCHANGE :
  180. {
  181. SendDlgItemMessage (hDlg, IDC_MASTERVOLUME, uMsg, wParam, lParam);
  182. }
  183. break;
  184. default:
  185. break;
  186. }
  187. return FALSE;
  188. }
  189. void OnNotify (HWND hDlg, LPNMHDR pnmh)
  190. {
  191. if (!pnmh)
  192. {
  193. DPF ("bad WM_NOTIFY pointer\n");
  194. return;
  195. }
  196. switch (pnmh->code)
  197. {
  198. case PSN_KILLACTIVE:
  199. FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage);
  200. break;
  201. case PSN_APPLY:
  202. FORWARD_WM_COMMAND (hDlg, ID_APPLY, 0, 0, SendMessage);
  203. break;
  204. case PSN_RESET:
  205. FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage);
  206. break;
  207. }
  208. }
  209. void OnDestroy (HWND hDlg)
  210. {
  211. // Unregister from notifications
  212. DeviceChange_Cleanup();
  213. SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) g_fnVolPSProc);
  214. // Free any mixer we have
  215. FreeMixer ();
  216. // Free any branding bitmap
  217. FreeBrandBmp ();
  218. // Free detail memory
  219. DeleteObject( ghSpkBitmap );
  220. if (g_mcd.paDetails)
  221. {
  222. LocalFree (g_mcd.paDetails);
  223. g_mcd.paDetails = NULL;
  224. }
  225. if (g_pvPrevious)
  226. {
  227. LocalFree (g_pvPrevious);
  228. g_pvPrevious = NULL;
  229. }
  230. if (g_pdblCacheMix)
  231. {
  232. LocalFree (g_pdblCacheMix);
  233. g_pdblCacheMix = NULL;
  234. }
  235. // Free URL memory
  236. if( g_szHotLinkURL )
  237. {
  238. LocalFree( g_szHotLinkURL );
  239. g_szHotLinkURL = NULL;
  240. }
  241. ZeroMemory (&g_mcd, sizeof (g_mcd));
  242. ZeroMemory (&g_mlDst, sizeof (g_mlDst));
  243. }
  244. void CreateHotLink (BOOL fHotLink)
  245. {
  246. WCHAR szMixerName[MAXMIXERLEN];
  247. WCHAR* szLinkName;
  248. UINT uiLinkSize = 0;
  249. // Underline the mixer name to appear like a browser hot-link.
  250. HWND hWndMixerName = GetDlgItem (g_hWnd, IDC_VOLUME_MIXER);
  251. DWORD dwStyle = GetWindowLong (hWndMixerName, GWL_STYLE);
  252. if (fHotLink)
  253. {
  254. GetDlgItemText( g_hWnd, IDC_VOLUME_MIXER, szMixerName, MAXMIXERLEN);
  255. uiLinkSize = ((lstrlen(g_szHotLinkURL) * sizeof(WCHAR)) + (lstrlen(szMixerName) * sizeof(WCHAR))
  256. + (17 * sizeof(WCHAR))); // The 17 is for extra characters plus a NULL
  257. szLinkName = (WCHAR *)LocalAlloc (LPTR, uiLinkSize);
  258. if (szLinkName)
  259. {
  260. wsprintf(szLinkName, TEXT("<A HREF=\"%s\">%s</A>"), g_szHotLinkURL, szMixerName);
  261. SetDlgItemText( g_hWnd, IDC_VOLUME_MIXER, szLinkName);
  262. LocalFree(szLinkName);
  263. }
  264. EnableWindow(hWndMixerName, TRUE);
  265. dwStyle |= WS_TABSTOP;
  266. }
  267. else
  268. {
  269. EnableWindow(hWndMixerName, FALSE);
  270. dwStyle &= ~WS_TABSTOP;
  271. }
  272. // Apply new style (remove/add tab stop)
  273. SetWindowLong (hWndMixerName, GWL_STYLE, dwStyle);
  274. }
  275. BOOL OnInitDialog (HWND hDlg, HWND hwndFocus, LPARAM lParam)
  276. {
  277. // Init Globals
  278. g_hWnd = hDlg;
  279. g_fChanged = FALSE;
  280. g_fInternalGenerated = FALSE;
  281. // Set Device Change Notification
  282. g_fnVolPSProc = (WNDPROC) SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) VolumeTabProc);
  283. g_uiVolDevChange = RegisterWindowMessage (_T("winmm_devicechange"));
  284. // Save the default "No Audio Device" string
  285. GetDlgItemText (hDlg, IDC_VOLUME_MIXER, g_szNoAudioDevice, sizeof(g_szNoAudioDevice)/sizeof(g_szNoAudioDevice[0]));
  286. // Init Volume
  287. InitVolume (hDlg);
  288. if (ghSpkBitmap)
  289. {
  290. DeleteObject(ghSpkBitmap);
  291. ghSpkBitmap = NULL;
  292. }
  293. ghSpkBitmap = (HBITMAP) LoadImage(ghInstance,MAKEINTATOM(IDB_MULTICHANNEL_SPKR), IMAGE_BITMAP,
  294. 0, 0, LR_LOADMAP3DCOLORS);
  295. SendDlgItemMessage(hDlg, IDC_VOLUME_SPEAKER_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) ghSpkBitmap);
  296. return FALSE;
  297. }
  298. BOOL PASCAL OnCommand (HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
  299. {
  300. switch (id)
  301. {
  302. case IDC_TASKBAR_VOLUME:
  303. {
  304. if (Button_GetCheck (GetDlgItem (hDlg, IDC_TASKBAR_VOLUME)) && (!SndVolPresent ()))
  305. {
  306. CheckDlgButton (hDlg, IDC_TASKBAR_VOLUME, FALSE);
  307. ErrorBox (hDlg, IDS_NOSNDVOL, NULL);
  308. g_sndvolPresent = sndvolNotChecked; // Reset
  309. }
  310. else
  311. {
  312. g_fTrayIcon = Button_GetCheck (GetDlgItem (hDlg, IDC_TASKBAR_VOLUME));
  313. PropSheet_Changed(GetParent(hDlg),hDlg);
  314. g_fChanged = TRUE;
  315. }
  316. }
  317. break;
  318. case IDC_VOLUME_MUTE:
  319. {
  320. BOOL fMute = !GetMute ();
  321. SetMute(fMute);
  322. if ((g_fPreviousMute != fMute) && !g_fChanged)
  323. {
  324. g_fChanged = TRUE;
  325. PropSheet_Changed(GetParent(hDlg),hDlg);
  326. }
  327. }
  328. break;
  329. case ID_APPLY:
  330. {
  331. // Update Tray Icon
  332. BOOL fTrayIcon = Button_GetCheck (GetDlgItem (hDlg, IDC_TASKBAR_VOLUME));
  333. if (fTrayIcon != GetTrayVolumeEnabled ())
  334. {
  335. g_fTrayIcon = fTrayIcon;
  336. SetTrayVolumeEnabled(g_fTrayIcon);
  337. }
  338. if (SUCCEEDED (GetVolume ()) && g_pvPrevious && g_mcd.paDetails)
  339. {
  340. // Copy data so can undo volume changes
  341. memcpy (g_pvPrevious, g_mcd.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  342. DisplayVolumeControl (hDlg);
  343. }
  344. g_fPreviousMute = GetMute ();
  345. g_fChanged = FALSE;
  346. return TRUE;
  347. }
  348. break;
  349. case IDOK:
  350. {
  351. // OK processing handled in ID_APPLY because it is always
  352. // called from the property sheet's IDOK processing.
  353. }
  354. break;
  355. case IDCANCEL:
  356. {
  357. if (g_hMixer)
  358. {
  359. SetMute (g_fPreviousMute);
  360. if (g_pvPrevious && g_mcd.paDetails)
  361. {
  362. // Undo volume changes
  363. memcpy (g_mcd.paDetails, g_pvPrevious, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  364. g_fInternalGenerated = TRUE;
  365. mixerSetControlDetails ((HMIXEROBJ) g_hMixer, &g_mcd, MIXER_SETCONTROLDETAILSF_VALUE);
  366. g_fInternalGenerated = FALSE;
  367. }
  368. }
  369. WinHelp (hDlg, gszWindowsHlp, HELP_QUIT, 0L);
  370. }
  371. break;
  372. case IDC_LAUNCH_SNDVOL:
  373. {
  374. TCHAR szCmd[MAXSTR];
  375. STARTUPINFO si;
  376. PROCESS_INFORMATION pi;
  377. memset(&si, 0, sizeof(si));
  378. si.cb = sizeof(si);
  379. si.wShowWindow = SW_SHOW;
  380. si.dwFlags = STARTF_USESHOWWINDOW;
  381. wsprintf(szCmd,TEXT("sndvol32.exe -D %d"), g_uiMixID);
  382. if (!CreateProcess(NULL,szCmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
  383. {
  384. ErrorBox (hDlg, IDS_NOSNDVOL, NULL);
  385. }
  386. }
  387. break;
  388. case IDC_LAUNCH_MULTICHANNEL:
  389. {
  390. Multichannel (hDlg, g_uiMixID, g_dwDest, g_dwVolID);
  391. }
  392. break;
  393. case IDC_PLAYBACK_ADVSETUP:
  394. {
  395. MIXERCAPS mc;
  396. DWORD dwDeviceID = g_uiMixID;
  397. if (MMSYSERR_NOERROR == mixerGetDevCaps (g_uiMixID, &mc, sizeof (mc)))
  398. {
  399. AdvancedAudio (hDlg, ghInstance, gszWindowsHlp, dwDeviceID, mc.szPname, FALSE);
  400. }
  401. }
  402. break;
  403. }
  404. return FALSE;
  405. }
  406. void InitVolume (HWND hDlg)
  407. {
  408. FreeMixer ();
  409. // Get the master volume & display
  410. MasterVolumeConfig (hDlg, &g_uiMixID);
  411. if (SUCCEEDED (GetVolume ()) && g_pvPrevious && g_mcd.paDetails)
  412. {
  413. RefreshMixCache ();
  414. // Copy data so can undo volume changes
  415. memcpy (g_pvPrevious, g_mcd.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  416. g_fPreviousMute = GetMute ();
  417. }
  418. DisplayVolumeControl (hDlg);
  419. DeviceChange_Init (hDlg, g_uiMixID);
  420. }
  421. // returns current mute state
  422. BOOL GetMute ()
  423. {
  424. BOOL fMute = FALSE;
  425. if (g_hMixer && (g_dwMuteID != (DWORD) -1))
  426. {
  427. MIXERCONTROLDETAILS_UNSIGNED mcuMute;
  428. MIXERCONTROLDETAILS mcd = g_mcd;
  429. // Modify local copy for mute ...
  430. mcd.dwControlID = g_dwMuteID;
  431. mcd.cChannels = 1;
  432. mcd.paDetails = &mcuMute;
  433. mixerGetControlDetails ((HMIXEROBJ) g_hMixer, &mcd, MIXER_GETCONTROLDETAILSF_VALUE);
  434. fMute = (BOOL) mcuMute.dwValue;
  435. }
  436. return fMute;
  437. }
  438. BOOL SndVolPresent ()
  439. {
  440. if (g_sndvolPresent == sndvolNotChecked)
  441. {
  442. OFSTRUCT of;
  443. if (HFILE_ERROR != OpenFile (aszSndVol32, &of, OF_EXIST | OF_SHARE_DENY_NONE))
  444. {
  445. g_sndvolPresent = sndvolPresent;
  446. }
  447. else
  448. {
  449. HKEY hkSndVol;
  450. g_sndvolPresent = sndvolNotPresent;
  451. if (!RegOpenKey (HKEY_LOCAL_MACHINE, aszSndVolOptionKey, &hkSndVol))
  452. {
  453. RegSetValueEx (hkSndVol, (LPTSTR) aszInstalled, 0L, REG_SZ, (LPBYTE)(TEXT("0")), 4);
  454. RegCloseKey (hkSndVol);
  455. }
  456. }
  457. }
  458. return (sndvolPresent == g_sndvolPresent);
  459. }
  460. void FreeMixer ()
  461. {
  462. if (g_hMixer)
  463. {
  464. mixerClose (g_hMixer);
  465. g_hMixer = NULL;
  466. }
  467. }
  468. // Gets the primary audio device ID and find the mixer line for it
  469. // It leaves it open so the slider can respond to other changes outside this app
  470. //
  471. void MasterVolumeConfig (HWND hWnd, UINT* puiMixID)
  472. {
  473. UINT uiWaveID;
  474. // Init
  475. g_fMasterVolume = g_fTrayIcon = g_fMasterMute = FALSE;
  476. g_dwDest = g_dwVolID = g_dwMuteID = 0;
  477. ResetBranding (hWnd);
  478. if (puiMixID && GetWaveID (&uiWaveID) == MMSYSERR_NOERROR)
  479. {
  480. if (MMSYSERR_NOERROR == mixerGetID (HMIXEROBJ_INDEX(uiWaveID), puiMixID, MIXER_OBJECTF_WAVEOUT))
  481. {
  482. SetBranding (hWnd, *puiMixID);
  483. if (SearchDevice (*puiMixID, &g_dwDest, &g_dwVolID, &g_dwMuteID))
  484. {
  485. FreeMixer ();
  486. if (MMSYSERR_NOERROR == mixerOpen (&g_hMixer, *puiMixID, (DWORD_PTR) hWnd, 0L, CALLBACK_WINDOW))
  487. {
  488. ZeroMemory (&g_mlDst, sizeof (g_mlDst));
  489. g_mlDst.cbStruct = sizeof (g_mlDst);
  490. g_mlDst.dwDestination = g_dwDest;
  491. if (MMSYSERR_NOERROR == mixerGetLineInfo ((HMIXEROBJ)g_hMixer, &g_mlDst, MIXER_GETLINEINFOF_DESTINATION))
  492. {
  493. g_mcd.cbStruct = sizeof (g_mcd);
  494. g_mcd.dwControlID = g_dwVolID;
  495. g_mcd.cChannels = g_mlDst.cChannels;
  496. g_mcd.hwndOwner = 0;
  497. g_mcd.cMultipleItems = 0;
  498. g_mcd.cbDetails = sizeof (DWORD); // seems like it would be sizeof(g_mcd),
  499. // but actually, it is the size of a single value
  500. // and is multiplied by channel in the driver.
  501. // TODO: Should Return Error on failure!
  502. g_mcd.paDetails = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  503. g_pvPrevious = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  504. g_pdblCacheMix = (double*) LocalAlloc (LPTR, sizeof (double) * g_mlDst.cChannels);
  505. g_fMasterVolume = TRUE;
  506. g_fMasterMute = (g_dwMuteID != (DWORD) -1);
  507. g_fTrayIcon = GetTrayVolumeEnabled ();
  508. }
  509. }
  510. }
  511. }
  512. }
  513. }
  514. // Locates the master volume and mute controls for this mixer line
  515. //
  516. void SearchControls(int mxid, LPMIXERLINE pml, LPDWORD pdwVolID, LPDWORD pdwMuteID, BOOL *pfFound)
  517. {
  518. MIXERLINECONTROLS mlc;
  519. DWORD dwControl;
  520. memset(&mlc, 0, sizeof(mlc));
  521. mlc.cbStruct = sizeof(mlc);
  522. mlc.dwLineID = pml->dwLineID;
  523. mlc.cControls = pml->cControls;
  524. mlc.cbmxctrl = sizeof(MIXERCONTROL);
  525. mlc.pamxctrl = (LPMIXERCONTROL) GlobalAlloc(GMEM_FIXED, sizeof(MIXERCONTROL) * pml->cControls);
  526. if (mlc.pamxctrl)
  527. {
  528. if (mixerGetLineControls(HMIXEROBJ_INDEX(mxid), &mlc, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
  529. {
  530. for (dwControl = 0; dwControl < pml->cControls && !(*pfFound); dwControl++)
  531. {
  532. if (mlc.pamxctrl[dwControl].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_VOLUME)
  533. {
  534. DWORD dwIndex;
  535. DWORD dwVolID = (DWORD) -1;
  536. DWORD dwMuteID = (DWORD) -1;
  537. dwVolID = mlc.pamxctrl[dwControl].dwControlID;
  538. for (dwIndex = 0; dwIndex < pml->cControls; dwIndex++)
  539. {
  540. if (mlc.pamxctrl[dwIndex].dwControlType == (DWORD)MIXERCONTROL_CONTROLTYPE_MUTE)
  541. {
  542. dwMuteID = mlc.pamxctrl[dwIndex].dwControlID;
  543. break;
  544. }
  545. }
  546. *pfFound = TRUE;
  547. *pdwVolID = dwVolID;
  548. *pdwMuteID = dwMuteID;
  549. }
  550. }
  551. }
  552. GlobalFree((HGLOBAL) mlc.pamxctrl);
  553. }
  554. }
  555. // Locates the volume slider control for this mixer device
  556. //
  557. BOOL SearchDevice (DWORD dwMixID, LPDWORD pdwDest, LPDWORD pdwVolID, LPDWORD pdwMuteID)
  558. {
  559. MIXERCAPS mc;
  560. MMRESULT mmr;
  561. BOOL fFound = FALSE;
  562. mmr = mixerGetDevCaps(dwMixID, &mc, sizeof(mc));
  563. if (mmr == MMSYSERR_NOERROR)
  564. {
  565. MIXERLINE mlDst;
  566. DWORD dwDestination;
  567. for (dwDestination = 0; dwDestination < mc.cDestinations && !fFound; dwDestination++)
  568. {
  569. mlDst.cbStruct = sizeof ( mlDst );
  570. mlDst.dwDestination = dwDestination;
  571. if (mixerGetLineInfo(HMIXEROBJ_INDEX(dwMixID), &mlDst, MIXER_GETLINEINFOF_DESTINATION ) == MMSYSERR_NOERROR)
  572. {
  573. if (mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || // needs to be a likely output destination
  574. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_DST_HEADPHONES ||
  575. mlDst.dwComponentType == (DWORD)MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT)
  576. {
  577. if (!fFound && mlDst.cControls) // If there are controls, we'll take the master
  578. {
  579. SearchControls(dwMixID, &mlDst, pdwVolID, pdwMuteID, &fFound);
  580. *pdwDest = dwDestination;
  581. }
  582. }
  583. }
  584. }
  585. }
  586. return(fFound);
  587. }
  588. // Call this function to configure to the current preferred device and reflect master volume
  589. // settings on the slider
  590. //
  591. void DisplayVolumeControl (HWND hDlg)
  592. {
  593. HWND hwndVol = GetDlgItem(hDlg, IDC_MASTERVOLUME);
  594. BOOL fMute = g_fMasterMute && GetMute ();
  595. SendMessage(hwndVol, TBM_SETTICFREQ, VOLUME_TICS / 10, 0);
  596. SendMessage(hwndVol, TBM_SETRANGE, FALSE, MAKELONG(0,VOLUME_TICS));
  597. EnableWindow(GetDlgItem(hDlg, IDC_MASTERVOLUME) , g_fMasterVolume);
  598. EnableWindow(GetDlgItem(hDlg, IDC_VOLUME_LOW) , g_fMasterVolume);
  599. EnableWindow(GetDlgItem(hDlg, IDC_VOLUME_HIGH) , g_fMasterVolume);
  600. EnableWindow(GetDlgItem(hDlg, IDC_TASKBAR_VOLUME),g_fMasterVolume);
  601. EnableWindow(GetDlgItem(hDlg, IDC_VOLUME_MUTE), g_fMasterMute);
  602. EnableWindow(GetDlgItem(hDlg, IDC_LAUNCH_SNDVOL), g_fMasterVolume && SndVolPresent ());
  603. EnableWindow(GetDlgItem(hDlg, IDC_LAUNCH_MULTICHANNEL), g_fMasterVolume);
  604. EnableWindow(GetDlgItem(hDlg, IDC_PLAYBACK_ADVSETUP), g_fMasterVolume);
  605. if (g_fMasterVolume)
  606. {
  607. UpdateVolumeSlider (hDlg, g_dwVolID);
  608. }
  609. else
  610. {
  611. SendMessage(GetDlgItem(hDlg, IDC_MASTERVOLUME), TBM_SETPOS, TRUE, 0 );
  612. }
  613. // Show if we are muted
  614. Button_SetCheck (GetDlgItem (hDlg, IDC_VOLUME_MUTE), fMute);
  615. // This displays the appropriate volume icon for the master mute state
  616. // This looks like a memory leak, but it's not. LoadIcon just gets a handle to the already-loaded
  617. // icon if it was previously loaded. See the docs for LoadIcon and DestroyIcon (which specifically
  618. // should not be called with a handle to an icon loaded via LoadIcon).
  619. if( fMute )
  620. {
  621. SendMessage (GetDlgItem (hDlg, IDC_VOLUME_ICON), STM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon(ghInstance, MAKEINTRESOURCE (IDI_MUTESPEAKERICON)) );
  622. }
  623. else
  624. {
  625. SendMessage (GetDlgItem (hDlg, IDC_VOLUME_ICON), STM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon(ghInstance, MAKEINTRESOURCE (IDI_SPEAKERICON)) );
  626. }
  627. CheckDlgButton (hDlg, IDC_TASKBAR_VOLUME, g_fTrayIcon);
  628. }
  629. // Called to update the slider when the volume is changed externally
  630. //
  631. void UpdateVolumeSlider(HWND hWnd, DWORD dwLine)
  632. {
  633. if ((g_hMixer != NULL) && (g_dwVolID != (DWORD) -1) && (dwLine == g_dwVolID))
  634. {
  635. double volume = ((double) GetMaxVolume () / (double) 0xFFFF) * ((double) VOLUME_TICS);
  636. // The 0.5f forces rounding (instead of truncation)
  637. SendMessage(GetDlgItem(hWnd, IDC_MASTERVOLUME), TBM_SETPOS, TRUE, (DWORD) (volume+0.5f) );
  638. }
  639. }
  640. // returns current volume level
  641. //
  642. DWORD GetMaxVolume ()
  643. {
  644. DWORD dwVol = 0;
  645. if (SUCCEEDED (GetVolume ()))
  646. {
  647. UINT uiIndx;
  648. MIXERCONTROLDETAILS_UNSIGNED* pmcuVolume;
  649. for (uiIndx = 0; uiIndx < g_mlDst.cChannels; uiIndx++)
  650. {
  651. pmcuVolume = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcd.paDetails + uiIndx);
  652. dwVol = max (dwVol, pmcuVolume -> dwValue);
  653. }
  654. }
  655. return dwVol;
  656. }
  657. HRESULT GetVolume ()
  658. {
  659. HRESULT hr = E_FAIL;
  660. if (g_hMixer && g_mcd.paDetails)
  661. {
  662. g_mcd.dwControlID = g_dwVolID;
  663. ZeroMemory (g_mcd.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels);
  664. hr = mixerGetControlDetails ((HMIXEROBJ)g_hMixer, &g_mcd, MIXER_GETCONTROLDETAILSF_VALUE);
  665. }
  666. return hr;
  667. }
  668. void DeviceChange_Cleanup ()
  669. {
  670. if (g_hDeviceEventContext)
  671. {
  672. UnregisterDeviceNotification (g_hDeviceEventContext);
  673. g_hDeviceEventContext = NULL;
  674. }
  675. }
  676. BOOL DeviceChange_GetHandle(DWORD dwMixerID, HANDLE *phDevice)
  677. {
  678. MMRESULT mmr;
  679. ULONG cbSize=0;
  680. TCHAR *szInterfaceName=NULL;
  681. //Query for the Device interface name
  682. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  683. if(MMSYSERR_NOERROR == mmr)
  684. {
  685. szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  686. if(!szInterfaceName)
  687. {
  688. return FALSE;
  689. }
  690. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  691. if(MMSYSERR_NOERROR != mmr)
  692. {
  693. GlobalFreePtr(szInterfaceName);
  694. return FALSE;
  695. }
  696. }
  697. else
  698. {
  699. return FALSE;
  700. }
  701. //Get an handle on the device interface name.
  702. *phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  703. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  704. GlobalFreePtr(szInterfaceName);
  705. if(INVALID_HANDLE_VALUE == *phDevice)
  706. {
  707. return FALSE;
  708. }
  709. return TRUE;
  710. }
  711. void DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
  712. {
  713. DEV_BROADCAST_HANDLE DevBrodHandle;
  714. HANDLE hMixerDevice=NULL;
  715. //If we had registered already for device notifications, unregister ourselves.
  716. DeviceChange_Cleanup();
  717. //If we get the device handle register for device notifications on it.
  718. if(DeviceChange_GetHandle(dwMixerID, &hMixerDevice))
  719. {
  720. memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
  721. DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  722. DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  723. DevBrodHandle.dbch_handle = hMixerDevice;
  724. g_hDeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  725. if(hMixerDevice)
  726. {
  727. CloseHandle(hMixerDevice);
  728. hMixerDevice = NULL;
  729. }
  730. }
  731. }
  732. // Handle the case where we need to dump mixer handle so PnP can get rid of a device
  733. // We assume we will get the WINMM_DEVICECHANGE handle when the dust settles after a remove or add
  734. // except for DEVICEQUERYREMOVEFAILED which will not generate that message.
  735. //
  736. void DeviceChange_Change(HWND hDlg, WPARAM wParam, LPARAM lParam)
  737. {
  738. PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
  739. if(!g_hDeviceEventContext || !bh || bh->dbch_devicetype != DBT_DEVTYP_HANDLE)
  740. {
  741. return;
  742. }
  743. switch (wParam)
  744. {
  745. case DBT_DEVICEQUERYREMOVE: // Must free up Mixer if they are trying to remove the device
  746. {
  747. FreeMixer ();
  748. }
  749. break;
  750. case DBT_DEVICEQUERYREMOVEFAILED: // Didn't happen, need to re-acquire mixer
  751. {
  752. InitVolume (hDlg);
  753. }
  754. break;
  755. }
  756. }
  757. // Sets the mute state
  758. void SetMute(BOOL fMute)
  759. {
  760. if (g_hMixer)
  761. {
  762. MIXERCONTROLDETAILS_UNSIGNED mcuMute;
  763. MIXERCONTROLDETAILS mcd = g_mcd;
  764. // Modify local copy for mute ...
  765. mcuMute.dwValue = (DWORD) fMute;
  766. mcd.dwControlID = g_dwMuteID;
  767. mcd.cChannels = 1;
  768. mcd.paDetails = &mcuMute;
  769. mixerSetControlDetails ((HMIXEROBJ)g_hMixer, &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
  770. }
  771. }
  772. // Called in response to slider movement, computes new volume level and sets it
  773. // it also controls the apply state (changed or not)
  774. //
  775. void MasterVolumeScroll (HWND hwnd, HWND hwndCtl, UINT code, int pos)
  776. {
  777. DWORD dwVol = (DWORD) SendMessage(GetDlgItem(hwnd, IDC_MASTERVOLUME), TBM_GETPOS, 0, 0);
  778. dwVol = (DWORD) (((double) dwVol / (double) VOLUME_TICS) * (double) 0xFFFF);
  779. SetVolume(dwVol);
  780. if (!g_fChanged && (memcmp (g_pvPrevious, g_mcd.paDetails,
  781. sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlDst.cChannels)))
  782. {
  783. g_fChanged = TRUE;
  784. PropSheet_Changed(GetParent(hwnd),hwnd);
  785. }
  786. // Play a sound on for the master volume slider when the
  787. // user ends the scroll and we are still in focus and the topmost app.
  788. if (code == SB_ENDSCROLL && hwndCtl == GetFocus() && GetParent (hwnd) == GetForegroundWindow ())
  789. {
  790. static const TCHAR cszDefSnd[] = TEXT(".Default");
  791. PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
  792. }
  793. }
  794. // Sets the volume level
  795. //
  796. void SetVolume (DWORD dwVol)
  797. {
  798. if (g_hMixer && g_pdblCacheMix && g_mcd.paDetails)
  799. {
  800. UINT uiIndx;
  801. MIXERCONTROLDETAILS_UNSIGNED* pmcuVolume;
  802. // Caculate the new volume level for each of the channels. For volume levels
  803. // at the current max, we simply set the newly requested level (in this case
  804. // the cache value is 1.0). For those less than the max, we set a value that
  805. // is a percentage of the max. This maintains the relative distance of the
  806. // channel levels from each other.
  807. for (uiIndx = 0; uiIndx < g_mlDst.cChannels; uiIndx++)
  808. {
  809. pmcuVolume = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcd.paDetails + uiIndx);
  810. // The 0.5f forces rounding (instead of truncation)
  811. pmcuVolume -> dwValue = (DWORD)(*(g_pdblCacheMix + uiIndx) * (double) dwVol + 0.5f);
  812. }
  813. g_fInternalGenerated = TRUE;
  814. mixerSetControlDetails ((HMIXEROBJ)g_hMixer, &g_mcd, MIXER_SETCONTROLDETAILSF_VALUE);
  815. g_fInternalGenerated = FALSE;
  816. }
  817. }
  818. void HandlePowerBroadcast (HWND hWnd, WPARAM wParam, LPARAM lParam)
  819. {
  820. switch (wParam)
  821. {
  822. case PBT_APMQUERYSUSPEND:
  823. {
  824. FreeMixer ();
  825. }
  826. break;
  827. case PBT_APMQUERYSUSPENDFAILED:
  828. case PBT_APMRESUMESUSPEND:
  829. {
  830. InitVolume (hWnd);
  831. }
  832. break;
  833. }
  834. }
  835. BOOL ChannelsAllMinimum()
  836. {
  837. MIXERCONTROLDETAILS_UNSIGNED* pmcuVolume;
  838. if (g_hMixer && g_mcd.paDetails)
  839. {
  840. UINT uiIndx;
  841. for (uiIndx = 0; uiIndx < g_mlDst.cChannels; uiIndx++)
  842. {
  843. pmcuVolume = (MIXERCONTROLDETAILS_UNSIGNED*)g_mcd.paDetails + uiIndx;
  844. if ( pmcuVolume->dwValue != 0)
  845. {
  846. return (FALSE);
  847. }
  848. }
  849. return (TRUE); // Volume of all channels equals zero since we haven't returned yet.
  850. }
  851. else return (FALSE);
  852. }
  853. void RefreshMixCache ()
  854. {
  855. if (g_fCacheCreated && ChannelsAllMinimum())
  856. {
  857. return;
  858. }
  859. if (g_pdblCacheMix && g_hMixer && g_mcd.paDetails)
  860. {
  861. UINT uiIndx;
  862. double* pdblMixPercent;
  863. MIXERCONTROLDETAILS_UNSIGNED* pmcuVolume;
  864. // Note: This call does a GetVolume(), so no need to call it again...
  865. DWORD dwMaxVol = GetMaxVolume ();
  866. // Caculate the percentage distance each channel is away from the max
  867. // value. Creating this cache allows us to maintain the relative distance
  868. // of the channel levels from each other as the user adjusts the master
  869. // volume level.
  870. for (uiIndx = 0; uiIndx < g_mlDst.cChannels; uiIndx++)
  871. {
  872. pmcuVolume = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcd.paDetails + uiIndx);
  873. pdblMixPercent = (g_pdblCacheMix + uiIndx);
  874. // Caculate the percentage this value is from the max ...
  875. if (dwMaxVol == pmcuVolume -> dwValue)
  876. {
  877. *pdblMixPercent = 1.0F;
  878. }
  879. else
  880. {
  881. *pdblMixPercent = ((double) pmcuVolume -> dwValue / (double) dwMaxVol);
  882. }
  883. }
  884. g_fCacheCreated = TRUE;
  885. }
  886. }
  887. void FreeBrandBmp ()
  888. {
  889. if (g_hbmBrand)
  890. {
  891. DeleteObject (g_hbmBrand);
  892. g_hbmBrand = NULL;
  893. }
  894. }
  895. void ResetBranding (HWND hwnd)
  896. {
  897. FreeBrandBmp ();
  898. if( g_szHotLinkURL )
  899. {
  900. LocalFree( g_szHotLinkURL );
  901. g_szHotLinkURL = NULL;
  902. }
  903. // Initialize the Device Name Text
  904. SetDlgItemText (hwnd, IDC_VOLUME_MIXER, g_szNoAudioDevice);
  905. EnableWindow (GetDlgItem (hwnd, IDC_VOLUME_MIXER), FALSE);
  906. // Show the default icon window, and hide the custom bitmap window
  907. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_ICON_BRAND), SW_SHOW);
  908. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_BRAND), SW_HIDE);
  909. }
  910. void SetBranding (HWND hwnd, UINT uiMixID)
  911. {
  912. HKEY hkeyBrand = NULL;
  913. MIXERCAPS mc;
  914. if (MMSYSERR_NOERROR != mixerGetDevCaps (uiMixID, &mc, sizeof (mc)))
  915. return; // bail
  916. ResetBranding (hwnd);
  917. // Device Name Text
  918. SetDlgItemText(hwnd, IDC_VOLUME_MIXER, mc.szPname);
  919. // Get Device Bitmap, if any.
  920. hkeyBrand = OpenDeviceBrandRegKey (uiMixID);
  921. if (hkeyBrand)
  922. {
  923. WCHAR szBuffer[MAX_PATH];
  924. DWORD dwType = REG_SZ;
  925. DWORD cb = sizeof (szBuffer);
  926. // Get Any Branding Bitmap
  927. if (ERROR_SUCCESS == RegQueryValueEx (hkeyBrand, REGSTR_VAL_AUDIO_BITMAP, NULL, &dwType, (LPBYTE)szBuffer, &cb))
  928. {
  929. BITMAP bm;
  930. WCHAR* pszComma = wcschr (szBuffer, L',');
  931. if (pszComma)
  932. {
  933. WCHAR* pszResourceID = pszComma + 1;
  934. HANDLE hResource;
  935. // Remove comma delimeter
  936. *pszComma = L'\0';
  937. // Should be a resource module and a resource ID
  938. hResource = LoadLibrary (szBuffer);
  939. if (!hResource)
  940. {
  941. WCHAR szDriversPath[MAX_PATH+1];
  942. szDriversPath[MAX_PATH] = 0;
  943. // If we didn't find it on the normal search path, try looking
  944. // in the "drivers" directory.
  945. if (GetSystemDirectory (szDriversPath, MAX_PATH))
  946. {
  947. wcsncat (szDriversPath, TEXT("\\drivers\\"), MAX_PATH - wcslen(szDriversPath));
  948. wcsncat (szDriversPath, szBuffer, MAX_PATH - wcslen(szDriversPath));
  949. hResource = LoadLibrary (szDriversPath);
  950. }
  951. }
  952. if (hResource)
  953. {
  954. g_hbmBrand = LoadImage (hResource, MAKEINTRESOURCE(_wtoi (pszResourceID)), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
  955. FreeLibrary (hResource);
  956. }
  957. }
  958. else
  959. // Should be an *.bmp file
  960. g_hbmBrand = LoadImage (NULL, szBuffer, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
  961. // Verify that this bitmap is not larger than our defined maximum. Do NOT
  962. // use GetBitmapDimensionEx() here as it not set or used by the system.
  963. if (g_hbmBrand && GetObject (g_hbmBrand, sizeof (BITMAP), &bm))
  964. {
  965. if (bm.bmWidth > ksizeBrandMax.cx ||
  966. bm.bmHeight > ksizeBrandMax.cy)
  967. {
  968. // Too big, we will just show the standard one below
  969. FreeBrandBmp ();
  970. }
  971. }
  972. }
  973. // Get Any Branding URL
  974. // Get the size of the URL
  975. if (ERROR_SUCCESS == RegQueryValueEx (hkeyBrand, REGSTR_VAL_AUDIO_URL, NULL, &dwType, NULL, &cb))
  976. {
  977. // Allocate a buffer to store the URL in, ensuring it is an integer number of WCHARs
  978. g_szHotLinkURL = (WCHAR *)LocalAlloc (LPTR, sizeof(WCHAR) * (cb + (sizeof(WCHAR)-1) / sizeof(WCHAR)));
  979. // Now, get the branding URL
  980. if (ERROR_SUCCESS != RegQueryValueEx (hkeyBrand, REGSTR_VAL_AUDIO_URL, NULL, &dwType, (LPBYTE)g_szHotLinkURL, &cb))
  981. {
  982. // If we failed, free up g_szHotLinkURL
  983. LocalFree( g_szHotLinkURL );
  984. g_szHotLinkURL = NULL;
  985. }
  986. }
  987. // Close the Branding key
  988. RegCloseKey (hkeyBrand);
  989. }
  990. // Apply any bitmap we have now.
  991. if (g_hbmBrand)
  992. {
  993. // Show the custom bitmap window, and hide the default icon window
  994. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_BRAND), SW_SHOW);
  995. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_ICON_BRAND), SW_HIDE);
  996. SendMessage (GetDlgItem (hwnd, IDC_VOLUME_BRAND), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hbmBrand);
  997. }
  998. else
  999. {
  1000. // Show the default icon window, and hide the custom bitmap window
  1001. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_ICON_BRAND), SW_SHOW);
  1002. ShowWindow (GetDlgItem (hwnd, IDC_VOLUME_BRAND), SW_HIDE);
  1003. }
  1004. // Create HotLink text if we have a valid internet address
  1005. CreateHotLink (ValidateURL ());
  1006. }
  1007. BOOL ValidateURL ()
  1008. {
  1009. BOOL fValid = FALSE;
  1010. // Test basic validity
  1011. if (g_szHotLinkURL && (0 < lstrlen (g_szHotLinkURL)))
  1012. {
  1013. // Test URL validity
  1014. if (UrlIsW (g_szHotLinkURL, URLIS_URL))
  1015. {
  1016. WIN32_FIND_DATA fd;
  1017. HANDLE hFile;
  1018. // Make certain that the URL is not a local file!
  1019. hFile = FindFirstFileW (g_szHotLinkURL, &fd);
  1020. if (INVALID_HANDLE_VALUE == hFile)
  1021. fValid = TRUE;
  1022. else
  1023. FindClose (hFile);
  1024. }
  1025. }
  1026. // Clear any bogus info...
  1027. if (!fValid && g_szHotLinkURL)
  1028. {
  1029. LocalFree (g_szHotLinkURL);
  1030. g_szHotLinkURL = NULL;
  1031. }
  1032. return fValid;
  1033. }
  1034. STDAPI_(void) Multichannel (HWND hwnd, UINT uiMixID, DWORD dwDest, DWORD dwVolID)
  1035. {
  1036. PROPSHEETHEADER psh;
  1037. PROPSHEETPAGE psp;
  1038. TCHAR szWindowTitle[255];
  1039. TCHAR szPageTitle[255];
  1040. UINT uiTitle;
  1041. // Save multichannel parameters for the multichannel page
  1042. if (SUCCEEDED (SetDevice (uiMixID, dwDest, dwVolID)))
  1043. {
  1044. // Load Page Title
  1045. LoadString (ghInstance, GetPageStringID (), szPageTitle, sizeof (szPageTitle)/sizeof (TCHAR));
  1046. ZeroMemory (&psp, sizeof (PROPSHEETPAGE));
  1047. psp.dwSize = sizeof (PROPSHEETPAGE);
  1048. psp.dwFlags = PSP_DEFAULT | PSP_USETITLE | PSP_USECALLBACK;
  1049. psp.hInstance = ghInstance;
  1050. psp.pszTemplate = MAKEINTRESOURCE (IDD_MULTICHANNEL);
  1051. psp.pszTitle = szPageTitle;
  1052. psp.pfnDlgProc = MultichannelDlg;
  1053. // Load Window Title (Same as page name now!)
  1054. LoadString (ghInstance, GetPageStringID (), szWindowTitle, sizeof (szWindowTitle)/sizeof (TCHAR));
  1055. ZeroMemory (&psh, sizeof (psh));
  1056. psh.dwSize = sizeof (psh);
  1057. psh.dwFlags = PSH_DEFAULT | PSH_PROPSHEETPAGE;
  1058. psh.hwndParent = hwnd;
  1059. psh.hInstance = ghInstance;
  1060. psh.pszCaption = szWindowTitle;
  1061. psh.nPages = 1;
  1062. psh.nStartPage = 0;
  1063. psh.ppsp = &psp;
  1064. PropertySheet (&psh);
  1065. }
  1066. }
  1067. HKEY OpenDeviceBrandRegKey (UINT uiMixID)
  1068. {
  1069. HKEY hkeyBrand = NULL;
  1070. HKEY hkeyDevice = OpenDeviceRegKey (uiMixID, KEY_READ);
  1071. if (hkeyDevice)
  1072. {
  1073. if (ERROR_SUCCESS != RegOpenKey (hkeyDevice, REGSTR_KEY_BRANDING, &hkeyBrand))
  1074. hkeyBrand = NULL; // Make sure NULL on failure
  1075. // Close the Device key
  1076. RegCloseKey (hkeyDevice);
  1077. }
  1078. return hkeyBrand;
  1079. }
  1080. ///////////////////////////////////////////////////////////////////////////////////////////
  1081. // Microsoft Confidential - DO NOT COPY THIS METHOD INTO ANY APPLICATION, THIS MEANS YOU!!!
  1082. ///////////////////////////////////////////////////////////////////////////////////////////
  1083. PTCHAR GetInterfaceName (DWORD dwMixerID)
  1084. {
  1085. MMRESULT mmr;
  1086. ULONG cbSize=0;
  1087. TCHAR *szInterfaceName=NULL;
  1088. //Query for the Device interface name
  1089. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  1090. if(MMSYSERR_NOERROR == mmr)
  1091. {
  1092. szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  1093. if(!szInterfaceName)
  1094. {
  1095. return NULL;
  1096. }
  1097. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  1098. if(MMSYSERR_NOERROR != mmr)
  1099. {
  1100. GlobalFreePtr(szInterfaceName);
  1101. return NULL;
  1102. }
  1103. }
  1104. return szInterfaceName;
  1105. }
  1106. HKEY OpenDeviceRegKey (UINT uiMixID, REGSAM sam)
  1107. {
  1108. HKEY hkeyDevice = NULL;
  1109. PTCHAR szInterfaceName = GetInterfaceName (uiMixID);
  1110. if (szInterfaceName)
  1111. {
  1112. HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList (NULL, NULL);
  1113. if (INVALID_HANDLE_VALUE != DeviceInfoSet)
  1114. {
  1115. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  1116. DeviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
  1117. if (SetupDiOpenDeviceInterface (DeviceInfoSet, szInterfaceName,
  1118. 0, &DeviceInterfaceData))
  1119. {
  1120. DWORD dwRequiredSize;
  1121. SP_DEVINFO_DATA DeviceInfoData;
  1122. DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
  1123. // Ignore error, it always returns "ERROR_INSUFFICIENT_BUFFER" even though
  1124. // the "SP_DEVICE_INTERFACE_DETAIL_DATA" parameter is supposed to be optional.
  1125. (void) SetupDiGetDeviceInterfaceDetail (DeviceInfoSet, &DeviceInterfaceData,
  1126. NULL, 0, &dwRequiredSize, &DeviceInfoData);
  1127. // Open device reg key
  1128. hkeyDevice = SetupDiOpenDevRegKey (DeviceInfoSet, &DeviceInfoData,
  1129. DICS_FLAG_GLOBAL, 0,
  1130. DIREG_DRV, sam);
  1131. }
  1132. SetupDiDestroyDeviceInfoList (DeviceInfoSet);
  1133. }
  1134. GlobalFreePtr (szInterfaceName);
  1135. }
  1136. return hkeyDevice;
  1137. }