volume.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 23k
Category:

Windows Kernel

Development Platform:

Visual C++

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