windows-nt/Source/XPSP1/NT/shell/osshell/control/mmsys/mmcpl.c
2020-09-26 16:20:57 +08:00

1495 lines
44 KiB
C

/*==========================================================================*/
//
// mmcpl.c
//
// Copyright (C) 1993-1994 Microsoft Corporation. All Rights Reserved.
//
// 06/94 -Created- VijR
//
/*==========================================================================*/
#pragma warning( disable: 4103)
#include "mmcpl.h"
#include <cpl.h>
#define NOSTATUSBAR
#include <commctrl.h>
#include <prsht.h>
#include <regstr.h>
#include <infstr.h>
#include <devguid.h>
#include "draw.h"
#include "utils.h"
#include "drivers.h"
#include "sulib.h"
#include <tchar.h>
#include <hwtab.h>
#include "debug.h"
#ifndef cchRESOURCE
#define cchRESOURCE 256
#endif
/*
***************************************************************
* Globals
***************************************************************
*/
HINSTANCE ghInstance = NULL;
BOOL gfNukeExt = -1;
HWND ghwndMsgBox = NULL;
HWND ghwndAdvProp = NULL;
BOOL gfVoiceTab = FALSE;
#ifdef FIX_BUG_15451
static TCHAR cszFORKLINE[] = TEXT("RUNDLL32.EXE MMSYS.CPL,ShowDriverSettingsAfterFork %s");
#endif // FIX_BUG_15451
SZCODE cszAUDIO[] = AUDIO;
SZCODE cszVIDEO[] = VIDEO;
SZCODE cszCDAUDIO[] = CDAUDIO;
SZCODE cszMIDI[] = MIDI;
SZCODE cszVOICE[] = VOICE;
SZCODE cszVOLUME[] = VOLUME;
/*
***************************************************************
* Typedefs
***************************************************************
*/
typedef struct _ExtPropSheetCBParam //Callback Parameter
{
HTREEITEM hti;
LPPROPSHEETHEADER ppsh;
LPARAM lParam1; //PIRESOURCE/PINSTRUMENT etc. depending on node. (OR) Simple propsheet class
LPARAM lParam2; //hwndTree (OR) Simple propsheet name
} EXTPROPSHEETCBPARAM, *PEXTPROPSHEETCBPARAM;
typedef struct _MBInfo
{
LPTSTR szTitle;
LPTSTR szMsg;
UINT uStyle;
} MBINFO, *PMBINFO;
/*
***************************************************************
* Defines
***************************************************************
*/
#define MAXPAGES 8 // MAX number of sheets allowed
#define MAXMODULES 32 // MAX number of external modules allowed
#define MAXCLASSSIZE 64
#define cComma TEXT(',')
#define PROPTABSIZE 13
#define GetString(_str,_id,_hi) LoadString (_hi, _id, _str, sizeof(_str)/sizeof(TCHAR))
/*
***************************************************************
* File Globals
***************************************************************
*/
static SZCODE aszSimpleProperties[] = REGSTR_PATH_MEDIARESOURCES TEXT("\\MediaExtensions\\shellx\\SimpleProperties\\");
static SZCODE aszShellName[] = TEXT("ShellName");
static UINT g_cRefCnt; // keeps track of the ref count
static int g_cProcesses = 0;
static int g_nStartPage = 0;
/*
***************************************************************
* Prototypes
***************************************************************
*/
INT_PTR CALLBACK AudioDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK VideoDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK CDDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK ACMDlg(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK SoundDlg(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK VolumeDlg(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK AddDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK AdvDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK HardwareDlgProc(HWND hdlg, UINT uMsg, WPARAM wp, LPARAM lp);
INT_PTR CALLBACK VoiceDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
//INT_PTR CALLBACK EffectDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
//
// This is the dialog procedure for the "Hardware" page.
//
INT_PTR CALLBACK HardwareDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
static HWND s_hwndHW = NULL;
switch (uMessage)
{
case WM_NOTIFY:
{
NMHDR * pnmhdr = (NMHDR *) lParam;
int code = pnmhdr->code;
switch (code)
{
case HWN_FILTERITEM:
{
NMHWTAB *pnmht = (NMHWTAB *) lParam;
BOOL fFilter = FALSE;
if (!pnmht->fHidden) // Let's not bother looking at devices already hidden
{
fFilter = FALSE;
}
return(TRUE);
}
break;
case HWN_SELECTIONCHANGED:
{
NMHWTAB *pnmht = (NMHWTAB *) lParam;
if (pnmht)
{
if (pnmht->pdinf)
{
if (IsEqualGUID(&(pnmht->pdinf->ClassGuid),&GUID_DEVCLASS_CDROM))
{
SetWindowText(s_hwndHW, TEXT("hh.exe ms-its:tshoot.chm::/hdw_drives.htm"));
}
else
{
SetWindowText(s_hwndHW, TEXT("hh.exe ms-its:tshoot.chm::/tssound.htm"));
}
}
}
}
break;
}
}
break;
case WM_INITDIALOG:
{
GUID guidClass[2];
guidClass[0] = GUID_DEVCLASS_CDROM;
guidClass[1] = GUID_DEVCLASS_MEDIA;
s_hwndHW = DeviceCreateHardwarePageEx(hDlg, (const GUID *) &guidClass, 2, HWTAB_LARGELIST );
if (s_hwndHW)
{
SetWindowText(s_hwndHW, TEXT("hh.exe ms-its:tshoot.chm::/tssound.htm"));
}
else
{
DestroyWindow(hDlg); // catastrophic failure
}
}
return FALSE;
}
return FALSE;
}
INT_PTR CALLBACK CD_HardwareDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
switch (uMessage)
{
case WM_INITDIALOG:
{
HWND hwndHW;
hwndHW = DeviceCreateHardwarePageEx(hDlg, &GUID_DEVCLASS_CDROM, 1, HWTAB_SMALLLIST);
if (hwndHW)
{
SetWindowText(hwndHW, TEXT("hh.exe ms-its:tshoot.chm::/hdw_multi.htm"));
}
else
{
DestroyWindow(hDlg); // catastrophic failure
}
}
return FALSE;
}
return FALSE;
}
/*
***************************************************************
***************************************************************
*/
INT_PTR FAR PASCAL mmse_MessageBoxProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
switch (wMsg)
{
case WM_INITDIALOG:
{
PMBINFO pmbInfo = (PMBINFO)lParam;
UINT uStyle = pmbInfo->uStyle;
SetWindowText(hDlg, pmbInfo->szTitle);
SetWindowText(GetDlgItem(hDlg, MMSE_TEXT), pmbInfo->szMsg);
if (IsFlagClear(uStyle, MMSE_OK))
DestroyWindow(GetDlgItem(hDlg, MMSE_OK));
if (IsFlagClear(uStyle, MMSE_YES))
DestroyWindow(GetDlgItem(hDlg, MMSE_YES));
if (IsFlagClear(uStyle, MMSE_NO))
DestroyWindow(GetDlgItem(hDlg, MMSE_NO));
ghwndMsgBox = hDlg;
break;
}
case WM_DESTROY:
ghwndMsgBox = NULL;
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case MMSE_YES:
EndDialog(hDlg, MMSE_YES);
break;
case MMSE_NO:
EndDialog(hDlg, MMSE_NO);
break;
case MMSE_OK:
EndDialog(hDlg, MMSE_OK);
break;
}
break;
}
default:
return FALSE;
}
return TRUE;
}
INT_PTR mmse_MessageBox(HWND hwndP, LPTSTR szMsg, LPTSTR szTitle, UINT uStyle)
{
MBINFO mbInfo;
mbInfo.szMsg = szMsg;
mbInfo.szTitle = szTitle;
mbInfo.uStyle = uStyle;
return DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_MESSAGE_BOX), hwndP, mmse_MessageBoxProc, (LPARAM)&mbInfo);
}
/*==========================================================================*/
int FAR PASCAL lstrncmpi(
LPCTSTR lszKey,
LPCTSTR lszClass,
int iSize)
{
TCHAR aszKey[64];
lstrcpyn(aszKey, lszKey, iSize);
return lstrcmpi(aszKey, lszClass);
}
int StrByteLen(LPTSTR sz)
{
LPTSTR psz;
if (!sz)
return 0;
for (psz = sz; *psz; psz = CharNext(psz))
;
return (int)(psz - sz);
}
static void NukeExt(LPTSTR sz)
{
int len;
len = StrByteLen(sz);
if (len > 4 && sz[len-4] == TEXT('.'))
sz[len-4] = 0;
}
static LPTSTR NukePath(LPTSTR sz)
{
LPTSTR pTmp, pSlash;
for (pSlash = pTmp = sz; *pTmp; pTmp = CharNext(pTmp))
{
if (*pTmp == TEXT('\\'))
pSlash = pTmp;
}
return (pSlash == sz ? pSlash : pSlash+1);
}
void CheckNukeExtOption(LPTSTR sz)
{
SHFILEINFO sfi;
SHGetFileInfo(sz, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME);
if (lstrcmpi((LPTSTR)(sfi.szDisplayName+lstrlen(sfi.szDisplayName)-4), cszWavExt))
gfNukeExt = TRUE;
else
gfNukeExt = FALSE;
}
LPTSTR PASCAL NiceName(LPTSTR sz, BOOL fNukePath)
{
SHFILEINFO sfi;
if (gfNukeExt == -1)
CheckNukeExtOption(sz);
if (!SHGetFileInfo(sz, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
return sz;
if (fNukePath)
{
lstrcpy(sz, sfi.szDisplayName);
}
else
{
LPTSTR lpszFileName;
lpszFileName = NukePath(sz);
lstrcpy(lpszFileName, sfi.szDisplayName);
if (lpszFileName != sz)
CharUpperBuff(sz, 1);
}
return sz;
}
/*
***************************************************************
* ErrorBox
*
* Description:
* Brings up error Dialog displaying error
*
* Parameters:
* HWND hDlg - Window handle
* int iResource - id of the resource to be loaded
* LPTSTR lpszDesc - The string to be inserted in the resource string
*
* Returns: BOOL
*
***************************************************************
*/
BOOL PASCAL ErrorBox(HWND hDlg, int iResource, LPTSTR lpszDesc)
{
TCHAR szBuf[MAXMSGLEN];
TCHAR szTitle[MAXSTR];
TCHAR szResource[MAXMSGLEN];
LoadString(ghInstance, iResource, szResource, MAXSTR);
LoadString(ghInstance, IDS_ERROR, szTitle, MAXSTR);
wsprintf(szBuf, szResource, lpszDesc);
MessageBox(hDlg, szBuf, szTitle, MB_APPLMODAL | MB_OK |MB_ICONSTOP);
return TRUE;
}
int PASCAL DisplayMessage(HWND hDlg, int iResTitle, int iResMsg, UINT uStyle)
{
TCHAR szBuf[MAXMSGLEN];
TCHAR szTitle[MAXSTR];
UINT uAddStyle = MB_APPLMODAL;
if (!LoadString(ghInstance, iResTitle, szTitle, MAXSTR))
return FALSE;
if (!LoadString(ghInstance, iResMsg, szBuf, MAXSTR))
return FALSE;
if (uStyle & MB_OK)
uAddStyle |= MB_ICONASTERISK;
else
uAddStyle |= MB_ICONQUESTION;
return MessageBox(hDlg, szBuf, szTitle, uStyle | uAddStyle);
}
//Adds spaces around Tab Names to make them all approx. same size.
STATIC void PadWithSpaces(LPTSTR szName, LPTSTR szPaddedName)
{
static SZCODE cszFmt[] = TEXT("%s%s%s");
TCHAR szPad[8];
int i;
i = PROPTABSIZE - lstrlen(szName);
i = (i <= 0) ? 0 : i/2;
for (szPad[i] = TEXT('\0');i; i--)
szPad[i-1] = TEXT(' ');
wsprintf(szPaddedName, cszFmt, szPad, szName, szPad);
}
/*==========================================================================*/
UINT CALLBACK CallbackPage(
HWND hwnd,
UINT uMsg,
LPPROPSHEETPAGE ppsp)
{
if (uMsg == PSPCB_RELEASE)
{
DPF_T("* RelasePage %s *", (LPTSTR)ppsp->pszTitle);
}
return 1;
}
/*==========================================================================*/
static BOOL PASCAL NEAR AddPage(
LPPROPSHEETHEADER ppsh,
LPCTSTR pszTitle,
DLGPROC pfnDialog,
UINT idTemplate,
LPARAM lParam)
{
if (ppsh->nPages < MAXPAGES)
{
if (pfnDialog)
{
PROPSHEETPAGE psp;
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USETITLE | PSP_USECALLBACK;
psp.hInstance = ghInstance;
psp.pszTemplate = MAKEINTRESOURCE(idTemplate);
psp.pszIcon = NULL;
psp.pszTitle = pszTitle;
psp.pfnDlgProc = pfnDialog;
psp.lParam = (LPARAM)lParam;
psp.pfnCallback = CallbackPage;
psp.pcRefParent = NULL;
if (ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(&psp))
{
ppsh->nPages++;
return TRUE;
}
}
}
return FALSE;
}
/*==========================================================================*/
BOOL CALLBACK MMExtPropSheetCallback(DWORD dwFunc, DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwInstance)
{
PEXTPROPSHEETCBPARAM pcbp = (PEXTPROPSHEETCBPARAM)dwInstance;
if (!pcbp && dwFunc != MM_EPS_BLIND_TREECHANGE)
return FALSE;
switch (dwFunc)
{
case MM_EPS_GETNODEDESC:
{
if (!dwParam1)
return FALSE;
if (pcbp->hti == NULL)
lstrcpy((LPTSTR)dwParam1, (LPTSTR)pcbp->lParam2);
else
{
GetTreeItemNodeDesc ((LPTSTR)dwParam1,
(PIRESOURCE)pcbp->lParam1);
}
break;
}
case MM_EPS_GETNODEID:
{
if (!dwParam1)
return FALSE;
if (pcbp->hti == NULL)
lstrcpy((LPTSTR)dwParam1, (LPTSTR)pcbp->lParam2);
else
{
GetTreeItemNodeID ((LPTSTR)dwParam1,
(PIRESOURCE)pcbp->lParam1);
}
break;
}
case MM_EPS_ADDSHEET:
{
HPROPSHEETPAGE hpsp = (HPROPSHEETPAGE)dwParam1;
if (hpsp && (pcbp->ppsh->nPages < MAXPAGES))
{
pcbp->ppsh->phpage[pcbp->ppsh->nPages++] = hpsp;
return TRUE;
}
return FALSE;
}
case MM_EPS_TREECHANGE:
{
RefreshAdvDlgTree ();
break;
}
case MM_EPS_BLIND_TREECHANGE:
{
RefreshAdvDlgTree ();
break;
}
default:
return FALSE;
}
return TRUE;
}
INT_PTR CALLBACK SpeechDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/*==========================================================================*/
static BOOL PASCAL NEAR AddSpeechPage(LPPROPSHEETHEADER ppsh)
{
TCHAR aszTitleRes[128];
TCHAR szTmp[32];
LoadString(ghInstance, IDS_SPEECH_NAME, aszTitleRes, sizeof(aszTitleRes)/sizeof(TCHAR));
PadWithSpaces((LPTSTR)aszTitleRes, (LPTSTR)szTmp);
return AddPage(ppsh, szTmp, SpeechDlgProc, IDD_SPEECH, (LPARAM)NULL);
}
/*==========================================================================*/
static BOOL PASCAL NEAR AddAdvancedPage(
LPPROPSHEETHEADER ppsh)
{
TCHAR aszTitleRes[128];
TCHAR szTmp[32];
LoadString(ghInstance, IDS_ADVANCED, aszTitleRes, sizeof(aszTitleRes)/sizeof(TCHAR));
PadWithSpaces((LPTSTR)aszTitleRes, (LPTSTR)szTmp);
return AddPage(ppsh, szTmp, AdvDlg, ADVDLG, (LPARAM)NULL);
}
/*==========================================================================*/
static BOOL PASCAL NEAR AddHardwarePage(
LPPROPSHEETHEADER ppsh)
{
TCHAR aszTitleRes[128];
TCHAR szTmp[32];
// Don't add a hardware tab if the admin restricted it
if (SHRestricted(REST_NOHARDWARETAB))
return FALSE;
LoadString(ghInstance, IDS_HARDWARE, aszTitleRes, sizeof(aszTitleRes)/sizeof(TCHAR));
PadWithSpaces((LPTSTR)aszTitleRes, (LPTSTR)szTmp);
return AddPage(ppsh, szTmp, HardwareDlgProc, HWDLG, (LPARAM)NULL);
}
/*==========================================================================*/
static BOOL PASCAL NEAR AddSchemesPage(
LPPROPSHEETHEADER ppsh)
{
TCHAR aszTitleRes[128];
LoadString(ghInstance, IDS_EVENTSNAME, aszTitleRes, sizeof(aszTitleRes)/sizeof(TCHAR));
return AddPage(ppsh, aszTitleRes, SoundDlg, SOUNDDIALOG, (LPARAM)NULL);
}
/*==========================================================================*/
static void PASCAL NEAR AddInternalPages (LPPROPSHEETHEADER ppsh)
{
static EXTPROPSHEETCBPARAM cbp;
TCHAR szText[ cchRESOURCE ];
TCHAR szPadded[ cchRESOURCE ];
// Add the Volume page
//
GetString (szText, IDS_VOLUMENAME, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, VolumeDlg, IDD_VOLUME, (LPARAM)NULL);
// Add the Sound Scheme page
//
GetString (szText, IDS_EVENTSNAME, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, SoundDlg, SOUNDDIALOG, (LPARAM)NULL);
// Add the Audio page
//
GetString (szText, IDS_AUDIO_TAB, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, AudioDlg, AUDIODLG, (LPARAM)NULL);
// Add the Voice page
//
GetString (szText, IDS_VOICE, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, VoiceDlg, VOICEDLG, (LPARAM)NULL);
// Add the Video page
//
/* GetString (szText, IDS_VIDEO_TAB, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, VideoDlg, VIDEODLG, (LPARAM)NULL); */
// Add the MIDI page
//
/* GetString (szText, IDS_MIDI_TAB, ghInstance);
PadWithSpaces (szText, szPadded);
cbp.ppsh = ppsh;
cbp.hti = NULL;
cbp.lParam1 = (LPARAM)cszMIDI;
cbp.lParam2 = (LPARAM)szPadded;
AddSimpleMidiPages ((LPVOID)szPadded, MMExtPropSheetCallback, (LPARAM)&cbp);
*/
// Add the CD Audio page
//
/* GetString (szText, IDS_CDAUDIO_TAB, ghInstance);
PadWithSpaces (szText, szPadded);
AddPage (ppsh, szPadded, CDDlg, CDDLG, (LPARAM)NULL);
*/
}
static void InitPSH(LPPROPSHEETHEADER ppsh, HWND hwndParent, LPTSTR pszCaption, HPROPSHEETPAGE FAR * phpsp)
{
ppsh->dwSize = sizeof(PROPSHEETHEADER);
ppsh->dwFlags = PSH_PROPTITLE;
ppsh->hwndParent = hwndParent;
ppsh->hInstance = ghInstance;
ppsh->pszCaption = pszCaption;
ppsh->nPages = 0;
ppsh->nStartPage = 0;
ppsh->phpage = phpsp;
}
/*==========================================================================*/
#ifdef FIX_BUG_15451
static void PASCAL cplMMDoubleClick (HWND hCPlWnd, int nStartPage)
#else // FIX_BUG_15451
static void PASCAL cplMMDoubleClick (HWND hCPlWnd)
#endif // FIX_BUG_15451
{
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
TCHAR strOldDir[MAX_PATH], strSysDir[MAX_PATH];
strOldDir[0] = TEXT('\0');
strSysDir[0] = TEXT('\0');
GetSystemDirectory(strSysDir, MAX_PATH);
GetCurrentDirectory(MAX_PATH, strOldDir);
SetCurrentDirectory(strSysDir);
wsInfParseInit();
InitCommonControls();
OleInitialize(NULL);
RegSndCntrlClass((LPCTSTR)DISPFRAMCLASS);
InitPSH(&psh,hCPlWnd,(LPTSTR)MAKEINTRESOURCE(IDS_MMNAME),hpsp);
#ifdef FIX_BUG_15451
psh.nStartPage = nStartPage;
#else // FIX_BUG_15451
psh.nStartPage = g_nStartPage;
#endif // FIX_BUG_15451
g_nStartPage = 0;
AddInternalPages(&psh);
//AddSpeechPage(&psh);
//AddAdvancedPage(&psh);
AddHardwarePage(&psh);
PropertySheet(&psh);
OleUninitialize();
infClose(NULL);
SetCurrentDirectory(strOldDir);
}
/*==========================================================================*/
static void PASCAL cplEventsDoubleClick (HWND hCPlWnd)
{
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
InitCommonControls();
RegSndCntrlClass((LPCTSTR)DISPFRAMCLASS);
InitPSH(&psh,hCPlWnd,(LPTSTR)MAKEINTRESOURCE(IDS_EVENTSNAME),hpsp);
AddSchemesPage(&psh);
PropertySheet(&psh);
}
#ifdef FIX_BUG_15451
/*==========================================================================*/
/*
* ShowDriverSettings
* ShowDriverSettingsAfterFork
*
* When the user selects DevicesTab.<anydevice>.Properties.Settings, a
* DRV_CONFIGURE message is sent to the selected user-mode driver, to cause
* it to display its configuration dialog. The sound drivers shipped with
* NT (SNDBLST,MVAUDIO,SNDSYS) exhibit a bug in this condition: when the
* configuration dialog is complete (regardless of whether OK or CANCEL was
* selected), these drivers attempt to unload-and-reload their kernel-mode
* component in order to begin using the new (or restore the original)
* driver settings. The unload request fails, because both the Audio tab
* and SNDVOL.EXE have open mixer handles and pending IRPs within the kernel
* driver (the latter are used to provide notifications of volume changes).
* Worse, when the unload fails, it leaves the driver useless: its state
* remains STOP_PENDING, and it cannot be resurrected without logging off
* and back on.
*
* These routines have been provided as a temporary workaround for bug 15451,
* which describes the problem mentioned above. The theory behind this
* solution is two-fold:
* 1- close SNDVOL.EXE as soon as a driver's configuration dialog is
* to be displayed, and restart it directly thereafter. This prevents
* it from maintaining any open handles to and/or pending IRPs within the
* kernel driver.
* 2- if the Audio tab has ever been displayed, it will have open mixers
* which must be closed. Because a bug/design flaw within these sound
* drivers prevents the mixers from being closed without killing this
* process (the sound drivers each cache open mixer handles), the
* routine ShowDriverSettings forks a new MMSYS.CPL process, which is
* then used to display the driver's settings dialog.
*
* The flow of this solution follows:
*
* 1- MMSYS.CPL starts on Audio tab, setting fHaveStartedAudioDialog to TRUE.
* 2- User selects Devices tab.
* 3- User selects a device driver.
* 4- User selects Properties+Settings; control reaches ShowDriverSettings().
* 5- ShowDriverSettings() determines if there is a need to fork a new process:
* this will be the case if the Audio tab has been displayed, and the
* device for which it is to display settings contains mixers. If either
* of these conditions is false, ShowDriverSettings displays the driver's
* settings dialog directly (via ConfigureDriver()).
* 6- ShowDriverSettings() uses WinExec() to fork a new process, using
* the routine ShowDriverSettingsAfterFork() as an entry point. If the
* exec fails, ShowDriverSettings() displays the driver's settings dialog
* directly (via ConfigureDriver()).
* PROCESS 1: PROCESS 2:
* 7- Enters WaitForNewCPLWindow(), 1- ShowDriverSettingsAfterFork() will
* which will wait up to 5 seconds receive on its command-line the
* for the new MMSYS.CPL process name of the driver for which
* to open a driver Properties settings have been requested. It
* dialog which matches its own: opens the primary dialog, using the
* if it finds such a dialog, Devices tab as the initial tab--
* WaitForNewCPLWindow() will post so that the Advanced tab is never
* IDCANCEL messages to both the displayed, and because the Devices
* current driver Properties dialog, tab is the active tab on the other
* and to this process's main process.
* dialog, terminating this process. 2- During WM_INITDIALOG of the Devices
* dialog, this process searches for
* the previous process' MMSYS.CPL dialog.
* If successful, it moves this MMSYS.CPL
* dialog directly behind the previous dialog.
* 3- During ID_INIT of the Devices dialog, this
* process searches the TreeView for the driver
* which was named on the comand-line: if found,
* it highlights the TreeItem and simulates a press
* of the Properties button
* 4- During WM_INITDIALOG of the device's Properties dialog,
* this process searches for the previous process' device's
* properties dialog. If successful, it moves this dialog
* directly behind its counterpart.
* 5- During ID_INIT of the device's Properties dialog, this process
* simulates a press of the Settings button
* 6- When the Settings button is pressed, this process recognizes that
* it has been forked and skips the call to ShowDriverSettings(),
* instead simply displaying the driver's settings dialog (via
* ConfigureDriver()).
*
* Let it be known that this is a hack, and should be removed post-beta.
*
*/
extern BOOL fHaveStartedAudioDialog; // in MSACMCPL.C
void ShowDriverSettings (HWND hDlg, LPTSTR pszName)
{
if (fHaveStartedAudioDialog && fDeviceHasMixers (pszName))
{
TCHAR szForkLine[ cchRESOURCE *2 ];
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
wsprintf (szForkLine, cszFORKLINE, pszName);
if (CreateProcess(NULL,szForkLine,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
{
(void)WaitForNewCPLWindow (hDlg);
}
else
{
ConfigureDriver (hDlg, pszName);
}
}
else
{
ConfigureDriver (hDlg, pszName);
}
}
void WINAPI ShowDriverSettingsAfterFork (
HWND hwndStub,
HINSTANCE hAppInstance,
LPTSTR lpszCmdLine,
int nCmdShow)
{
#ifdef UNICODE
WCHAR szCmdLine[ cchRESOURCE ];
#else
#define szCmdLine lpszCmdLine
#endif
lstrcpy (szDriverWhichNeedsSettings, szCmdLine);
cplMMDoubleClick (NULL, 4); // 4==Start on Advanced ("Devices") tab
}
void WINAPI ShowDriverSettingsAfterForkW (
HWND hwndStub,
HINSTANCE hAppInstance,
LPWSTR lpwszCmdLine,
int nCmdShow)
{
#ifdef UNICODE
#define szCmdLine lpwszCmdLine
#else
CHAR szCmdLine[ cchRESOURCE ];
wcstombs(szCmdLine, lpwszCmdLine, cchRESOURCE);
#endif
lstrcpy (szDriverWhichNeedsSettings, szCmdLine);
cplMMDoubleClick (NULL, 4); // 4==Start on Advanced ("Devices") tab
}
#endif // FIX_BUG_15451
// Globals to support sound event command line parameters.
#define MAX_SND_EVNT_CMD_LINE 32
TCHAR gszCmdLineApp[MAX_SND_EVNT_CMD_LINE];
TCHAR gszCmdLineEvent[MAX_SND_EVNT_CMD_LINE];
/*==========================================================================*/
LONG CPlApplet(
HWND hCPlWnd,
UINT Msg,
LPARAM lParam1,
LPARAM lParam2)
{
switch (Msg)
{
case CPL_INIT:
wHelpMessage = RegisterWindowMessage(TEXT("ShellHelp"));
DPF_T("*CPL_INIT*");
g_cRefCnt++;
return (LRESULT)TRUE;
case CPL_GETCOUNT:
return (LRESULT)1;
case CPL_INQUIRE:
DPF_T("*CPL_INQUIRE*");
switch (lParam1)
{
case 0:
((LPCPLINFO)lParam2)->idIcon = IDI_MMICON;
((LPCPLINFO)lParam2)->idName = IDS_MMNAME;
((LPCPLINFO)lParam2)->idInfo = IDS_MMINFO;
break;
default:
return FALSE;
}
((LPCPLINFO)lParam2)->lData = 0L;
return TRUE;
case CPL_NEWINQUIRE:
switch (lParam1)
{
case 0:
((LPNEWCPLINFO)lParam2)->hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_MMICON));
LoadString(ghInstance, IDS_MMNAME, ((LPNEWCPLINFO)lParam2)->szName, sizeof(((LPNEWCPLINFO)lParam2)->szName)/sizeof(TCHAR));
LoadString(ghInstance, IDS_MMINFO, ((LPNEWCPLINFO)lParam2)->szInfo, sizeof(((LPNEWCPLINFO)lParam2)->szInfo)/sizeof(TCHAR));
break;
default:
return FALSE;
}
((LPNEWCPLINFO)lParam2)->dwHelpContext = 0;
((LPNEWCPLINFO)lParam2)->dwSize = sizeof(NEWCPLINFO);
((LPNEWCPLINFO)lParam2)->lData = 0L;
((LPNEWCPLINFO)lParam2)->szHelpFile[0] = 0;
return TRUE;
case CPL_DBLCLK:
DPF_T("* CPL_DBLCLICK*");
// Do the applet thing.
switch (lParam1)
{
case 0:
// Check for obsolete command line (see comments
// under CPL_STARTWPARAMS)
if ((-1) == g_nStartPage) break;
#ifdef FIX_BUG_15451
lstrcpy (szDriverWhichNeedsSettings, TEXT(""));
cplMMDoubleClick(hCPlWnd, g_nStartPage);
#else // FIX_BUG_15451
cplMMDoubleClick(hCPlWnd);
#endif // FIX_BUG_15451
break;
}
break;
case CPL_STARTWPARMS:
switch (lParam1)
{
case 0:
if (lParam2 && *((LPTSTR)lParam2))
{
TCHAR c;
c = *((LPTSTR)lParam2);
if (c > TEXT('0') && c < TEXT('5'))
{
g_nStartPage = c - TEXT('0');
break;
}
// The "S" command line was used on Windows 98 and Windows 98
// SE. The command line was written to the Active Setup
// registry to run logic to setup preferred devices on a
// specific PnP device instance. It is obsolete and handled in
// winmm.dll itself. This cpl should do nothing for this
// command line.
if ((c == TEXT('S')) || (c == TEXT('s'))) {
g_nStartPage = (-1);
break;
}
}
g_nStartPage = 0;
break;
// For sound events, the passed in parameter indicates a module
// name and event. If a name is passed only show it's sound events.
// If a name and event are passed only show the event.
/* case 1:
if (lParam2 && *((LPTSTR)lParam2))
{
TCHAR *psz;
if ((psz = wcschr((LPTSTR)lParam2, TEXT(','))) != NULL)
{
*psz++ = TEXT('\0');
wcsncpy(gszCmdLineEvent, psz, MAX_SND_EVNT_CMD_LINE/sizeof(TCHAR));
gszCmdLineEvent[MAX_SND_EVNT_CMD_LINE-sizeof(TCHAR)] = TEXT('\0');
}
wcsncpy(gszCmdLineApp, (LPTSTR)lParam2,
MAX_SND_EVNT_CMD_LINE/sizeof(TCHAR));
gszCmdLineApp[MAX_SND_EVNT_CMD_LINE-sizeof(TCHAR)] = TEXT('\0');
}
break; */
}
break;
case CPL_EXIT:
DPF_T("* CPL_EXIT*");
g_cRefCnt--;
break;
}
return 0;
}
void PASCAL ShowPropSheet(LPCTSTR pszTitle,
DLGPROC pfnDialog,
UINT idTemplate,
HWND hWndParent,
LPTSTR pszCaption,
LPARAM lParam)
{
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
InitPSH(&psh,hWndParent,pszCaption,hpsp);
AddPage(&psh, pszTitle, pfnDialog, idTemplate, lParam);
PropertySheet(&psh);
}
void PASCAL ShowMidiPropSheet(LPPROPSHEETHEADER ppshExt,
LPCTSTR pszTitle,
HWND hWndParent,
short iMidiPropType,
LPTSTR pszCaption,
HTREEITEM hti,
LPARAM lParam1,
LPARAM lParam2)
{
PROPSHEETHEADER psh;
LPPROPSHEETHEADER ppsh;
HPROPSHEETPAGE hpsp[MAXPAGES];
static EXTPROPSHEETCBPARAM cbp;
if (!ppshExt)
{
ppsh = &psh;
InitPSH(ppsh,hWndParent,pszCaption,hpsp);
}
else
ppsh = ppshExt;
cbp.lParam1 = lParam1;
cbp.lParam2 = lParam2;
cbp.hti = hti;
cbp.ppsh = ppsh;
if (iMidiPropType == MIDI_CLASS_PROP)
{
if (AddMidiPages((LPVOID)pszTitle, MMExtPropSheetCallback, (LPARAM)&cbp))
{
PropertySheet(ppsh);
}
}
else if (iMidiPropType == MIDI_INSTRUMENT_PROP)
{
if (AddInstrumentPages((LPVOID)pszTitle, MMExtPropSheetCallback, (LPARAM)&cbp))
{
PropertySheet(ppsh);
}
}
else
{
if (AddDevicePages((LPVOID)pszTitle, MMExtPropSheetCallback, (LPARAM)&cbp))
{
PropertySheet(ppsh);
}
}
}
void PASCAL ShowWithMidiDevPropSheet(LPCTSTR pszTitle,
DLGPROC pfnDialog,
UINT idTemplate,
HWND hWndParent,
LPTSTR pszCaption,
HTREEITEM hti,
LPARAM lParam, LPARAM lParamExt1, LPARAM lParamExt2)
{
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
InitPSH(&psh,hWndParent,pszCaption,hpsp);
AddPage(&psh, pszTitle, pfnDialog, idTemplate, lParam);
PropertySheet(&psh);
// Disabling the details sheet - obsolete 01/10/2001
//ShowMidiPropSheet(&psh, pszCaption, hWndParent,MIDI_DEVICE_PROP,pszCaption,hti,lParamExt1,lParamExt2);
}
BOOL WINAPI ShowMMCPLPropertySheetW(HWND hwndParent, LPCTSTR pszPropSheetID, LPTSTR pszTabName, LPTSTR pszCaption)
{
DLGPROC pfnDlgProc;
UINT idTemplate;
HWND hwndP;
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
if (GetWindowLongPtr(hwndParent, GWL_EXSTYLE) & WS_EX_TOPMOST)
hwndP = NULL;
else
hwndP = hwndParent;
InitPSH(&psh,hwndP,pszCaption,hpsp);
psh.dwFlags = 0;
if (!lstrcmpi(pszPropSheetID, cszAUDIO))
{
pfnDlgProc = AudioDlg;
idTemplate = AUDIODLG;
goto ShowSheet;
}
if (!lstrcmpi(pszPropSheetID, cszVOICE))
{
pfnDlgProc = VoiceDlg;
idTemplate = VOICEDLG;
goto ShowSheet;
}
if (!lstrcmpi(pszPropSheetID, cszVOLUME))
{
pfnDlgProc = VolumeDlg;
idTemplate = IDD_VOLUME;
goto ShowSheet;
}
if (!lstrcmpi(pszPropSheetID, cszVIDEO))
{
pfnDlgProc = VideoDlg;
idTemplate = VIDEODLG;
goto ShowSheet;
}
if (!lstrcmpi(pszPropSheetID, cszCDAUDIO))
{
pfnDlgProc = CD_HardwareDlgProc;
idTemplate = HWDLG;
goto ShowSheet;
}
if (!lstrcmpi(pszPropSheetID, cszMIDI))
{
/*
static EXTPROPSHEETCBPARAM cbpMIDI;
cbpMIDI.ppsh = &psh;
cbpMIDI.hti = NULL;
cbpMIDI.lParam1 = (LPARAM)pszPropSheetID;
cbpMIDI.lParam2 = (LPARAM)pszTabName;
AddSimpleMidiPages((LPVOID)pszTabName, MMExtPropSheetCallback, (LPARAM)&cbpMIDI);
PropertySheet(&psh);
return TRUE;
*/
pfnDlgProc = AudioDlg;
idTemplate = AUDIODLG;
goto ShowSheet;
}
return FALSE;
ShowSheet:
AddPage(&psh, pszTabName, pfnDlgProc, idTemplate, (LPARAM)NULL);
PropertySheet(&psh);
return TRUE;
}
BOOL WINAPI ShowMMCPLPropertySheet(HWND hwndParent, LPCSTR pszPropSheetID, LPSTR pszTabName, LPSTR pszCaption)
{
DLGPROC pfnDlgProc;
UINT idTemplate;
HWND hwndP;
PROPSHEETHEADER psh;
HPROPSHEETPAGE hpsp[MAXPAGES];
TCHAR szPropSheetID[MAX_PATH];
TCHAR szTabName[MAX_PATH];
TCHAR szCaption[MAX_PATH];
//convert three params into UNICODE strings
MultiByteToWideChar( GetACP(), 0, pszPropSheetID, -1, szPropSheetID, sizeof(szPropSheetID) / sizeof(TCHAR) );
MultiByteToWideChar( GetACP(), 0, pszTabName, -1, szTabName, sizeof(szTabName) / sizeof(TCHAR) );
MultiByteToWideChar( GetACP(), 0, pszCaption, -1, szCaption, sizeof(szCaption) / sizeof(TCHAR) );
return (ShowMMCPLPropertySheetW(hwndParent,szPropSheetID,szTabName,szCaption));
}
//allows you to show control panel from RUNDLL32
DWORD WINAPI ShowFullControlPanel(HWND hwndP, HINSTANCE hInst, LPTSTR szCmd, int nShow)
{
cplMMDoubleClick(hwndP, 0);
return 0;
}
DWORD WINAPI ShowAudioPropertySheet(HWND hwndP, HINSTANCE hInst, LPTSTR szCmd, int nShow)
{
TCHAR szAudio[MAXLNAME];
TCHAR szAudioProperties[MAXLNAME];
char mbcszAUDIO[MAXLNAME];
char mbszAudio[MAXLNAME];
char mbszAudioProperties[MAXLNAME];
HWND hwndPrev;
LoadString(ghInstance, IDS_AUDIOPROPERTIES, szAudioProperties, sizeof(szAudioProperties)/sizeof(TCHAR));
hwndPrev = FindWindow(NULL,szAudioProperties);
if (hwndPrev)
{
SetForegroundWindow(hwndPrev);
}
else
{
LoadString(ghInstance, IDS_WAVE_HEADER, szAudio, sizeof(szAudio)/sizeof(TCHAR));
ShowMMCPLPropertySheetW(hwndP, cszAUDIO, szAudio, szAudioProperties);
}
return 0;
}
DWORD WINAPI mmseRunOnce(HWND hwnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nShow)
{
// This is an obsolete function formerly used to migrate
// registry and driver information. We leave the export
// in place in case an old installation has left a RunOnce
// command in place to execute this function.
return 0;
}
DWORD WINAPI mmseRunOnceW(HWND hwnd, HINSTANCE hInst, LPWSTR lpwszCmdLine, int nShow)
{
// This is an obsolete function formerly used to migrate
// registry and driver information. We leave the export
// in place in case an old installation has left a RunOnce
// command in place to execute this function.
return 0;
}
extern BOOL DriversDllInitialize (IN PVOID, IN DWORD, IN PCONTEXT OPTIONAL);
BOOL DllInitialize (IN PVOID hInstance,
IN DWORD ulReason,
IN PCONTEXT pctx OPTIONAL)
{
// patch in the old DRIVERS.DLL code (see DRIVERS.C)
//
DriversDllInitialize (hInstance, ulReason, pctx);
if (ulReason == DLL_PROCESS_ATTACH)
{
++g_cProcesses;
ghInstance = hInstance;
DisableThreadLibraryCalls(hInstance);
return TRUE;
}
if (ulReason == DLL_PROCESS_DETACH)
{
--g_cProcesses;
return TRUE;
}
return TRUE;
}
DWORD
WINAPI
MediaClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine acts as the class installer for Media devices.
Arguments:
InstallFunction - Specifies the device installer function code indicating
the action being performed.
DeviceInfoSet - Supplies a handle to the device information set being
acted upon by this install action.
DeviceInfoData - Optionally, supplies the address of a device information
element being acted upon by this install action.
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If the default behavior is to be performed for the requested action, the
return value is ERROR_DI_DO_DEFAULT.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
DWORD dwRet=ERROR_DI_DO_DEFAULT;
switch (InstallFunction)
{
case DIF_SELECTBESTCOMPATDRV:
dwRet = Media_SelectBestCompatDrv(DeviceInfoSet,DeviceInfoData);
break;
case DIF_ALLOW_INSTALL:
dwRet = Media_AllowInstall(DeviceInfoSet,DeviceInfoData);
break;
case DIF_INSTALLDEVICE :
dwRet = Media_InstallDevice(DeviceInfoSet, DeviceInfoData);
break;
case DIF_REMOVE:
dwRet = Media_RemoveDevice(DeviceInfoSet,DeviceInfoData);
break;
case DIF_SELECTDEVICE:
dwRet = Media_SelectDevice(DeviceInfoSet,DeviceInfoData);
break;
case DIF_FIRSTTIMESETUP:
// Fall through
case DIF_DETECT:
dwRet = Media_MigrateLegacy(DeviceInfoSet,DeviceInfoData);
break;
}
return dwRet;
}
DWORD WINAPI mmWOW64MediaInstallDevice(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData)
{
SP_DEVINSTALL_PARAMS DeviceInstallParams;
HWND hWnd;
//
// Get the device install parameters, so we'll know what parent window to use for any
// UI that occurs during configuration of this device.
//
DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
if (SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams))
{
hWnd = DeviceInstallParams.hwndParent;
}
else
{
hWnd = NULL;
}
//
// The INF will have created a "Drivers" subkey under the device's software key.
// This tree, in turn, contains subtrees for each type of driver (aux, midi, etc.)
// applicable for this device. We must now traverse this tree, and create entries
// in Drivers32 for each function alias.
//
return InstallDriversForPnPDevice(hWnd, DeviceInfoSet, DeviceInfoData);
}
DWORD WINAPI mmWOW64MediaRemoveDevice(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData)
{
if( RemoveDriver(DeviceInfoSet, DeviceInfoData) )
{
return NO_ERROR;
}
else
{
return ERROR_BAD_DRIVER;
}
}
DWORD WINAPI mmWOW64MediaClassInstallerA(HWND hwnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nShow)
/*++
Routine Description:
This routine acts as a thunking layer for calling the 32-bit
installation functions from the 64-bit setup via RunDLL32.exe.
Arguments:
hwnd - not used
hInst - not used
lpwszCmdLine - command line arguments: "Instance ID (string)" DI_FUNCTION (as an integer)
nShow - not used
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
LPSTR strInstanceID = NULL;
LPSTR strInstallIndex = NULL;
DWORD dwInstallIndex = 0;
LPSTR strTemp = NULL;
HDEVINFO DeviceInfoSet = NULL;
SP_DEVINFO_DATA DeviceInfoData;
DWORD dwResult = NO_ERROR;
// Find first quote
strTemp = strchr( lpszCmdLine, '\"' );
if( !strTemp )
{
return ERROR_INVALID_PARAMETER;
}
// Instance ID
// Skip first quote
strInstanceID = ++strTemp;
// Find second quote
strTemp = strchr( strTemp, '\"' );
if( !strTemp )
{
return ERROR_INVALID_PARAMETER;
}
// NULL-terminate the InstanceID
*strTemp = 0;
// Install Index
// Skip the NULL
strInstallIndex = ++strTemp;
// Convert the installation index
dwInstallIndex = atoi( strInstallIndex );
// Create a device handle
DeviceInfoSet = SetupDiCreateDeviceInfoList( NULL, NULL );
if( INVALID_HANDLE_VALUE == DeviceInfoSet )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
// Create the device info structure
ZeroMemory( &DeviceInfoData, sizeof(SP_DEVINFO_DATA) );
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if( 0 == SetupDiOpenDeviceInfoA( DeviceInfoSet, strInstanceID, NULL, 0, &DeviceInfoData ) )
{
dwResult = GetLastError();
}
// Do the installation task
if( NO_ERROR == dwResult )
{
switch( dwInstallIndex )
{
case DIF_INSTALLDEVICE:
dwResult = mmWOW64MediaInstallDevice(DeviceInfoSet, &DeviceInfoData);
break;
case DIF_REMOVE:
dwResult = mmWOW64MediaRemoveDevice(DeviceInfoSet, &DeviceInfoData);
break;
default:
dwResult = ERROR_INVALID_PARAMETER;
break;
}
}
SetupDiDestroyDeviceInfoList( DeviceInfoSet );
return dwResult;
}