mirror of https://github.com/lianthony/NT4.0
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.
1620 lines
51 KiB
1620 lines
51 KiB
|
|
/*==========================================================================*/
|
|
//
|
|
// 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 "draw.h"
|
|
#include "utils.h"
|
|
#include "drivers.h"
|
|
#include "sulib.h"
|
|
|
|
#ifndef cchRESOURCE
|
|
#define cchRESOURCE 256
|
|
#endif
|
|
|
|
/*
|
|
***************************************************************
|
|
* Globals
|
|
***************************************************************
|
|
*/
|
|
HINSTANCE ghInstance = NULL;
|
|
BOOL gfNukeExt = -1;
|
|
HWND ghwndMsgBox = NULL;
|
|
HWND ghwndAdvProp = NULL;
|
|
|
|
TCHAR gszDevEnabled[ cchRESOURCE ];
|
|
TCHAR gszDevDisabled[ cchRESOURCE ];
|
|
|
|
#ifdef FIX_BUG_15451
|
|
static TCHAR cszFORKLINE[] = TEXT("RUNDLL32 MMSYS.CPL,"
|
|
"ShowDriverSettingsAfterFork %s");
|
|
#endif // FIX_BUG_15451
|
|
|
|
SZCODE cszAUDIO[] = AUDIO;
|
|
SZCODE cszVIDEO[] = VIDEO;
|
|
SZCODE cszCDAUDIO[] = CDAUDIO;
|
|
SZCODE cszMIDI[] = MIDI;
|
|
SZCODE gszServiceInstallSuffix[] = TEXT(".") INFSTR_SUBKEY_SERVICES;
|
|
|
|
/*
|
|
***************************************************************
|
|
* 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
|
|
{
|
|
LPSTR szTitle;
|
|
LPSTR 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 ','
|
|
#define PROPTABSIZE 13
|
|
|
|
#define GetString(_str,_id,_hi) LoadString (_hi, _id, _str, sizeof(_str))
|
|
|
|
/*
|
|
***************************************************************
|
|
* File Globals
|
|
***************************************************************
|
|
*/
|
|
static SZCODE aszSimpleProperties[] = REGSTR_PATH_MEDIARESOURCES "\\MediaExtensions\\shellx\\SimpleProperties\\";
|
|
static SZCODE aszShellName[] = "ShellName";
|
|
|
|
static UINT g_cRefCnt; // keeps track of the ref count
|
|
static int g_cProcesses = 0;
|
|
static int g_nStartPage = 0;
|
|
|
|
/*
|
|
***************************************************************
|
|
* Prototypes
|
|
***************************************************************
|
|
*/
|
|
BOOL CALLBACK AudioDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL CALLBACK VideoDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL CALLBACK CDDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL CALLBACK ACMDlg(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
BOOL CALLBACK SoundDlg(HWND, UINT, WPARAM, LPARAM);
|
|
BOOL CALLBACK AddDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL CALLBACK AdvDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
void LoadStringTable (void);
|
|
|
|
BOOL DriverNodeSupportsNt(LPCTSTR InfFileName, LPCTSTR SectionName);
|
|
|
|
|
|
/*
|
|
***************************************************************
|
|
***************************************************************
|
|
*/
|
|
|
|
int 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 mmse_MessageBox(HWND hwndP, LPSTR szMsg, LPSTR 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(
|
|
LPCSTR lszKey,
|
|
LPCSTR lszClass,
|
|
int iSize)
|
|
{
|
|
char aszKey[64];
|
|
|
|
lstrcpyn(aszKey, lszKey, iSize);
|
|
return lstrcmpi(aszKey, lszClass);
|
|
}
|
|
|
|
int StrByteLen(LPSTR sz)
|
|
{
|
|
LPSTR psz;
|
|
|
|
if (!sz)
|
|
return 0;
|
|
for (psz = sz; *psz; psz = AnsiNext(psz))
|
|
;
|
|
return (int)(psz - sz);
|
|
}
|
|
|
|
static void NukeExt(LPSTR sz)
|
|
{
|
|
int len;
|
|
|
|
len = StrByteLen(sz);
|
|
|
|
if (len > 4 && sz[len-4] == '.')
|
|
sz[len-4] = 0;
|
|
}
|
|
|
|
static LPSTR NukePath(LPSTR sz)
|
|
{
|
|
LPSTR pTmp, pSlash;
|
|
|
|
for(pSlash = pTmp = sz; *pTmp; pTmp = AnsiNext(pTmp))
|
|
{
|
|
if (*pTmp == '\\')
|
|
pSlash = pTmp;
|
|
}
|
|
return (pSlash == sz ? pSlash : pSlash+1);
|
|
}
|
|
|
|
void CheckNukeExtOption(LPSTR sz)
|
|
{
|
|
SHFILEINFO sfi;
|
|
|
|
SHGetFileInfo(sz, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME);
|
|
if (lstrcmpi((LPSTR)(sfi.szDisplayName+lstrlen(sfi.szDisplayName)-4), cszWavExt))
|
|
gfNukeExt = TRUE;
|
|
else
|
|
gfNukeExt = FALSE;
|
|
}
|
|
|
|
LPSTR PASCAL NiceName(LPSTR 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
|
|
{
|
|
LPSTR lpszFileName;
|
|
|
|
lpszFileName = NukePath(sz);
|
|
lstrcpy(lpszFileName, sfi.szDisplayName);
|
|
if (lpszFileName != sz)
|
|
AnsiUpperBuff(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
|
|
* LPSTR lpszDesc - The string to be inserted in the resource string
|
|
*
|
|
* Returns: BOOL
|
|
*
|
|
***************************************************************
|
|
*/
|
|
BOOL PASCAL ErrorBox(HWND hDlg, int iResource, LPSTR lpszDesc)
|
|
{
|
|
char szBuf[MAXMSGLEN];
|
|
char szTitle[MAXSTR];
|
|
char 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)
|
|
{
|
|
char szBuf[MAXMSGLEN];
|
|
char 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(LPSTR szName, LPSTR szPaddedName)
|
|
{
|
|
static SZCODE cszFmt[] = "%s%s%s";
|
|
char szPad[8];
|
|
int i;
|
|
|
|
i = PROPTABSIZE - lstrlen(szName);
|
|
|
|
i = (i <= 0) ? 0 : i/2;
|
|
for (szPad[i] = '\0';i; i--)
|
|
szPad[i-1] = ' ';
|
|
wsprintf(szPaddedName, cszFmt, szPad, szName, szPad);
|
|
}
|
|
|
|
/*==========================================================================*/
|
|
UINT CALLBACK CallbackPage(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
LPPROPSHEETPAGE ppsp)
|
|
{
|
|
if (uMsg == PSPCB_RELEASE) {
|
|
DPF_T("* RelasePage %s *", (LPSTR)ppsp->pszTitle);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*==========================================================================*/
|
|
static BOOL PASCAL NEAR AddPage(
|
|
LPPROPSHEETHEADER ppsh,
|
|
LPCSTR 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 dwParam1, DWORD dwParam2, DWORD 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((LPSTR)dwParam1, (LPSTR)pcbp->lParam2);
|
|
else
|
|
{
|
|
GetTreeItemNodeDesc ((LPSTR)dwParam1,
|
|
(PIRESOURCE)pcbp->lParam1);
|
|
}
|
|
break;
|
|
}
|
|
case MM_EPS_GETNODEID:
|
|
{
|
|
if (!dwParam1)
|
|
return FALSE;
|
|
if (pcbp->hti == NULL)
|
|
lstrcpy((LPSTR)dwParam1, (LPSTR)pcbp->lParam2);
|
|
else
|
|
{
|
|
GetTreeItemNodeID ((LPSTR)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;
|
|
}
|
|
|
|
|
|
/*==========================================================================*/
|
|
static BOOL PASCAL NEAR AddAdvancedPage(
|
|
LPPROPSHEETHEADER ppsh)
|
|
{
|
|
char aszTitleRes[128];
|
|
char szTmp[32];
|
|
|
|
LoadString(ghInstance, IDS_ADVANCED, aszTitleRes, sizeof(aszTitleRes));
|
|
PadWithSpaces((LPSTR)aszTitleRes, (LPSTR)szTmp);
|
|
return AddPage(ppsh, szTmp, AdvDlg, ADVDLG, (LPARAM)NULL);
|
|
}
|
|
|
|
/*==========================================================================*/
|
|
static BOOL PASCAL NEAR AddSchemesPage(
|
|
LPPROPSHEETHEADER ppsh)
|
|
{
|
|
char aszTitleRes[128];
|
|
|
|
LoadString(ghInstance, IDS_EVENTSNAME, aszTitleRes, sizeof(aszTitleRes));
|
|
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 Audio page
|
|
//
|
|
GetString (szText, IDS_AUDIO_TAB, ghInstance);
|
|
PadWithSpaces (szText, szPadded);
|
|
AddPage (ppsh, szPadded, AudioDlg, AUDIODLG, (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, LPSTR 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];
|
|
char strOldDir[MAX_PATH], strSysDir[MAX_PATH];
|
|
|
|
GetSystemDirectory(strSysDir, MAX_PATH);
|
|
GetCurrentDirectory(MAX_PATH, strOldDir);
|
|
SetCurrentDirectory(strSysDir);
|
|
wsInfParseInit();
|
|
|
|
InitCommonControls();
|
|
InitPSH(&psh,hCPlWnd,(LPSTR)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);
|
|
AddAdvancedPage(&psh);
|
|
PropertySheet(&psh);
|
|
|
|
infClose(NULL);
|
|
SetCurrentDirectory(strOldDir);
|
|
}
|
|
|
|
/*==========================================================================*/
|
|
static void PASCAL cplEventsDoubleClick (HWND hCPlWnd)
|
|
{
|
|
PROPSHEETHEADER psh;
|
|
HPROPSHEETPAGE hpsp[MAXPAGES];
|
|
|
|
InitCommonControls();
|
|
RegSndCntrlClass((LPCSTR)DISPFRAMCLASS);
|
|
InitPSH(&psh,hCPlWnd,(LPSTR)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))
|
|
{
|
|
int we_rc;
|
|
TCHAR szForkLine[ cchRESOURCE *2 ];
|
|
|
|
wsprintf (szForkLine, cszFORKLINE, pszName);
|
|
|
|
if ((we_rc = WinExec (szForkLine, SW_SHOW)) < 32)
|
|
{
|
|
ConfigureDriver (hDlg, pszName);
|
|
}
|
|
else
|
|
{
|
|
(void)WaitForNewCPLWindow (hDlg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ConfigureDriver (hDlg, pszName);
|
|
}
|
|
}
|
|
|
|
|
|
void WINAPI ShowDriverSettingsAfterFork (
|
|
HWND hwndStub,
|
|
HINSTANCE hAppInstance,
|
|
LPSTR lpszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
#ifdef UNICODE
|
|
WCHAR szCmdLine[ cchRESOURCE ];
|
|
mbstowcs(szCmdLine, lpszCmdLine, 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
|
|
|
|
|
|
/*==========================================================================*/
|
|
LONG CPlApplet(
|
|
HWND hCPlWnd,
|
|
UINT Msg,
|
|
UINT lParam1,
|
|
LONG lParam2)
|
|
{
|
|
switch (Msg)
|
|
{
|
|
case CPL_INIT:
|
|
wHelpMessage = RegisterWindowMessage("ShellHelp");
|
|
DPF_T("*CPL_INIT*");
|
|
g_cRefCnt++;
|
|
return (LRESULT)TRUE;
|
|
|
|
case CPL_GETCOUNT:
|
|
return (LRESULT)2;
|
|
|
|
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;
|
|
case 1:
|
|
((LPCPLINFO)lParam2)->idIcon = IDI_EVENTSICON;
|
|
((LPCPLINFO)lParam2)->idName = IDS_EVENTSNAME;
|
|
((LPCPLINFO)lParam2)->idInfo = IDS_EVENTSINFO;
|
|
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));
|
|
LoadString(ghInstance, IDS_MMINFO, ((LPNEWCPLINFO)lParam2)->szInfo, sizeof(((LPNEWCPLINFO)lParam2)->szInfo));
|
|
|
|
break;
|
|
case 1:
|
|
((LPNEWCPLINFO)lParam2)->hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_EVENTSICON));
|
|
LoadString(ghInstance, IDS_EVENTSNAME, ((LPNEWCPLINFO)lParam2)->szName, sizeof(((LPNEWCPLINFO)lParam2)->szName));
|
|
LoadString(ghInstance, IDS_EVENTSINFO, ((LPNEWCPLINFO)lParam2)->szInfo, sizeof(((LPNEWCPLINFO)lParam2)->szInfo));
|
|
|
|
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:
|
|
#ifdef FIX_BUG_15451
|
|
lstrcpy (szDriverWhichNeedsSettings, TEXT(""));
|
|
cplMMDoubleClick(hCPlWnd, g_nStartPage);
|
|
#else // FIX_BUG_15451
|
|
cplMMDoubleClick(hCPlWnd);
|
|
#endif // FIX_BUG_15451
|
|
break;
|
|
case 1:
|
|
cplEventsDoubleClick(hCPlWnd);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CPL_STARTWPARMS:
|
|
if (lParam2 && *((LPSTR)lParam2))
|
|
{
|
|
char c;
|
|
|
|
c = *((LPSTR)lParam2);
|
|
if (c > '0' && c < '5')
|
|
{
|
|
g_nStartPage = c - '0';
|
|
break;
|
|
}
|
|
}
|
|
g_nStartPage = 0;
|
|
break;
|
|
|
|
case CPL_EXIT:
|
|
DPF_T("* CPL_EXIT*");
|
|
g_cRefCnt--;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void PASCAL ShowPropSheet(LPCSTR pszTitle,
|
|
DLGPROC pfnDialog,
|
|
UINT idTemplate,
|
|
HWND hWndParent,
|
|
LPSTR 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,
|
|
LPCSTR pszTitle,
|
|
HWND hWndParent,
|
|
short iMidiPropType,
|
|
LPSTR 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(LPCSTR pszTitle,
|
|
DLGPROC pfnDialog,
|
|
UINT idTemplate,
|
|
HWND hWndParent,
|
|
LPSTR 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);
|
|
ShowMidiPropSheet(&psh, pszCaption, hWndParent,MIDI_DEVICE_PROP,pszCaption,hti,lParamExt1,lParamExt2);
|
|
}
|
|
|
|
BOOL WINAPI ShowMMCPLPropertySheet(HWND hwndParent, LPCSTR pszPropSheetID, LPSTR pszTabName, LPSTR pszCaption)
|
|
{
|
|
DLGPROC pfnDlgProc;
|
|
UINT idTemplate;
|
|
HWND hwndP;
|
|
PROPSHEETHEADER psh;
|
|
HPROPSHEETPAGE hpsp[MAXPAGES];
|
|
|
|
|
|
if (GetWindowLong(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, cszVIDEO))
|
|
{
|
|
pfnDlgProc = VideoDlg;
|
|
idTemplate = VIDEODLG;
|
|
goto ShowSheet;
|
|
}
|
|
if (!lstrcmpi(pszPropSheetID, cszCDAUDIO))
|
|
{
|
|
pfnDlgProc = CDDlg;
|
|
idTemplate = CDDLG;
|
|
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;
|
|
}
|
|
|
|
return FALSE;
|
|
ShowSheet:
|
|
AddPage(&psh, pszTabName, pfnDlgProc, idTemplate, (LPARAM)NULL);
|
|
PropertySheet(&psh);
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD WINAPI ShowAudioPropertySheet(HWND hwndP, HINSTANCE hInst, LPSTR szCmd, int nShow)
|
|
{
|
|
char szAudio[MAXLNAME];
|
|
char szAudioProperties[MAXLNAME];
|
|
HWND hwndPrev;
|
|
|
|
LoadString(ghInstance, IDS_AUDIOPROPERTIES, szAudioProperties, sizeof(szAudioProperties));
|
|
hwndPrev = FindWindow(NULL,szAudioProperties);
|
|
if (hwndPrev)
|
|
{
|
|
SetForegroundWindow(hwndPrev);
|
|
}
|
|
else
|
|
{
|
|
LoadString(ghInstance, IDS_WAVE_HEADER, szAudio, sizeof(szAudio));
|
|
ShowMMCPLPropertySheet(hwndP, cszAUDIO, szAudio, szAudioProperties);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
STATIC void MakeWin31CompatSchemes(LPSTR szSchemeList)
|
|
{
|
|
typedef struct _FileList * PFILELIST;
|
|
typedef struct _FileList
|
|
{
|
|
PFILELIST pNext;
|
|
LPSTR pszOFN;
|
|
LPSTR pszLFN;
|
|
char szFiles[1];
|
|
} FILELIST;
|
|
|
|
HKEY hk = NULL;
|
|
PFILELIST pflHead = NULL;
|
|
PFILELIST pfl;
|
|
static SZCODE cszRenamePath[] = REGSTR_PATH_SETUP "\\RenameFiles\\%s";
|
|
char szRename[MAX_PATH];
|
|
LPSTR pszNextScheme;
|
|
|
|
for (pszNextScheme = szSchemeList; pszNextScheme && *pszNextScheme; pszNextScheme += lstrlen(pszNextScheme) + 1)
|
|
{
|
|
wsprintf(szRename, cszRenamePath, pszNextScheme);
|
|
|
|
if (!RegOpenKey(HKEY_LOCAL_MACHINE, szRename, &hk))
|
|
{
|
|
DWORD dwIndex, dwType, cbOFN, cbLFN, cbFolder;
|
|
char szOFN[16];
|
|
char szLFN[MAX_PATH];
|
|
char szFolder[MAX_PATH];
|
|
|
|
cbFolder = sizeof(szFolder);
|
|
if (RegQueryValue(hk, NULL, szFolder, &cbFolder) && cbFolder <= 2)
|
|
{
|
|
RegCloseKey(hk);
|
|
continue;
|
|
}
|
|
cbOFN = sizeof(szOFN);
|
|
cbLFN = sizeof(szLFN);
|
|
for (dwIndex = 0; !RegEnumValue(hk, dwIndex, szOFN, &cbOFN, 0, &dwType, szLFN, &cbLFN); dwIndex++)
|
|
{
|
|
if (cbOFN > 2 && cbLFN > 2 && dwType == REG_SZ)
|
|
{
|
|
static SZCODE cszCat[] = "%s\\%s";
|
|
|
|
pfl = (PFILELIST)LocalAlloc(LPTR, sizeof(FILELIST) + cbOFN + cbLFN + 2 * (cbFolder + 3));
|
|
if (!pfl)
|
|
{
|
|
RegCloseKey(hk);
|
|
goto Exit;
|
|
}
|
|
DPF("Adding to LIST: %s ** %s\r\n", szOFN, szLFN);
|
|
pfl->pszOFN = pfl->szFiles;
|
|
wsprintf(pfl->pszOFN, cszCat, szFolder, szOFN);
|
|
pfl->pszLFN = pfl->pszOFN + lstrlen(pfl->pszOFN) + 1;
|
|
wsprintf(pfl->pszLFN, cszCat, szFolder, szLFN);
|
|
pfl->pNext = pflHead;
|
|
pflHead = pfl;
|
|
}
|
|
cbOFN = sizeof(szOFN);
|
|
cbLFN = sizeof(szLFN);
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
}
|
|
|
|
if (pflHead)
|
|
{
|
|
static SZCODE cszSchemeRoot[] = REGSTR_PATH_APPS;
|
|
HKEY hkApp, hkEvent, hkScheme;
|
|
DWORD dwApp, dwEvent, dwScheme;
|
|
char szApp[MAXSTR], szEvent[MAXSTR], szScheme[MAXSTR], szFile[MAX_PATH];
|
|
DWORD cbApp, cbEvent, cbScheme, cbFile;
|
|
|
|
if (!RegOpenKey(HKEY_CURRENT_USER, cszSchemeRoot, &hkApp))
|
|
{
|
|
cbApp = sizeof(szApp);
|
|
for (dwApp = 0; !RegEnumKey(hkApp, dwApp, szApp, cbApp); dwApp++)
|
|
{
|
|
DPF("\r\n====APP = %s ====\r\n", szApp);
|
|
if (!RegOpenKey(hkApp, szApp, &hkEvent))
|
|
{
|
|
cbEvent = sizeof(szEvent);
|
|
for (dwEvent = 0; !RegEnumKey(hkEvent, dwEvent, szEvent, cbEvent); dwEvent++)
|
|
{
|
|
DPF("\r\n=======EVENT = %s =====\r\n", szEvent);
|
|
|
|
if (!RegOpenKey(hkEvent, szEvent, &hkScheme))
|
|
{
|
|
cbScheme = sizeof(szScheme);
|
|
for (dwScheme = 0; !RegEnumKey(hkScheme, dwScheme, szScheme, cbScheme); dwScheme++)
|
|
{
|
|
DPF("======SCHEME = %s =====\r\n", szScheme);
|
|
|
|
cbFile = sizeof(szFile);
|
|
if (!RegQueryValue(hkScheme, szScheme, szFile, &cbFile) && cbFile > 2)
|
|
{
|
|
for (pfl = pflHead; pfl; pfl = pfl->pNext)
|
|
{
|
|
if (!lstrcmpi(pfl->pszLFN, szFile))
|
|
{
|
|
RegSetValue(hkScheme, szScheme, REG_SZ, pfl->pszOFN, lstrlen(pfl->pszOFN) +1);
|
|
DPF("Replacing %s ** %s \r\n", szFile, pfl->pszOFN);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hkScheme);
|
|
}
|
|
}
|
|
RegCloseKey(hkEvent);
|
|
}
|
|
}
|
|
RegCloseKey(hkApp);
|
|
}
|
|
}
|
|
Exit:
|
|
if (pflHead)
|
|
{
|
|
PFILELIST pflTmp;
|
|
|
|
pfl = pflHead;
|
|
while(pfl)
|
|
{
|
|
pflTmp = pfl->pNext;
|
|
LocalFree((HLOCAL)pfl);
|
|
pfl = pflTmp;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void CheckForOFNSoundEvents(void)
|
|
{
|
|
static SZCODE cszFileSystem[] = REGSTR_PATH_FILESYSTEM;
|
|
static SZCODE cszWin31FileSystem[] = REGSTR_VAL_WIN31FILESYSTEM;
|
|
static SZCODE cszRealModeNet[] = REGSTR_PATH_REALMODENET;
|
|
static SZCODE cszSetupN[] = REGSTR_VAL_SETUPN;
|
|
static SZCODE cszNewSchemes[] = REGSTR_PATH_SCHEMES "\\NewSchemes";
|
|
BOOL fOFNSetup = FALSE;
|
|
HKEY hk;
|
|
DWORD dwVal;
|
|
DWORD cbLen;
|
|
|
|
if (!RegOpenKey(HKEY_LOCAL_MACHINE, cszFileSystem, &hk))
|
|
{
|
|
cbLen = sizeof(DWORD);
|
|
if(!RegQueryValueEx(hk, cszWin31FileSystem, (LPDWORD)NULL, (LPDWORD)NULL, (LPBYTE)&dwVal, &cbLen) && dwVal)
|
|
{
|
|
fOFNSetup = TRUE;
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
if (!fOFNSetup)
|
|
{
|
|
if (!RegOpenKey(HKEY_LOCAL_MACHINE, cszRealModeNet, &hk))
|
|
{
|
|
cbLen = sizeof(DWORD);
|
|
if(!RegQueryValueEx(hk, cszSetupN, (LPDWORD)NULL, (LPDWORD)NULL, (LPBYTE)&dwVal, &cbLen) && dwVal)
|
|
{
|
|
fOFNSetup = TRUE;
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
}
|
|
|
|
if(fOFNSetup)
|
|
{
|
|
char szSchemeList[MAXSTR];
|
|
HKEY hkNewSchemes;
|
|
LPSTR pszEnd = (LPSTR)(szSchemeList + sizeof(szSchemeList));
|
|
LPSTR pszNext;
|
|
|
|
if (!RegOpenKey(HKEY_CURRENT_USER, cszNewSchemes, &hkNewSchemes))
|
|
{
|
|
DWORD dwIndex;
|
|
char szScheme[MAXSTR];
|
|
int iLen;
|
|
|
|
pszNext = szSchemeList;
|
|
for (dwIndex = 0; !RegEnumKey(hkNewSchemes, dwIndex, szScheme, sizeof(szScheme)); dwIndex++)
|
|
{
|
|
iLen = lstrlen(szScheme);
|
|
|
|
DPF("Adding Scheme: %s **\r\n", szScheme);
|
|
if (pszNext + iLen + 2 < pszEnd)
|
|
{
|
|
lstrcpy(pszNext, szScheme);
|
|
pszNext += iLen + 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
*pszNext = 0;
|
|
DPF("Scheme List = %s\r\n", szSchemeList);
|
|
RegCloseKey(hkNewSchemes);
|
|
MakeWin31CompatSchemes(szSchemeList);
|
|
}
|
|
}
|
|
RegDeleteKey(HKEY_CURRENT_USER, cszNewSchemes);
|
|
}
|
|
|
|
|
|
// Migrates all Midi Drivers
|
|
// as well as initializes users Midi schemes
|
|
typedef void (*MIGRATEFUNC)(void);
|
|
DWORD WINAPI mmseRunOnce_Common(HWND hwnd, HINSTANCE hInst, LPTSTR szCmd, int nShow)
|
|
{
|
|
static SZCODE cszCheckOFN[] = "CHECK_OFN";
|
|
HMODULE hModule;
|
|
MIGRATEFUNC fnMigrate;
|
|
|
|
CheckForOFNSoundEvents();
|
|
if (!szCmd || lstrcmpi(szCmd, cszCheckOFN))
|
|
{
|
|
hModule = GetModuleHandle (TEXT ("winmm.dll"));
|
|
if (hModule)
|
|
{
|
|
fnMigrate = (MIGRATEFUNC)GetProcAddress (hModule, "MigrateAllDrivers");
|
|
if (fnMigrate)
|
|
{
|
|
(*fnMigrate)();
|
|
RunOnceSchemeInit(hwnd, hInst, szCmd, nShow);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hModule = (HMODULE)LoadLibrary (TEXT ("winmm.dll"));
|
|
if (hModule)
|
|
{
|
|
fnMigrate = (MIGRATEFUNC)GetProcAddress (hModule, "MigrateAllDrivers");
|
|
if (fnMigrate)
|
|
{
|
|
(*fnMigrate)();
|
|
RunOnceSchemeInit(hwnd, hInst, szCmd, nShow);
|
|
}
|
|
FreeLibrary (hModule);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // End mmseRunOnce
|
|
|
|
DWORD WINAPI mmseRunOnce(HWND hwnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nShow)
|
|
{
|
|
#ifdef UNICODE
|
|
UINT iLen = lstrlenA(lpszCmdLine)+1;
|
|
LPWSTR lpwszCmdLine;
|
|
DWORD dwResult;
|
|
|
|
lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR,iLen*SIZEOF(WCHAR));
|
|
if (lpwszCmdLine)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
lpszCmdLine, -1,
|
|
lpwszCmdLine, iLen);
|
|
dwResult = mmseRunOnce_Common( hwnd,
|
|
hInst,
|
|
lpwszCmdLine,
|
|
nShow );
|
|
LocalFree(lpwszCmdLine);
|
|
return dwResult;
|
|
} else {
|
|
return 0;
|
|
}
|
|
#else
|
|
return mmseRunOnce_Common( hwnd,
|
|
hInst,
|
|
lpszCmdLine,
|
|
nShow );
|
|
#endif
|
|
}
|
|
|
|
DWORD WINAPI mmseRunOnceW(HWND hwnd, HINSTANCE hInst, LPWSTR lpwszCmdLine, int nShow)
|
|
{
|
|
#ifdef UNICODE
|
|
return mmseRunOnce_Common( hwnd,
|
|
hInst,
|
|
lpwszCmdLine,
|
|
nShow );
|
|
#else
|
|
UINT iLen = WideCharToMultiByte(CP_ACP, 0,
|
|
lpwszCmdLine, -1,
|
|
NULL, 0, NULL, NULL) + 1;
|
|
LPSTR lpszCmdLine;
|
|
DWORD dwResult;
|
|
|
|
lpszCmdLine = (LPSTR)LocalAlloc(LPTR,iLen);
|
|
if (lpszCmdLine)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
lpwszCmdLine, -1,
|
|
lpszCmdLine, iLen,
|
|
NULL, NULL);
|
|
dwResult = mmseRunOnce_Common( hwnd,
|
|
hInst,
|
|
lpszCmdLine,
|
|
nShow );
|
|
LocalFree(lpszCmdLine);
|
|
return dwResult;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// Adds mmseRunOnce to Registry for next reboot
|
|
BOOL WINAPI SetRunOnceSchemeInit (void)
|
|
{
|
|
static const TCHAR aszRunOnce[] = REGSTR_PATH_RUNONCE;
|
|
static const TCHAR aszName[] = TEXT ("MigrateMMDrivers");
|
|
static const TCHAR aszCommand[] = TEXT ("rundll32.exe mmsys.cpl,mmseRunOnce");
|
|
|
|
HKEY hKey;
|
|
DWORD cbSize;
|
|
|
|
if (ERROR_SUCCESS != RegCreateKeyEx (HKEY_LOCAL_MACHINE, aszRunOnce, 0, NULL, 0,
|
|
KEY_ALL_ACCESS, NULL, &hKey, NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
cbSize = sizeof (aszCommand);
|
|
if (ERROR_SUCCESS != RegSetValueEx (hKey, aszName, 0, REG_SZ,
|
|
(LPBYTE)(LPVOID)aszCommand, cbSize))
|
|
{
|
|
RegCloseKey (hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
return TRUE;
|
|
} // End SetRunOnce
|
|
|
|
|
|
/*
|
|
**** LoadStringTable - Preloads some frequently-used string resources
|
|
*
|
|
*/
|
|
|
|
void LoadStringTable (void)
|
|
{
|
|
if (gszDevEnabled[0] != '\0')
|
|
return;
|
|
|
|
GetString (gszDevEnabled, IDS_DEVENABLEDOK, ghInstance);
|
|
GetString (gszDevDisabled, IDS_DEVDISABLED, ghInstance);
|
|
}
|
|
|
|
|
|
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;
|
|
LoadStringTable ();
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
SP_DRVINFO_DATA DriverInfoData;
|
|
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
|
|
DWORD Err, ConfigFlags;
|
|
SP_DEVINSTALL_PARAMS DeviceInstallParams;
|
|
HWND hWnd;
|
|
|
|
switch(InstallFunction) {
|
|
|
|
case DIF_INSTALLDEVICE :
|
|
//
|
|
// Make sure before doing anything else that there is no service property for
|
|
// this device instance. This allows us to know whether we should clean up the
|
|
// device instance if we boot and find that it's no longer present.
|
|
//
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, NULL, 0);
|
|
|
|
//
|
|
// First, verify that the driver node selected for this device supports
|
|
// NT. It will probably be a pretty common scenario for users to try to
|
|
// give us their Win95 INFs. These INFs can put lots of spooge in the
|
|
// registry that causes weird popups later on.
|
|
//
|
|
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
|
|
if(!SetupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData, &DriverInfoData)) {
|
|
//
|
|
// The NULL driver is to be installed for this device. We don't need to
|
|
// do anything special in that case.
|
|
//
|
|
return ERROR_DI_DO_DEFAULT;
|
|
}
|
|
|
|
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
|
|
if(!SetupDiGetDriverInfoDetail(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
&DriverInfoData,
|
|
&DriverInfoDetailData,
|
|
sizeof(DriverInfoDetailData),
|
|
NULL) &&
|
|
((Err = GetLastError()) != ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
return Err;
|
|
}
|
|
|
|
if(!DriverNodeSupportsNt(DriverInfoDetailData.InfFileName, DriverInfoDetailData.SectionName)) {
|
|
return ERROR_SECTION_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// OK, we have an honest-to-goodness NT INF. Perform the default install action.
|
|
//
|
|
if(!SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData)) {
|
|
|
|
Err = GetLastError();
|
|
|
|
//
|
|
// In certain circumstances, we have INFs that control some of the functions on the
|
|
// card, but not all (e.g., our sndblst driver controls wave, midi, aux, mixer but
|
|
// not the fancy 3D stuff). In order to give the user a descriptive name that lets
|
|
// them know what we're trying to install, the INF contains driver nodes for devices
|
|
// it can't support. If this is the case, then SetupDiInstallDevice will fail with
|
|
// ERROR_NO_ASSOCIATED_SERVICE. If this happens, we want to clear the
|
|
// CONFIGFLAG_REINSTALL that got set, so we don't keep hounding the user about this.
|
|
// While we're at it, we go ahead and store the driver node's device description as
|
|
// the device instance's description, so that we know what the device instances are
|
|
// later on (for diagnostic purposes, mainly).
|
|
//
|
|
if(Err == ERROR_NO_ASSOCIATED_SERVICE) {
|
|
|
|
if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
NULL,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags),
|
|
NULL))
|
|
{
|
|
ConfigFlags &= ~CONFIGFLAG_REINSTALL;
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags)
|
|
);
|
|
}
|
|
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_DEVICEDESC,
|
|
(PBYTE)DriverInfoData.Description,
|
|
(lstrlen(DriverInfoData.Description) + 1) * sizeof(TCHAR)
|
|
);
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
//
|
|
// 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(SP_DEVINSTALL_PARAMS);
|
|
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.
|
|
//
|
|
if((Err = InstallDriversForPnPDevice(hWnd, DeviceInfoSet, DeviceInfoData)) != NO_ERROR) {
|
|
//
|
|
// The device is in an unknown state. Disable it by setting the
|
|
// CONFIGFLAG_DISABLED config flag, and mark it as needing a reinstall.
|
|
//
|
|
if(!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
NULL,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags),
|
|
NULL))
|
|
{
|
|
ConfigFlags = 0;
|
|
}
|
|
|
|
ConfigFlags |= (CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL);
|
|
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
SPDRP_CONFIGFLAGS,
|
|
(PBYTE)&ConfigFlags,
|
|
sizeof(ConfigFlags)
|
|
);
|
|
|
|
//
|
|
// Delete the Driver= entry from the Dev Reg Key and delete the
|
|
// DrvRegKey.
|
|
//
|
|
SetupDiDeleteDevRegKey(DeviceInfoSet,
|
|
DeviceInfoData,
|
|
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
|
|
0,
|
|
DIREG_DRV
|
|
);
|
|
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_DRIVER, NULL, 0);
|
|
|
|
//
|
|
// Also, delete the service property, so we'll know this device instance needs to be
|
|
// cleaned up if we later reboot and don't find the device.
|
|
//
|
|
SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, NULL, 0);
|
|
|
|
return Err;
|
|
}
|
|
|
|
SetRunOnceSchemeInit();
|
|
|
|
return NO_ERROR;
|
|
|
|
default :
|
|
//
|
|
// Just do the default action.
|
|
//
|
|
return ERROR_DI_DO_DEFAULT;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DriverNodeSupportsNt(
|
|
IN LPCTSTR InfFileName,
|
|
IN LPCTSTR SectionName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether the driver node specified is capable of
|
|
installing on Windows NT (as opposed to being a Win95-only driver node).
|
|
This determination is made based upon whether or not there is a corresponding
|
|
service install section for this device install section.
|
|
|
|
Arguments:
|
|
|
|
InfFileName - Supplies the fully-qualified path of the INF file containing
|
|
the driver node.
|
|
|
|
SectionName - Supplies the install section name for the driver node.
|
|
|
|
Return Value:
|
|
|
|
If the driver node supports Windows NT, the return value is TRUE, otherwise
|
|
it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
HINF hInf;
|
|
TCHAR ActualSectionName[255];
|
|
DWORD ActualSectionNameLen;
|
|
LONG LineCount;
|
|
|
|
//
|
|
// Open the associated INF file.
|
|
//
|
|
if((hInf = SetupOpenInfFile(InfFileName, NULL, INF_STYLE_WIN4, NULL)) == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Retrieve the actual name of the install section to be used for this
|
|
// driver node.
|
|
//
|
|
SetupDiGetActualSectionToInstall(hInf,
|
|
SectionName,
|
|
ActualSectionName,
|
|
sizeof(ActualSectionName) / sizeof(TCHAR),
|
|
&ActualSectionNameLen,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Generate the service install section name, and see if it exists.
|
|
//
|
|
CopyMemory(&(ActualSectionName[ActualSectionNameLen - 1]),
|
|
gszServiceInstallSuffix,
|
|
sizeof(gszServiceInstallSuffix)
|
|
);
|
|
|
|
LineCount = SetupGetLineCount(hInf, ActualSectionName);
|
|
|
|
SetupCloseInfFile(hInf);
|
|
|
|
return (LineCount != -1);
|
|
}
|
|
|