mirror of https://github.com/tongzx/nt5src
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
816 lines
22 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 1993-1994
|
|
*
|
|
* TITLE: VOLUME.C
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* AUTHOR: RAL
|
|
*
|
|
* DATE: 11/01/94
|
|
*
|
|
********************************************************************************
|
|
*
|
|
* CHANGE LOG:
|
|
*
|
|
* DATE REV DESCRIPTION
|
|
* ----------- --- -------------------------------------------------------------
|
|
* Nov. 11, 94 RAL Original
|
|
* Oct. 24, 95 Shawnb UNICODE enabled
|
|
*
|
|
*******************************************************************************/
|
|
#include "stdafx.h"
|
|
#include "systray.h"
|
|
|
|
#include <objbase.h>
|
|
#include <setupapi.h>
|
|
#include <cfgmgr32.h>
|
|
#include <initguid.h>
|
|
#include <devguid.h>
|
|
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
#include <mmddkp.h>
|
|
|
|
|
|
#define HWAVEOUT_MAPPER ((HWAVEOUT)IntToPtr(WAVE_MAPPER))
|
|
#define HWAVEIN_MAPPER ((HWAVEIN)IntToPtr(WAVE_MAPPER))
|
|
#define HMIDIOUT_MAPPER ((HMIDIOUT)IntToPtr(WAVE_MAPPER))
|
|
|
|
#define HWAVEOUT_INDEX(i) ((HWAVEOUT)IntToPtr(i))
|
|
#define HWAVEIN_INDEX(i) ((HWAVEIN)IntToPtr(i))
|
|
#define HMIDIOUT_INDEX(i) ((HMIDIOUT)IntToPtr(i))
|
|
#define HMIXER_INDEX(i) ((HMIXER)IntToPtr(i))
|
|
#define HMIXEROBJ_INDEX(i) ((HMIXEROBJ)IntToPtr(i))
|
|
|
|
/* defined in mmddk.h */
|
|
#define DRV_QUERYDEVNODE (DRV_RESERVED + 2)
|
|
|
|
#define VOLUMEMENU_PROPERTIES 100
|
|
#define VOLUMEMENU_SNDVOL 101
|
|
|
|
extern HINSTANCE g_hInstance;
|
|
|
|
static BOOL g_bVolumeEnabled = FALSE;
|
|
static BOOL g_bVolumeIconShown = FALSE;
|
|
static HICON g_hVolumeIcon = NULL;
|
|
static HICON g_hMuteIcon = NULL;
|
|
static HMENU g_hVolumeMenu = NULL;
|
|
static HMIXER g_hMixer = NULL;
|
|
static UINT g_uMixer = 0;
|
|
static DWORD g_dwMixerDevNode = 0;
|
|
static DWORD g_dwMute = (DWORD) -1;
|
|
static DWORD g_dwVSlider = 0;
|
|
static DWORD g_dwMasterLine = (DWORD) -1;
|
|
|
|
HDEVNOTIFY DeviceEventContext = NULL;
|
|
|
|
|
|
void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID);
|
|
void Volume_DeviceChange_Cleanup(void);
|
|
|
|
void Volume_UpdateStatus(HWND hWnd, BOOL bShowIcon, BOOL bKillSndVol32);
|
|
void Volume_VolumeControl();
|
|
void Volume_ControlPanel(HWND hwnd);
|
|
MMRESULT Volume_GetDefaultMixerID(int *pid);
|
|
void Volume_UpdateIcon(HWND hwnd, DWORD message);
|
|
BOOL Volume_Controls(UINT uMxID);
|
|
BOOL FileExists (LPCTSTR pszFileName);
|
|
BOOL FindSystemFile (LPCTSTR pszFileName, LPTSTR pszFullPath, UINT cchSize);
|
|
void Volume_WakeUpOrClose(BOOL fClose);
|
|
|
|
HMENU Volume_CreateMenu()
|
|
{
|
|
HMENU hmenu;
|
|
LPTSTR lpszMenu1;
|
|
LPTSTR lpszMenu2;
|
|
|
|
lpszMenu1 = LoadDynamicString(IDS_VOLUMEMENU1);
|
|
if (!lpszMenu1)
|
|
return NULL;
|
|
|
|
lpszMenu2 = LoadDynamicString(IDS_VOLUMEMENU2);
|
|
if (!lpszMenu2)
|
|
{
|
|
DeleteDynamicString(lpszMenu1);
|
|
return NULL;
|
|
}
|
|
|
|
hmenu = CreatePopupMenu();
|
|
if (!hmenu)
|
|
{
|
|
DeleteDynamicString(lpszMenu1);
|
|
DeleteDynamicString(lpszMenu2);
|
|
return NULL;
|
|
}
|
|
|
|
AppendMenu(hmenu,MF_STRING,VOLUMEMENU_SNDVOL,lpszMenu2);
|
|
AppendMenu(hmenu,MF_STRING,VOLUMEMENU_PROPERTIES,lpszMenu1);
|
|
|
|
SetMenuDefaultItem(hmenu,VOLUMEMENU_SNDVOL,FALSE);
|
|
|
|
DeleteDynamicString(lpszMenu1);
|
|
DeleteDynamicString(lpszMenu2);
|
|
|
|
return hmenu;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL Volume_Init(HWND hWnd)
|
|
{
|
|
UINT uMxID;
|
|
const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
|
|
|
|
if (g_hMixer == NULL)
|
|
{
|
|
if (Volume_GetDefaultMixerID(&uMxID) != MMSYSERR_NOERROR)
|
|
return FALSE;
|
|
|
|
//
|
|
// check for sndvol32 existence. checking for the .exe
|
|
// first will ensure that the service gets disabled properly
|
|
//
|
|
|
|
if (! FindSystemFile (szVolApp, NULL, 0))
|
|
{
|
|
//
|
|
// disable the volume service
|
|
//
|
|
EnableService (STSERVICE_VOLUME, FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// do we have output volume controls on this mixer?
|
|
//
|
|
if (! Volume_Controls(uMxID))
|
|
return FALSE;
|
|
|
|
if (mixerOpen(&g_hMixer, uMxID, (DWORD_PTR)hWnd, 0
|
|
, CALLBACK_WINDOW | MIXER_OBJECTF_MIXER)
|
|
== MMSYSERR_NOERROR)
|
|
{
|
|
Volume_DeviceChange_Init(hWnd, uMxID);
|
|
|
|
g_uMixer = uMxID;
|
|
if (mixerMessage (HMIXER_INDEX(uMxID), DRV_QUERYDEVNODE
|
|
, (DWORD_PTR)&g_dwMixerDevNode, 0L))
|
|
g_dwMixerDevNode = 0L;
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Called at init time and whenever services are enabled/disabled.
|
|
// Returns false if mixer services are not active.
|
|
//
|
|
BOOL Volume_CheckEnable(HWND hWnd, BOOL bSvcEnabled)
|
|
{
|
|
BOOL bEnable = bSvcEnabled && Volume_Init(hWnd);
|
|
|
|
if (bEnable != g_bVolumeEnabled) {
|
|
//
|
|
// state change
|
|
//
|
|
g_bVolumeEnabled = bEnable;
|
|
Volume_UpdateStatus(hWnd, bEnable, TRUE);
|
|
}
|
|
return(bEnable);
|
|
}
|
|
|
|
void Volume_UpdateStatus(HWND hWnd, BOOL bShowIcon, BOOL bKillSndVol32)
|
|
{
|
|
// Don't show icon if not enabled
|
|
if (!g_bVolumeEnabled)
|
|
bShowIcon = FALSE;
|
|
|
|
if (bShowIcon != g_bVolumeIconShown) {
|
|
g_bVolumeIconShown = bShowIcon;
|
|
if (bShowIcon) {
|
|
g_hVolumeIcon = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_VOLUME),
|
|
IMAGE_ICON, 16, 16, 0);
|
|
g_hMuteIcon = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_MUTE),
|
|
IMAGE_ICON, 16, 16, 0);
|
|
Volume_UpdateIcon(hWnd, NIM_ADD);
|
|
} else {
|
|
SysTray_NotifyIcon(hWnd, STWM_NOTIFYVOLUME, NIM_DELETE, NULL, NULL);
|
|
if (g_hVolumeIcon) {
|
|
DestroyIcon(g_hVolumeIcon);
|
|
g_hVolumeIcon = NULL;
|
|
}
|
|
if (g_hMuteIcon) {
|
|
DestroyIcon(g_hMuteIcon);
|
|
g_hMuteIcon = NULL;
|
|
}
|
|
if (g_hMixer)
|
|
{
|
|
mixerClose(g_hMixer);
|
|
g_hMixer = NULL;
|
|
}
|
|
g_uMixer = 0;
|
|
g_dwMixerDevNode = 0L;
|
|
|
|
//
|
|
// SNDVOL32 may have a TRAYMASTER window open,
|
|
// sitting on a timer before it closes (so multiple
|
|
// l-clicks on the tray icon can bring up the app
|
|
// quickly after the first hit). Close that app
|
|
// if it's around.
|
|
//
|
|
if (bKillSndVol32)
|
|
{
|
|
Volume_WakeUpOrClose (TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const TCHAR szMapperPath[] = TEXT ("Software\\Microsoft\\Multimedia\\Sound Mapper");
|
|
const TCHAR szPlayback[] = TEXT ("Playback");
|
|
const TCHAR szPreferredOnly[] = TEXT ("PreferredOnly");
|
|
|
|
|
|
/*
|
|
* Volume_GetDefaultMixerID
|
|
*
|
|
* Get the default mixer id. We only appear if there is a mixer associated
|
|
* with the default wave.
|
|
*
|
|
*/
|
|
MMRESULT Volume_GetDefaultMixerID(int *pid)
|
|
{
|
|
MMRESULT mmr;
|
|
DWORD dwWaveID;
|
|
DWORD dwMixID;
|
|
DWORD dwFlags = 0;
|
|
|
|
mmr = waveOutMessage(HWAVEOUT_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &dwWaveID, (DWORD_PTR) &dwFlags);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
mmr = mixerGetID(HMIXEROBJ_INDEX(dwWaveID), &dwMixID, MIXER_OBJECTF_WAVEOUT);
|
|
|
|
if (mmr == MMSYSERR_NOERROR && pid)
|
|
{
|
|
*pid = dwMixID;
|
|
}
|
|
}
|
|
|
|
return mmr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process line changes
|
|
*/
|
|
void Volume_LineChange(
|
|
HWND hwnd,
|
|
HMIXER hmx,
|
|
DWORD dwLineID)
|
|
{
|
|
if (dwLineID != g_dwMasterLine)
|
|
return;
|
|
//
|
|
// if our line is disabled, go away, I guess
|
|
//
|
|
}
|
|
|
|
/*
|
|
* Process control changes
|
|
*/
|
|
void Volume_ControlChange(
|
|
HWND hwnd,
|
|
HMIXER hmx,
|
|
DWORD dwControlID)
|
|
{
|
|
if ((dwControlID != g_dwMute) && (g_dwMute != (DWORD) -1))
|
|
return;
|
|
|
|
//
|
|
// Change mute icon state
|
|
//
|
|
Volume_UpdateIcon(hwnd, NIM_MODIFY);
|
|
}
|
|
|
|
|
|
BOOL Volume_IsMute()
|
|
{
|
|
MMRESULT mmr;
|
|
MIXERCONTROLDETAILS mxcd;
|
|
BOOL fMute;
|
|
|
|
if (!g_hMixer && (g_dwMute != (DWORD) -1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
mxcd.cbStruct = sizeof(mxcd);
|
|
mxcd.dwControlID = g_dwMute;
|
|
mxcd.cChannels = 1;
|
|
mxcd.cMultipleItems = 0;
|
|
mxcd.cbDetails = sizeof(DWORD);
|
|
mxcd.paDetails = (LPVOID)&fMute;
|
|
|
|
mmr = mixerGetControlDetails( (HMIXEROBJ)g_hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
return fMute;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL Volume_Controls(
|
|
UINT uMxID)
|
|
{
|
|
MIXERLINECONTROLS mxlc;
|
|
MIXERCONTROL mxctrl;
|
|
MIXERCAPS mxcaps;
|
|
MMRESULT mmr;
|
|
BOOL fResult = FALSE;
|
|
DWORD iDest;
|
|
g_dwMasterLine = (DWORD) -1;
|
|
g_dwMute = (DWORD) -1;
|
|
|
|
mmr = mixerGetDevCaps(uMxID, &mxcaps, sizeof(mxcaps));
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (iDest = 0; iDest < mxcaps.cDestinations; iDest++)
|
|
{
|
|
MIXERLINE mlDst;
|
|
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwDestination = iDest;
|
|
|
|
mmr = mixerGetLineInfo( HMIXEROBJ_INDEX(uMxID), &mlDst, MIXER_GETLINEINFOF_DESTINATION);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (mlDst.dwComponentType)
|
|
{
|
|
default:
|
|
continue;
|
|
|
|
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
|
|
case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
|
|
{
|
|
g_dwMasterLine = mlDst.dwLineID;
|
|
}
|
|
break;
|
|
}
|
|
|
|
mxlc.cbStruct = sizeof(mxlc);
|
|
mxlc.dwLineID = g_dwMasterLine;
|
|
mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
|
|
mxlc.cControls = 1;
|
|
mxlc.cbmxctrl = sizeof(mxctrl);
|
|
mxlc.pamxctrl = &mxctrl;
|
|
|
|
mmr = mixerGetLineControls( HMIXEROBJ_INDEX(uMxID), &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
g_dwMute = mxctrl.dwControlID;
|
|
}
|
|
|
|
fResult = TRUE;
|
|
break;
|
|
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
void Volume_UpdateIcon(
|
|
HWND hWnd,
|
|
DWORD message)
|
|
{
|
|
BOOL fMute;
|
|
LPTSTR lpsz;
|
|
HICON hVol;
|
|
|
|
fMute = Volume_IsMute();
|
|
hVol = fMute?g_hMuteIcon:g_hVolumeIcon;
|
|
lpsz = LoadDynamicString(fMute?IDS_MUTED:IDS_VOLUME);
|
|
SysTray_NotifyIcon(hWnd, STWM_NOTIFYVOLUME, message, hVol, lpsz);
|
|
DeleteDynamicString(lpsz);
|
|
}
|
|
|
|
|
|
|
|
// WinMM is telling us the preferred device has changed for some reason
|
|
// Dump the old, open the new
|
|
//
|
|
void Volume_WinMMDeviceChange(HWND hWnd)
|
|
{
|
|
DWORD dwMixID;
|
|
|
|
if (g_hMixer) // Dumping the Old
|
|
{
|
|
mixerClose(g_hMixer);
|
|
g_hMixer = NULL;
|
|
g_uMixer = 0;
|
|
g_dwMixerDevNode = 0L;
|
|
}
|
|
// Opening the new
|
|
if (Volume_GetDefaultMixerID(&dwMixID) == MMSYSERR_NOERROR)
|
|
{
|
|
if ( Volume_Controls(dwMixID) &&
|
|
(mixerOpen(&g_hMixer, dwMixID, (DWORD_PTR)hWnd, 0L, CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR))
|
|
{
|
|
Volume_UpdateStatus(hWnd, TRUE, TRUE);
|
|
|
|
if (mixerMessage (HMIXER_INDEX(dwMixID), DRV_QUERYDEVNODE, (DWORD_PTR)&g_dwMixerDevNode, 0L))
|
|
{
|
|
g_dwMixerDevNode = 0L;
|
|
}
|
|
|
|
g_uMixer = dwMixID;
|
|
|
|
Volume_UpdateIcon(hWnd, NIM_MODIFY);
|
|
}
|
|
else
|
|
{
|
|
Volume_UpdateStatus(hWnd, FALSE, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Volume_UpdateStatus(hWnd, FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
// Need to free up in the event of a power broadcast as well
|
|
//
|
|
void Volume_HandlePowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case PBT_APMQUERYSUSPEND:
|
|
{
|
|
if (g_hMixer) // Dumping the Old
|
|
{
|
|
mixerClose(g_hMixer);
|
|
g_hMixer = NULL;
|
|
g_uMixer = 0;
|
|
g_dwMixerDevNode = 0L;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PBT_APMQUERYSUSPENDFAILED:
|
|
case PBT_APMRESUMESUSPEND:
|
|
{
|
|
Volume_WinMMDeviceChange(hWnd);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void Volume_DeviceChange_Cleanup()
|
|
{
|
|
if (DeviceEventContext)
|
|
{
|
|
UnregisterDeviceNotification(DeviceEventContext);
|
|
DeviceEventContext = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
**************************************************************************************************
|
|
Volume_GetDeviceHandle()
|
|
|
|
given a mixerID this functions opens its corresponding device handle. This handle can be used
|
|
to register for DeviceNotifications.
|
|
|
|
dwMixerID -- The mixer ID
|
|
phDevice -- a pointer to a handle. This pointer will hold the handle value if the function is
|
|
successful
|
|
|
|
return values -- If the handle could be obtained successfully the return vlaue is TRUE.
|
|
|
|
**************************************************************************************************
|
|
*/
|
|
BOOL Volume_GetDeviceHandle(DWORD dwMixerID, HANDLE *phDevice)
|
|
{
|
|
MMRESULT mmr;
|
|
ULONG cbSize=0;
|
|
TCHAR *szInterfaceName=NULL;
|
|
|
|
//Query for the Device interface name
|
|
mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
|
|
if(MMSYSERR_NOERROR == mmr)
|
|
{
|
|
szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
|
|
if(!szInterfaceName)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
|
|
if(MMSYSERR_NOERROR != mmr)
|
|
{
|
|
GlobalFreePtr(szInterfaceName);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//Get an handle on the device interface name.
|
|
*phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
GlobalFreePtr(szInterfaceName);
|
|
if(INVALID_HANDLE_VALUE == *phDevice)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* DeviceChange_Init()
|
|
* First time initialization for WM_DEVICECHANGE messages
|
|
*
|
|
* On NT 5.0, you have to register for device notification
|
|
*/
|
|
void Volume_DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
|
|
{
|
|
DEV_BROADCAST_HANDLE DevBrodHandle;
|
|
HANDLE hMixerDevice=NULL;
|
|
|
|
|
|
//If we had registered already for device notifications, unregister ourselves.
|
|
Volume_DeviceChange_Cleanup();
|
|
|
|
//If we get the device handle register for device notifications on it.
|
|
if(Volume_GetDeviceHandle(dwMixerID, &hMixerDevice))
|
|
{
|
|
memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
|
|
|
|
DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
|
DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
DevBrodHandle.dbch_handle = hMixerDevice;
|
|
|
|
DeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
|
|
if(hMixerDevice)
|
|
{
|
|
CloseHandle(hMixerDevice);
|
|
hMixerDevice = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Watch for PNP events to free up the open handle when needed
|
|
// We will assume any changes will now generate a WINMM_DEVICECHANGED message from WinMM
|
|
// except for the QUERYREMOVEFAILED case, in this case we will just re-aquire the preferred mixer
|
|
//
|
|
void Volume_DeviceChange(HWND hWnd,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
|
|
|
|
//If we have an handle on the device then we get a DEV_BROADCAST_HDR structure as the lParam.
|
|
|
|
if(!DeviceEventContext || !bh || (bh->dbch_devicetype != DBT_DEVTYP_HANDLE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (wParam)
|
|
{
|
|
case DBT_DEVICEQUERYREMOVE: // Someone wants to remove this device, let's let them.
|
|
{
|
|
if (g_hMixer)
|
|
{
|
|
mixerClose(g_hMixer);
|
|
g_hMixer = NULL;
|
|
g_uMixer = 0;
|
|
g_dwMixerDevNode = 0L;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DBT_DEVICEQUERYREMOVEFAILED: // The query failed, the device will not be removed, so lets reopen it.
|
|
{
|
|
Volume_WinMMDeviceChange(hWnd); // Lets just use this function to do it.
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Volume_WmDestroy(
|
|
HWND hDlg
|
|
)
|
|
{
|
|
Volume_DeviceChange_Cleanup();
|
|
}
|
|
|
|
void Volume_Shutdown(
|
|
HWND hWnd)
|
|
{
|
|
Volume_UpdateStatus(hWnd, FALSE, FALSE);
|
|
}
|
|
|
|
void Volume_Menu(HWND hwnd, UINT uMenuNum, UINT uButton)
|
|
{
|
|
POINT pt;
|
|
UINT iCmd;
|
|
HMENU hmenu;
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
hmenu = Volume_CreateMenu();
|
|
if (!hmenu)
|
|
return;
|
|
|
|
SetForegroundWindow(hwnd);
|
|
iCmd = TrackPopupMenu(hmenu, uButton | TPM_RETURNCMD | TPM_NONOTIFY,
|
|
pt.x, pt.y, 0, hwnd, NULL);
|
|
|
|
DestroyMenu(hmenu);
|
|
switch (iCmd) {
|
|
case VOLUMEMENU_PROPERTIES:
|
|
Volume_ControlPanel(hwnd);
|
|
break;
|
|
|
|
case VOLUMEMENU_SNDVOL:
|
|
Volume_VolumeControl();
|
|
break;
|
|
}
|
|
|
|
SetIconFocus(hwnd, STWM_NOTIFYVOLUME);
|
|
|
|
}
|
|
|
|
void Volume_Notify(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (lParam)
|
|
{
|
|
case WM_RBUTTONUP:
|
|
Volume_Menu(hwnd, 1, TPM_RIGHTBUTTON);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
SetTimer(hwnd, VOLUME_TIMER_ID, GetDoubleClickTime()+100, NULL);
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
KillTimer(hwnd, VOLUME_TIMER_ID);
|
|
Volume_VolumeControl();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
/*
|
|
* MYWM_WAKEUP and the "Tray Volume" window are defined by the SNDVOL32.EXE
|
|
* application. Changing these values or changing the values in SNDVOL32.EXE
|
|
* without mirroring them here will break the tray volume dialog.
|
|
*/
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
/* WARNING - WARNING - DANGER - DANGER - WARNING - WARNING - DANGER - DANGER */
|
|
|
|
#define MYWM_WAKEUP (WM_APP+100+6)
|
|
|
|
void Volume_Timer(HWND hwnd)
|
|
{
|
|
KillTimer(hwnd, VOLUME_TIMER_ID);
|
|
|
|
Volume_WakeUpOrClose (FALSE);
|
|
}
|
|
|
|
void Volume_WakeUpOrClose(BOOL fClose)
|
|
{
|
|
const TCHAR szVolWindow [] = TEXT ("Tray Volume");
|
|
HWND hApp;
|
|
|
|
if (hApp = FindWindow(szVolWindow, NULL))
|
|
{
|
|
SendMessage(hApp, MYWM_WAKEUP, (WPARAM)fClose, 0);
|
|
}
|
|
else if (!fClose)
|
|
{
|
|
const TCHAR szOpen[] = TEXT ("open");
|
|
const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
|
|
const TCHAR szParamsWakeup[] = TEXT ("/t");
|
|
|
|
ShellExecute (NULL, szOpen, szVolApp, szParamsWakeup, NULL, SW_SHOWNORMAL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_ControlPanel
|
|
*
|
|
* Launch "Audio" control panel/property sheet upon request.
|
|
*
|
|
* */
|
|
void Volume_ControlPanel(HWND hwnd)
|
|
{
|
|
const TCHAR szOpen[] = TEXT ("open");
|
|
const TCHAR szRunDLL[] = TEXT ("RUNDLL32.EXE");
|
|
const TCHAR szParams[] = TEXT ("MMSYS.CPL,ShowFullControlPanel");
|
|
|
|
ShellExecute(NULL, szOpen, szRunDLL, szParams, NULL, SW_SHOWNORMAL);
|
|
}
|
|
|
|
/*
|
|
* Volume_VolumeControl
|
|
*
|
|
* Launch Volume Control App
|
|
*
|
|
* */
|
|
void Volume_VolumeControl()
|
|
{
|
|
const TCHAR szOpen[] = TEXT ("open");
|
|
const TCHAR szVolApp[] = TEXT ("SNDVOL32.EXE");
|
|
|
|
ShellExecute(NULL, szOpen, szVolApp, NULL, NULL, SW_SHOWNORMAL);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* FileExists
|
|
*
|
|
* Does a file exist
|
|
*
|
|
* */
|
|
|
|
BOOL FileExists(LPCTSTR pszPath)
|
|
{
|
|
return (GetFileAttributes(pszPath) != (DWORD)-1);
|
|
} // End FileExists
|
|
|
|
|
|
/*
|
|
* FindSystemFile
|
|
*
|
|
* Finds full path to specified file
|
|
*
|
|
* */
|
|
|
|
BOOL FindSystemFile(LPCTSTR pszFileName, LPTSTR pszFullPath, UINT cchSize)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
LPTSTR pszName;
|
|
DWORD cchLen;
|
|
|
|
if ((pszFileName == NULL) || (pszFileName[0] == 0))
|
|
return FALSE;
|
|
|
|
cchLen = SearchPath(NULL, pszFileName, NULL, MAX_PATH,
|
|
szPath,&pszName);
|
|
if (cchLen == 0)
|
|
return FALSE;
|
|
|
|
if (cchLen >= MAX_PATH)
|
|
cchLen = MAX_PATH - 1;
|
|
|
|
if (! FileExists (szPath))
|
|
return FALSE;
|
|
|
|
if ((pszFullPath == NULL) || (cchSize == 0))
|
|
return TRUE;
|
|
|
|
// Copy full path into buffer
|
|
if (cchLen >= cchSize)
|
|
cchLen = cchSize - 1;
|
|
|
|
lstrcpyn (pszFullPath, szPath, cchLen);
|
|
|
|
pszFullPath[cchLen] = 0;
|
|
|
|
return TRUE;
|
|
} // End FindSystemFile
|