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.

816 lines
22 KiB

  1. /*******************************************************************************
  2. *
  3. * (C) COPYRIGHT MICROSOFT CORP., 1993-1994
  4. *
  5. * TITLE: VOLUME.C
  6. *
  7. * VERSION: 1.0
  8. *
  9. * AUTHOR: RAL
  10. *
  11. * DATE: 11/01/94
  12. *
  13. ********************************************************************************
  14. *
  15. * CHANGE LOG:
  16. *
  17. * DATE REV DESCRIPTION
  18. * ----------- --- -------------------------------------------------------------
  19. * Nov. 11, 94 RAL Original
  20. * Oct. 24, 95 Shawnb UNICODE enabled
  21. *
  22. *******************************************************************************/
  23. #include "stdafx.h"
  24. #include "systray.h"
  25. #include <objbase.h>
  26. #include <setupapi.h>
  27. #include <cfgmgr32.h>
  28. #include <initguid.h>
  29. #include <devguid.h>
  30. #include <ks.h>
  31. #include <ksmedia.h>
  32. #include <mmddkp.h>
  33. #define HWAVEOUT_MAPPER ((HWAVEOUT)IntToPtr(WAVE_MAPPER))
  34. #define HWAVEIN_MAPPER ((HWAVEIN)IntToPtr(WAVE_MAPPER))
  35. #define HMIDIOUT_MAPPER ((HMIDIOUT)IntToPtr(WAVE_MAPPER))
  36. #define HWAVEOUT_INDEX(i) ((HWAVEOUT)IntToPtr(i))
  37. #define HWAVEIN_INDEX(i) ((HWAVEIN)IntToPtr(i))
  38. #define HMIDIOUT_INDEX(i) ((HMIDIOUT)IntToPtr(i))
  39. #define HMIXER_INDEX(i) ((HMIXER)IntToPtr(i))
  40. #define HMIXEROBJ_INDEX(i) ((HMIXEROBJ)IntToPtr(i))
  41. /* defined in mmddk.h */
  42. #define DRV_QUERYDEVNODE (DRV_RESERVED + 2)
  43. #define VOLUMEMENU_PROPERTIES 100
  44. #define VOLUMEMENU_SNDVOL 101
  45. extern HINSTANCE g_hInstance;
  46. static BOOL g_bVolumeEnabled = FALSE;
  47. static BOOL g_bVolumeIconShown = FALSE;
  48. static HICON g_hVolumeIcon = NULL;
  49. static HICON g_hMuteIcon = NULL;
  50. static HMENU g_hVolumeMenu = NULL;
  51. static HMIXER g_hMixer = NULL;
  52. static UINT g_uMixer = 0;
  53. static DWORD g_dwMixerDevNode = 0;
  54. static DWORD g_dwMute = (DWORD) -1;
  55. static DWORD g_dwVSlider = 0;
  56. static DWORD g_dwMasterLine = (DWORD) -1;
  57. HDEVNOTIFY DeviceEventContext = NULL;
  58. void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID);
  59. void Volume_DeviceChange_Cleanup(void);
  60. void Volume_UpdateStatus(HWND hWnd, BOOL bShowIcon, BOOL bKillSndVol32);
  61. void Volume_VolumeControl();
  62. void Volume_ControlPanel(HWND hwnd);
  63. MMRESULT Volume_GetDefaultMixerID(int *pid);
  64. void Volume_UpdateIcon(HWND hwnd, DWORD message);
  65. BOOL Volume_Controls(UINT uMxID);
  66. BOOL FileExists (LPCTSTR pszFileName);
  67. BOOL FindSystemFile (LPCTSTR pszFileName, LPTSTR pszFullPath, UINT cchSize);
  68. void Volume_WakeUpOrClose(BOOL fClose);
  69. HMENU Volume_CreateMenu()
  70. {
  71. HMENU hmenu;
  72. LPTSTR lpszMenu1;
  73. LPTSTR lpszMenu2;
  74. lpszMenu1 = LoadDynamicString(IDS_VOLUMEMENU1);
  75. if (!lpszMenu1)
  76. return NULL;
  77. lpszMenu2 = LoadDynamicString(IDS_VOLUMEMENU2);
  78. if (!lpszMenu2)
  79. {
  80. DeleteDynamicString(lpszMenu1);
  81. return NULL;
  82. }
  83. hmenu = CreatePopupMenu();
  84. if (!hmenu)
  85. {
  86. DeleteDynamicString(lpszMenu1);
  87. DeleteDynamicString(lpszMenu2);
  88. return NULL;
  89. }
  90. AppendMenu(hmenu,MF_STRING,VOLUMEMENU_SNDVOL,lpszMenu2);
  91. AppendMenu(hmenu,MF_STRING,VOLUMEMENU_PROPERTIES,lpszMenu1);
  92. SetMenuDefaultItem(hmenu,VOLUMEMENU_SNDVOL,FALSE);
  93. DeleteDynamicString(lpszMenu1);
  94. DeleteDynamicString(lpszMenu2);
  95. return hmenu;
  96. }
  97. BOOL Volume_Init(HWND hWnd)
  98. {
  99. UINT uMxID;
  100. const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
  101. if (g_hMixer == NULL)
  102. {
  103. if (Volume_GetDefaultMixerID(&uMxID) != MMSYSERR_NOERROR)
  104. return FALSE;
  105. //
  106. // check for sndvol32 existence. checking for the .exe
  107. // first will ensure that the service gets disabled properly
  108. //
  109. if (! FindSystemFile (szVolApp, NULL, 0))
  110. {
  111. //
  112. // disable the volume service
  113. //
  114. EnableService (STSERVICE_VOLUME, FALSE);
  115. return FALSE;
  116. }
  117. //
  118. // do we have output volume controls on this mixer?
  119. //
  120. if (! Volume_Controls(uMxID))
  121. return FALSE;
  122. if (mixerOpen(&g_hMixer, uMxID, (DWORD_PTR)hWnd, 0
  123. , CALLBACK_WINDOW | MIXER_OBJECTF_MIXER)
  124. == MMSYSERR_NOERROR)
  125. {
  126. Volume_DeviceChange_Init(hWnd, uMxID);
  127. g_uMixer = uMxID;
  128. if (mixerMessage (HMIXER_INDEX(uMxID), DRV_QUERYDEVNODE
  129. , (DWORD_PTR)&g_dwMixerDevNode, 0L))
  130. g_dwMixerDevNode = 0L;
  131. return TRUE;
  132. }
  133. }
  134. else
  135. return TRUE;
  136. return FALSE;
  137. }
  138. //
  139. // Called at init time and whenever services are enabled/disabled.
  140. // Returns false if mixer services are not active.
  141. //
  142. BOOL Volume_CheckEnable(HWND hWnd, BOOL bSvcEnabled)
  143. {
  144. BOOL bEnable = bSvcEnabled && Volume_Init(hWnd);
  145. if (bEnable != g_bVolumeEnabled) {
  146. //
  147. // state change
  148. //
  149. g_bVolumeEnabled = bEnable;
  150. Volume_UpdateStatus(hWnd, bEnable, TRUE);
  151. }
  152. return(bEnable);
  153. }
  154. void Volume_UpdateStatus(HWND hWnd, BOOL bShowIcon, BOOL bKillSndVol32)
  155. {
  156. // Don't show icon if not enabled
  157. if (!g_bVolumeEnabled)
  158. bShowIcon = FALSE;
  159. if (bShowIcon != g_bVolumeIconShown) {
  160. g_bVolumeIconShown = bShowIcon;
  161. if (bShowIcon) {
  162. g_hVolumeIcon = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_VOLUME),
  163. IMAGE_ICON, 16, 16, 0);
  164. g_hMuteIcon = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_MUTE),
  165. IMAGE_ICON, 16, 16, 0);
  166. Volume_UpdateIcon(hWnd, NIM_ADD);
  167. } else {
  168. SysTray_NotifyIcon(hWnd, STWM_NOTIFYVOLUME, NIM_DELETE, NULL, NULL);
  169. if (g_hVolumeIcon) {
  170. DestroyIcon(g_hVolumeIcon);
  171. g_hVolumeIcon = NULL;
  172. }
  173. if (g_hMuteIcon) {
  174. DestroyIcon(g_hMuteIcon);
  175. g_hMuteIcon = NULL;
  176. }
  177. if (g_hMixer)
  178. {
  179. mixerClose(g_hMixer);
  180. g_hMixer = NULL;
  181. }
  182. g_uMixer = 0;
  183. g_dwMixerDevNode = 0L;
  184. //
  185. // SNDVOL32 may have a TRAYMASTER window open,
  186. // sitting on a timer before it closes (so multiple
  187. // l-clicks on the tray icon can bring up the app
  188. // quickly after the first hit). Close that app
  189. // if it's around.
  190. //
  191. if (bKillSndVol32)
  192. {
  193. Volume_WakeUpOrClose (TRUE);
  194. }
  195. }
  196. }
  197. }
  198. const TCHAR szMapperPath[] = TEXT ("Software\\Microsoft\\Multimedia\\Sound Mapper");
  199. const TCHAR szPlayback[] = TEXT ("Playback");
  200. const TCHAR szPreferredOnly[] = TEXT ("PreferredOnly");
  201. /*
  202. * Volume_GetDefaultMixerID
  203. *
  204. * Get the default mixer id. We only appear if there is a mixer associated
  205. * with the default wave.
  206. *
  207. */
  208. MMRESULT Volume_GetDefaultMixerID(int *pid)
  209. {
  210. MMRESULT mmr;
  211. DWORD dwWaveID;
  212. DWORD dwMixID;
  213. DWORD dwFlags = 0;
  214. mmr = waveOutMessage(HWAVEOUT_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &dwWaveID, (DWORD_PTR) &dwFlags);
  215. if (mmr == MMSYSERR_NOERROR)
  216. {
  217. mmr = mixerGetID(HMIXEROBJ_INDEX(dwWaveID), &dwMixID, MIXER_OBJECTF_WAVEOUT);
  218. if (mmr == MMSYSERR_NOERROR && pid)
  219. {
  220. *pid = dwMixID;
  221. }
  222. }
  223. return mmr;
  224. }
  225. /*
  226. * Process line changes
  227. */
  228. void Volume_LineChange(
  229. HWND hwnd,
  230. HMIXER hmx,
  231. DWORD dwLineID)
  232. {
  233. if (dwLineID != g_dwMasterLine)
  234. return;
  235. //
  236. // if our line is disabled, go away, I guess
  237. //
  238. }
  239. /*
  240. * Process control changes
  241. */
  242. void Volume_ControlChange(
  243. HWND hwnd,
  244. HMIXER hmx,
  245. DWORD dwControlID)
  246. {
  247. if ((dwControlID != g_dwMute) && (g_dwMute != (DWORD) -1))
  248. return;
  249. //
  250. // Change mute icon state
  251. //
  252. Volume_UpdateIcon(hwnd, NIM_MODIFY);
  253. }
  254. BOOL Volume_IsMute()
  255. {
  256. MMRESULT mmr;
  257. MIXERCONTROLDETAILS mxcd;
  258. BOOL fMute;
  259. if (!g_hMixer && (g_dwMute != (DWORD) -1))
  260. {
  261. return FALSE;
  262. }
  263. mxcd.cbStruct = sizeof(mxcd);
  264. mxcd.dwControlID = g_dwMute;
  265. mxcd.cChannels = 1;
  266. mxcd.cMultipleItems = 0;
  267. mxcd.cbDetails = sizeof(DWORD);
  268. mxcd.paDetails = (LPVOID)&fMute;
  269. mmr = mixerGetControlDetails( (HMIXEROBJ)g_hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE);
  270. if (mmr == MMSYSERR_NOERROR)
  271. {
  272. return fMute;
  273. }
  274. return FALSE;
  275. }
  276. BOOL Volume_Controls(
  277. UINT uMxID)
  278. {
  279. MIXERLINECONTROLS mxlc;
  280. MIXERCONTROL mxctrl;
  281. MIXERCAPS mxcaps;
  282. MMRESULT mmr;
  283. BOOL fResult = FALSE;
  284. DWORD iDest;
  285. g_dwMasterLine = (DWORD) -1;
  286. g_dwMute = (DWORD) -1;
  287. mmr = mixerGetDevCaps(uMxID, &mxcaps, sizeof(mxcaps));
  288. if (mmr != MMSYSERR_NOERROR)
  289. {
  290. return FALSE;
  291. }
  292. for (iDest = 0; iDest < mxcaps.cDestinations; iDest++)
  293. {
  294. MIXERLINE mlDst;
  295. mlDst.cbStruct = sizeof ( mlDst );
  296. mlDst.dwDestination = iDest;
  297. mmr = mixerGetLineInfo( HMIXEROBJ_INDEX(uMxID), &mlDst, MIXER_GETLINEINFOF_DESTINATION);
  298. if (mmr != MMSYSERR_NOERROR)
  299. {
  300. continue;
  301. }
  302. switch (mlDst.dwComponentType)
  303. {
  304. default:
  305. continue;
  306. case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
  307. case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
  308. {
  309. g_dwMasterLine = mlDst.dwLineID;
  310. }
  311. break;
  312. }
  313. mxlc.cbStruct = sizeof(mxlc);
  314. mxlc.dwLineID = g_dwMasterLine;
  315. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  316. mxlc.cControls = 1;
  317. mxlc.cbmxctrl = sizeof(mxctrl);
  318. mxlc.pamxctrl = &mxctrl;
  319. mmr = mixerGetLineControls( HMIXEROBJ_INDEX(uMxID), &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
  320. if (mmr == MMSYSERR_NOERROR)
  321. {
  322. g_dwMute = mxctrl.dwControlID;
  323. }
  324. fResult = TRUE;
  325. break;
  326. }
  327. return fResult;
  328. }
  329. void Volume_UpdateIcon(
  330. HWND hWnd,
  331. DWORD message)
  332. {
  333. BOOL fMute;
  334. LPTSTR lpsz;
  335. HICON hVol;
  336. fMute = Volume_IsMute();
  337. hVol = fMute?g_hMuteIcon:g_hVolumeIcon;
  338. lpsz = LoadDynamicString(fMute?IDS_MUTED:IDS_VOLUME);
  339. SysTray_NotifyIcon(hWnd, STWM_NOTIFYVOLUME, message, hVol, lpsz);
  340. DeleteDynamicString(lpsz);
  341. }
  342. // WinMM is telling us the preferred device has changed for some reason
  343. // Dump the old, open the new
  344. //
  345. void Volume_WinMMDeviceChange(HWND hWnd)
  346. {
  347. DWORD dwMixID;
  348. if (g_hMixer) // Dumping the Old
  349. {
  350. mixerClose(g_hMixer);
  351. g_hMixer = NULL;
  352. g_uMixer = 0;
  353. g_dwMixerDevNode = 0L;
  354. }
  355. // Opening the new
  356. if (Volume_GetDefaultMixerID(&dwMixID) == MMSYSERR_NOERROR)
  357. {
  358. if ( Volume_Controls(dwMixID) &&
  359. (mixerOpen(&g_hMixer, dwMixID, (DWORD_PTR)hWnd, 0L, CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR))
  360. {
  361. Volume_UpdateStatus(hWnd, TRUE, TRUE);
  362. if (mixerMessage (HMIXER_INDEX(dwMixID), DRV_QUERYDEVNODE, (DWORD_PTR)&g_dwMixerDevNode, 0L))
  363. {
  364. g_dwMixerDevNode = 0L;
  365. }
  366. g_uMixer = dwMixID;
  367. Volume_UpdateIcon(hWnd, NIM_MODIFY);
  368. }
  369. else
  370. {
  371. Volume_UpdateStatus(hWnd, FALSE, TRUE);
  372. }
  373. }
  374. else
  375. {
  376. Volume_UpdateStatus(hWnd, FALSE, TRUE);
  377. }
  378. }
  379. // Need to free up in the event of a power broadcast as well
  380. //
  381. void Volume_HandlePowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam)
  382. {
  383. switch (wParam)
  384. {
  385. case PBT_APMQUERYSUSPEND:
  386. {
  387. if (g_hMixer) // Dumping the Old
  388. {
  389. mixerClose(g_hMixer);
  390. g_hMixer = NULL;
  391. g_uMixer = 0;
  392. g_dwMixerDevNode = 0L;
  393. }
  394. }
  395. break;
  396. case PBT_APMQUERYSUSPENDFAILED:
  397. case PBT_APMRESUMESUSPEND:
  398. {
  399. Volume_WinMMDeviceChange(hWnd);
  400. }
  401. break;
  402. }
  403. }
  404. void Volume_DeviceChange_Cleanup()
  405. {
  406. if (DeviceEventContext)
  407. {
  408. UnregisterDeviceNotification(DeviceEventContext);
  409. DeviceEventContext = 0;
  410. }
  411. return;
  412. }
  413. /*
  414. **************************************************************************************************
  415. Volume_GetDeviceHandle()
  416. given a mixerID this functions opens its corresponding device handle. This handle can be used
  417. to register for DeviceNotifications.
  418. dwMixerID -- The mixer ID
  419. phDevice -- a pointer to a handle. This pointer will hold the handle value if the function is
  420. successful
  421. return values -- If the handle could be obtained successfully the return vlaue is TRUE.
  422. **************************************************************************************************
  423. */
  424. BOOL Volume_GetDeviceHandle(DWORD dwMixerID, HANDLE *phDevice)
  425. {
  426. MMRESULT mmr;
  427. ULONG cbSize=0;
  428. TCHAR *szInterfaceName=NULL;
  429. //Query for the Device interface name
  430. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  431. if(MMSYSERR_NOERROR == mmr)
  432. {
  433. szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  434. if(!szInterfaceName)
  435. {
  436. return FALSE;
  437. }
  438. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  439. if(MMSYSERR_NOERROR != mmr)
  440. {
  441. GlobalFreePtr(szInterfaceName);
  442. return FALSE;
  443. }
  444. }
  445. else
  446. {
  447. return FALSE;
  448. }
  449. //Get an handle on the device interface name.
  450. *phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  451. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  452. GlobalFreePtr(szInterfaceName);
  453. if(INVALID_HANDLE_VALUE == *phDevice)
  454. {
  455. return FALSE;
  456. }
  457. return TRUE;
  458. }
  459. /* DeviceChange_Init()
  460. * First time initialization for WM_DEVICECHANGE messages
  461. *
  462. * On NT 5.0, you have to register for device notification
  463. */
  464. void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
  465. {
  466. DEV_BROADCAST_HANDLE DevBrodHandle;
  467. HANDLE hMixerDevice=NULL;
  468. //If we had registered already for device notifications, unregister ourselves.
  469. Volume_DeviceChange_Cleanup();
  470. //If we get the device handle register for device notifications on it.
  471. if(Volume_GetDeviceHandle(dwMixerID, &hMixerDevice))
  472. {
  473. memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
  474. DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  475. DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  476. DevBrodHandle.dbch_handle = hMixerDevice;
  477. DeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  478. if(hMixerDevice)
  479. {
  480. CloseHandle(hMixerDevice);
  481. hMixerDevice = NULL;
  482. }
  483. }
  484. }
  485. // Watch for PNP events to free up the open handle when needed
  486. // We will assume any changes will now generate a WINMM_DEVICECHANGED message from WinMM
  487. // except for the QUERYREMOVEFAILED case, in this case we will just re-aquire the preferred mixer
  488. //
  489. void Volume_DeviceChange(HWND hWnd,WPARAM wParam,LPARAM lParam)
  490. {
  491. PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
  492. //If we have an handle on the device then we get a DEV_BROADCAST_HDR structure as the lParam.
  493. if(!DeviceEventContext || !bh || (bh->dbch_devicetype != DBT_DEVTYP_HANDLE))
  494. {
  495. return;
  496. }
  497. switch (wParam)
  498. {
  499. case DBT_DEVICEQUERYREMOVE: // Someone wants to remove this device, let's let them.
  500. {
  501. if (g_hMixer)
  502. {
  503. mixerClose(g_hMixer);
  504. g_hMixer = NULL;
  505. g_uMixer = 0;
  506. g_dwMixerDevNode = 0L;
  507. }
  508. }
  509. break;
  510. case DBT_DEVICEQUERYREMOVEFAILED: // The query failed, the device will not be removed, so lets reopen it.
  511. {
  512. Volume_WinMMDeviceChange(hWnd); // Lets just use this function to do it.
  513. }
  514. break;
  515. }
  516. }
  517. void Volume_WmDestroy(
  518. HWND hDlg
  519. )
  520. {
  521. Volume_DeviceChange_Cleanup();
  522. }
  523. void Volume_Shutdown(
  524. HWND hWnd)
  525. {
  526. Volume_UpdateStatus(hWnd, FALSE, FALSE);
  527. }
  528. void Volume_Menu(HWND hwnd, UINT uMenuNum, UINT uButton)
  529. {
  530. POINT pt;
  531. UINT iCmd;
  532. HMENU hmenu;
  533. GetCursorPos(&pt);
  534. hmenu = Volume_CreateMenu();
  535. if (!hmenu)
  536. return;
  537. SetForegroundWindow(hwnd);
  538. iCmd = TrackPopupMenu(hmenu, uButton | TPM_RETURNCMD | TPM_NONOTIFY,
  539. pt.x, pt.y, 0, hwnd, NULL);
  540. DestroyMenu(hmenu);
  541. switch (iCmd) {
  542. case VOLUMEMENU_PROPERTIES:
  543. Volume_ControlPanel(hwnd);
  544. break;
  545. case VOLUMEMENU_SNDVOL:
  546. Volume_VolumeControl();
  547. break;
  548. }
  549. SetIconFocus(hwnd, STWM_NOTIFYVOLUME);
  550. }
  551. void Volume_Notify(HWND hwnd, WPARAM wParam, LPARAM lParam)
  552. {
  553. switch (lParam)
  554. {
  555. case WM_RBUTTONUP:
  556. Volume_Menu(hwnd, 1, TPM_RIGHTBUTTON);
  557. break;
  558. case WM_LBUTTONDOWN:
  559. SetTimer(hwnd, VOLUME_TIMER_ID, GetDoubleClickTime()+100, NULL);
  560. break;
  561. case WM_LBUTTONDBLCLK:
  562. KillTimer(hwnd, VOLUME_TIMER_ID);
  563. Volume_VolumeControl();
  564. break;
  565. }
  566. }
  567. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  568. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  569. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  570. /*
  571. * MYWM_WAKEUP and the "Tray Volume" window are defined by the SNDVOL32.EXE
  572. * application. Changing these values or changing the values in SNDVOL32.EXE
  573. * without mirroring them here will break the tray volume dialog.
  574. */
  575. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  576. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  577. /* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
  578. #define MYWM_WAKEUP (WM_APP+100+6)
  579. void Volume_Timer(HWND hwnd)
  580. {
  581. KillTimer(hwnd, VOLUME_TIMER_ID);
  582. Volume_WakeUpOrClose (FALSE);
  583. }
  584. void Volume_WakeUpOrClose(BOOL fClose)
  585. {
  586. const TCHAR szVolWindow [] = TEXT ("Tray Volume");
  587. HWND hApp;
  588. if (hApp = FindWindow(szVolWindow, NULL))
  589. {
  590. SendMessage(hApp, MYWM_WAKEUP, (WPARAM)fClose, 0);
  591. }
  592. else if (!fClose)
  593. {
  594. const TCHAR szOpen[] = TEXT ("open");
  595. const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
  596. const TCHAR szParamsWakeup[] = TEXT ("/t");
  597. ShellExecute (NULL, szOpen, szVolApp, szParamsWakeup, NULL, SW_SHOWNORMAL);
  598. }
  599. }
  600. /*
  601. * Volume_ControlPanel
  602. *
  603. * Launch "Audio" control panel/property sheet upon request.
  604. *
  605. * */
  606. void Volume_ControlPanel(HWND hwnd)
  607. {
  608. const TCHAR szOpen[] = TEXT ("open");
  609. const TCHAR szRunDLL[] = TEXT ("RUNDLL32.EXE");
  610. const TCHAR szParams[] = TEXT ("MMSYS.CPL,ShowFullControlPanel");
  611. ShellExecute(NULL, szOpen, szRunDLL, szParams, NULL, SW_SHOWNORMAL);
  612. }
  613. /*
  614. * Volume_VolumeControl
  615. *
  616. * Launch Volume Control App
  617. *
  618. * */
  619. void Volume_VolumeControl()
  620. {
  621. const TCHAR szOpen[] = TEXT ("open");
  622. const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
  623. ShellExecute(NULL, szOpen, szVolApp, NULL, NULL, SW_SHOWNORMAL);
  624. }
  625. /*
  626. * FileExists
  627. *
  628. * Does a file exist
  629. *
  630. * */
  631. BOOL FileExists(LPCTSTR pszPath)
  632. {
  633. return (GetFileAttributes(pszPath) != (DWORD)-1);
  634. } // End FileExists
  635. /*
  636. * FindSystemFile
  637. *
  638. * Finds full path to specified file
  639. *
  640. * */
  641. BOOL FindSystemFile(LPCTSTR pszFileName, LPTSTR pszFullPath, UINT cchSize)
  642. {
  643. TCHAR szPath[MAX_PATH];
  644. LPTSTR pszName;
  645. DWORD cchLen;
  646. if ((pszFileName == NULL) || (pszFileName[0] == 0))
  647. return FALSE;
  648. cchLen = SearchPath(NULL, pszFileName, NULL, MAX_PATH,
  649. szPath,&pszName);
  650. if (cchLen == 0)
  651. return FALSE;
  652. if (cchLen >= MAX_PATH)
  653. cchLen = MAX_PATH - 1;
  654. if (! FileExists (szPath))
  655. return FALSE;
  656. if ((pszFullPath == NULL) || (cchSize == 0))
  657. return TRUE;
  658. // Copy full path into buffer
  659. if (cchLen >= cchSize)
  660. cchLen = cchSize - 1;
  661. lstrcpyn (pszFullPath, szPath, cchLen);
  662. pszFullPath[cchLen] = 0;
  663. return TRUE;
  664. } // End FindSystemFile