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.
5126 lines
156 KiB
5126 lines
156 KiB
/* DRIVERS.C
|
|
**
|
|
** Copyright (C) Microsoft, 1990, All Rights Reserved.
|
|
**
|
|
** Multimedia Control Panel Applet for installing/configuring installable
|
|
** device drivers. See the ispec doc DRIVERS.DOC for more information.
|
|
**
|
|
** History:
|
|
**
|
|
** Tue Jul 31 1990 -by- MichaelE
|
|
** Created.
|
|
**
|
|
** Thu Oct 25 1990 -by- MichaelE
|
|
** Added restart, horz. scroll, added SKIPDESC reading desc. strings.
|
|
**
|
|
** Sat Oct 27 1990 -by- MichaelE
|
|
** Added FileCopy. Uses SULIB.LIB and LZCOPY.LIB. Finished stuff
|
|
** for case of installing a driver with more than one type.
|
|
**
|
|
** May 1991 -by- JohnYG
|
|
** Added and replaced too many things to list. Better management
|
|
** of removed drivers, correct usage of DRV_INSTALL/DRV_REMOVE,
|
|
** installing VxD's, replaced "Unknown" dialog with an OEMSETUP.INF
|
|
** method, proper "Cancel" method, fixed many potential UAE's.
|
|
*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntseapi.h>
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <cpl.h>
|
|
#include <cphelp.h>
|
|
#include <commctrl.h>
|
|
#include <mmcpl.h>
|
|
#include <mmddkp.h>
|
|
#include <mmreg.h>
|
|
#include <msacm.h>
|
|
#include <msacmdrv.h>
|
|
#include <regstr.h>
|
|
|
|
#include "drivers.h"
|
|
#include "sulib.h"
|
|
#include "utils.h"
|
|
#include "medhelp.h"
|
|
#include "midi.h"
|
|
#ifdef FIX_BUG_15451
|
|
#include "trayvol.h"
|
|
#endif // FIX_BUG_15451
|
|
|
|
#ifndef cchLENGTH
|
|
#define cchLENGTH(_sz) (sizeof(_sz) / sizeof((_sz)[0]))
|
|
#endif
|
|
|
|
#ifndef TreeView_GetGrandParent
|
|
#define TreeView_GetGrandParent(_htree,_hti) \
|
|
TreeView_GetParent(_htree,TreeView_GetParent(_htree,_hti))
|
|
#endif
|
|
|
|
/*
|
|
* Enable the definition below to cause MCI devices to be listed by their
|
|
* internal descriptions in the tree, rather than their descriptions as
|
|
* read from Drivers.Desc.
|
|
*
|
|
*/
|
|
// #define GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES
|
|
|
|
typedef struct
|
|
{
|
|
int idIcon;
|
|
int idName;
|
|
int idInfo;
|
|
BOOL bEnabled;
|
|
DWORD dwContext;
|
|
LPTSTR pszHelp;
|
|
} APPLET_INFO;
|
|
|
|
#define NUM_APPLETS 1
|
|
#define OBJECT_SIZE 1024
|
|
|
|
BOOL bBadOemSetup;
|
|
BOOL bRestart = FALSE;
|
|
int iRestartMessage = 0;
|
|
BOOL bInstallBootLine = FALSE;
|
|
BOOL bCopyVxD;
|
|
BOOL bFindOEM = FALSE;
|
|
BOOL bRelated = FALSE;
|
|
BOOL bCopyEvenIfOlder = FALSE;
|
|
BOOL bDriversAppInUse;
|
|
BOOL bCopyingRelated;
|
|
BOOL bDescFileValid;
|
|
HANDLE myInstance;
|
|
HWND hAdvDlgTree;
|
|
UINT wHelpMessage;
|
|
DWORD dwContext;
|
|
PINF pinfOldDefault = NULL;
|
|
TCHAR szDriversHlp[24];
|
|
TCHAR szLastQuery[20];
|
|
TCHAR szSetupInf[18];
|
|
TCHAR szKnown[250];
|
|
TCHAR szRestartDrv[MAXDRVSTR]; // Warning - Making this string longer could cause a buffer overflow
|
|
TCHAR szUnlisted[150];
|
|
TCHAR szRelatedDesc[30];
|
|
TCHAR szAppName[26];
|
|
TCHAR szDrivers[12];
|
|
TCHAR szRemove[12];
|
|
TCHAR szControlIni[20];
|
|
TCHAR szSysIni[20];
|
|
TCHAR szMCI[6];
|
|
TCHAR szOutOfRemoveSpace[54];
|
|
TCHAR szDriversDesc[38];
|
|
TCHAR szUserDrivers[38];
|
|
|
|
// Where the source of files to copy is - user updates
|
|
|
|
TCHAR szDirOfSrc[MAX_PATH];
|
|
TCHAR szAddDriver[36];
|
|
TCHAR szNoDesc[36];
|
|
TCHAR szError[20];
|
|
TCHAR szRemoveOrNot[250];
|
|
TCHAR szRemoveOrNotStrict[250];
|
|
TCHAR szStringBuf[128];
|
|
TCHAR szMDrivers[38];
|
|
TCHAR szMDrivers32[38];
|
|
TCHAR szFullPath[MAXFILESPECLEN];
|
|
TCHAR szSystem[MAX_PATH];
|
|
TCHAR szOemInf[MAX_PATH];
|
|
TCHAR aszClose[16];
|
|
TCHAR szFileError[50];
|
|
|
|
#ifdef FIX_BUG_15451
|
|
TCHAR szDriverWhichNeedsSettings[MAX_PATH]; // See MMCPL.C
|
|
#endif // FIX_BUG_15451
|
|
|
|
static HANDLE hIList;
|
|
static HANDLE hWndMain;
|
|
|
|
/*
|
|
* Global flag telling us if we're allowed to write to ini files
|
|
*/
|
|
|
|
BOOL IniFileReadAllowed;
|
|
BOOL IniFileWriteAllowed;
|
|
|
|
|
|
/*
|
|
*** Stuff for keeping track of the TreeView window
|
|
*
|
|
*/
|
|
|
|
#define GetString(_psz,_id) LoadString(myInstance,(_id),(_psz),sizeof((_psz))/sizeof(TCHAR))
|
|
|
|
static struct // aDriverKeyword
|
|
{
|
|
LPTSTR psz; // text found as alias for driver
|
|
DriverClass dc; // DriverClass inferred from keyword
|
|
}
|
|
aDriverKeyword[] = // (used by GuessDriverClass())
|
|
{
|
|
{ TEXT("waveaudio"), dcMCI }, // (sort in inverse alphabetical;
|
|
{ TEXT("wavemap"), dcWAVE }, // in particular, longer names first)
|
|
{ TEXT("wave"), dcWAVE },
|
|
{ TEXT("vids"), dcVCODEC },
|
|
{ TEXT("vidc"), dcVCODEC },
|
|
{ TEXT("sequencer"), dcMCI },
|
|
{ TEXT("msvideo"), dcVIDCAP },
|
|
{ TEXT("msacm"), dcACODEC },
|
|
{ TEXT("mpegvideo"), dcMCI },
|
|
{ TEXT("mixer"), dcMIXER },
|
|
{ TEXT("midimapper"), dcMIDI },
|
|
{ TEXT("midi"), dcMIDI },
|
|
{ TEXT("mci"), dcMCI },
|
|
{ TEXT("icm"), dcVCODEC },
|
|
{ TEXT("cdaudio"), dcMCI },
|
|
{ TEXT("avivideo"), dcMCI },
|
|
{ TEXT("aux"), dcAUX },
|
|
{ TEXT("acm"), dcACODEC },
|
|
{ TEXT("joy"), dcJOY }
|
|
};
|
|
|
|
#define nDriverKEYWORDS ((int)(sizeof(aDriverKeyword) / \
|
|
sizeof(aDriverKeyword[0])))
|
|
|
|
static struct // aKeywordDesc
|
|
{
|
|
DriverClass dc; // DriverClass
|
|
LPTSTR psz; // alias which best describes class
|
|
}
|
|
aKeywordDesc[] = // (used by DriverClassToClassNode())
|
|
{
|
|
{ dcWAVE, TEXT("wave") },
|
|
{ dcMIXER, TEXT("mixer") },
|
|
{ dcVIDCAP, TEXT("msvideo") },
|
|
{ dcVCODEC, TEXT("icm") },
|
|
{ dcAUX, TEXT("aux") },
|
|
{ dcACODEC, TEXT("acm") },
|
|
{ dcMIDI, TEXT("midi") },
|
|
{ dcJOY, TEXT("joystick") }
|
|
};
|
|
|
|
#define nKeywordDESCS ((int)(sizeof(aKeywordDesc) / \
|
|
sizeof(aKeywordDesc[0])))
|
|
|
|
static struct // aDriverRoot
|
|
{
|
|
DriverClass dc; // corresponding driver classification
|
|
BOOL fAlwaysMake; // TRUE if should exist even w/o child
|
|
int idIcon; // icon for items under this tree
|
|
int idDesc; // description string for parent
|
|
int idEnable; // string to describe enabling action
|
|
int idDisable; // string to describe disabling action
|
|
HTREEITEM hti; // item within tree
|
|
DWORD dwBit; // bit mask representing this node
|
|
}
|
|
aDriverRoot[] = // (order will define order in display)
|
|
{
|
|
{ dcINVALID, TRUE, IDI_MMICON, IDS_MM_HEADER,
|
|
0,
|
|
0 },
|
|
{ dcWAVE, TRUE, IDI_WAVE, IDS_WAVE_HEADER,
|
|
IDS_ENABLEAUDIO,
|
|
IDS_DISABLEAUDIO },
|
|
{ dcMIDI, TRUE, IDI_MIDI, IDS_MIDI_HEADER,
|
|
IDS_ENABLEMIDI,
|
|
IDS_DISABLEMIDI },
|
|
{ dcMIXER, TRUE, IDI_MIXER, IDS_MIXER_HEADER,
|
|
IDS_ENABLEMIXER,
|
|
IDS_DISABLEMIXER },
|
|
{ dcAUX, TRUE, IDI_AUX, IDS_AUX_HEADER,
|
|
IDS_ENABLEAUX,
|
|
IDS_DISABLEAUX },
|
|
{ dcMCI, TRUE, IDI_MCI, IDS_MCI_HEADER,
|
|
IDS_ENABLEMCI,
|
|
IDS_DISABLEMCI },
|
|
{ dcVCODEC, TRUE, IDI_ICM, IDS_ICM_HEADER,
|
|
IDS_ENABLEICM,
|
|
IDS_DISABLEICM },
|
|
{ dcACODEC, TRUE, IDI_ACM, IDS_ACM_HEADER,
|
|
IDS_ENABLEACM,
|
|
IDS_DISABLEACM },
|
|
{ dcVIDCAP, TRUE, IDI_VIDEO, IDS_VIDCAP_HEADER,
|
|
IDS_ENABLECAP,
|
|
IDS_DISABLECAP },
|
|
{ dcJOY, TRUE, IDI_JOYSTICK,IDS_JOYSTICK_HEADER,
|
|
IDS_ENABLEJOY,
|
|
IDS_DISABLEJOY },
|
|
{ dcOTHER, FALSE, IDI_MMICON, IDS_OTHER_HEADER,
|
|
IDS_ENABLEJOY,
|
|
IDS_DISABLEJOY },
|
|
};
|
|
|
|
#define nDriverROOTS ((int)(sizeof(aDriverRoot) / sizeof(aDriverRoot[0])))
|
|
|
|
static LPCTSTR aDriversToSKIP[] =
|
|
{
|
|
TEXT( "MMDRV.DLL" ),
|
|
TEXT( "MIDIMAP.DLL" ),
|
|
TEXT( "MSACM32.DRV" )
|
|
};
|
|
|
|
|
|
static TCHAR cszMMDRVDLL[] = TEXT("MMDRV.DLL");
|
|
static TCHAR cszAliasKERNEL[] = TEXT("KERNEL");
|
|
static TCHAR cszRegValueLOADTYPE[] = TEXT("Load Type");
|
|
|
|
#define nDriversToSKIP ((int)( sizeof(aDriversToSKIP) \
|
|
/ sizeof(aDriversToSKIP[0]) ))
|
|
|
|
static HIMAGELIST hImageList = NULL; // image list for treeview in advdlg
|
|
DriverClass g_dcFilterClass = dcINVALID;
|
|
|
|
short DriverClassToRootIndex (DriverClass);
|
|
DriverClass GuessDriverClass (PIDRIVER);
|
|
#ifdef FIX_BUG_15451
|
|
DriverClass GuessDriverClassFromAlias (LPTSTR);
|
|
#endif // FIX_BUG_15451
|
|
DriverClass GuessDriverClassFromTreeItem (HTREEITEM hti);
|
|
BOOL EnsureRootIndexExists (HWND, short);
|
|
HTREEITEM AdvDlgFindTopLevel (void);
|
|
BOOL InitAdvDlgTree (HWND);
|
|
void FreeAdvDlgTree (HWND);
|
|
void TreeContextMenu (HWND, HWND);
|
|
|
|
int lstrnicmp (LPTSTR, LPTSTR, size_t);
|
|
LPTSTR lstrchr (LPTSTR, TCHAR);
|
|
void lstrncpy (LPTSTR, LPTSTR, size_t);
|
|
|
|
void ShowDeviceProperties (HWND, HTREEITEM);
|
|
|
|
PIDRIVER FindIDriverByTreeItem (HTREEITEM);
|
|
|
|
#ifdef FIX_BUG_15451
|
|
HTREEITEM FindTreeItemByDriverName (LPTSTR);
|
|
#endif // FIX_BUG_15451
|
|
|
|
// We want to run "control joy.cpl" when the joystick devices
|
|
// are highlight and the user clicks Add/Remove/Properties buttons.
|
|
BOOL RunJoyControlPanel(void); //qzheng
|
|
|
|
/*
|
|
***
|
|
*
|
|
*/
|
|
|
|
|
|
DWORD GetFileDateTime (LPTSTR);
|
|
LPTSTR GetProfile (LPTSTR,LPTSTR, LPTSTR, LPTSTR, int);
|
|
void AddIDrivers (HWND, LPTSTR, LPTSTR);
|
|
HTREEITEM AddIDriver (HWND, PIDRIVER, DriverClass);
|
|
BOOL AddIDriverByName (HWND, LPCWSTR, DriverClass);
|
|
PIDRIVER GetSelectedIDriver (HWND);
|
|
BOOL FillTreeFromWinMM (HWND);
|
|
BOOL FillTreeFromMSACM (HWND);
|
|
BOOL FillTreeFromMCI (HWND);
|
|
BOOL FillTreeFromMIDI (HWND);
|
|
BOOL FillTreeFromRemaining (HWND);
|
|
void FillTreeFromRemainingBySection (HWND, long ii, LPCTSTR, DriverClass);
|
|
BOOL CALLBACK FillTreeFromMSACMQueryCallback (HACMDRIVERID, DWORD_PTR, DWORD);
|
|
int __cdecl FillTreeFromMSACMSortCallback (const void *p1, const void *p2);
|
|
BOOL InitAvailable (HWND, int);
|
|
void RemoveAvailable (HWND);
|
|
BOOL UserInstalled (LPTSTR);
|
|
INT_PTR RestartDlg (HWND, unsigned, WPARAM, LPARAM);
|
|
INT_PTR AddUnlistedDlg (HWND, unsigned, WPARAM, LPARAM);
|
|
INT_PTR AvailableDriversDlg (HWND, unsigned, WPARAM, LPARAM);
|
|
INT_PTR AdvDlg (HWND, unsigned, WPARAM, LPARAM);
|
|
void ReBoot (HWND);
|
|
BOOL GetMappable (PIDRIVER);
|
|
BOOL SetMappable (PIRESOURCE, BOOL);
|
|
|
|
#define REGSTR_PATH_WAVEMAPPER TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Wave Mapper")
|
|
#define REGSTR_VALUE_MAPPABLE TEXT("Mappable")
|
|
|
|
#define REGSTR_PATH_MCI TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI")
|
|
#define REGSTR_PATH_MCI32 TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI32")
|
|
#define REGSTR_PATH_DRIVERS TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers")
|
|
#define REGSTR_PATH_DRIVERS32 TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32")
|
|
|
|
|
|
/*
|
|
* REALLOC - Allows expansion of GlobalAlloc()'d block while retaining contents
|
|
*
|
|
* Newly-allocated portions of memory are initialized to zero, while
|
|
* the contents of reallocated portions of memory are retained.
|
|
*
|
|
* Parameters:
|
|
* LPVOID _pData....allocated array
|
|
* mysize_t _cOld.....current count of elements in allocated array
|
|
* mysize_t _cNew.....minimum number of elements requested
|
|
* mysize_t _cDelta...granularity of increase
|
|
*
|
|
* Example:
|
|
* {
|
|
* mysize_t cElements = 0; // Number of elements allocated so far
|
|
* DataType *aElements = NULL; // Allocated array of DataType
|
|
*
|
|
* // At this point, cElements == 0 (obviously)
|
|
*
|
|
* REALLOC (aElements, cElements, 10, 16)
|
|
*
|
|
* // The line above requested 10 elements, and indicated that elements
|
|
* // should be allocated in increments of 16. So cElements is 16 at this
|
|
* // point (thus, sizeof(aElements) == sizeof(DataType)*16).
|
|
*
|
|
* REALLOC (aElements, cElements, 12, 16)
|
|
*
|
|
* // The line above requested 12 elements. Since cElements is already 16,
|
|
* // REALLOC knows that 12 elements are already available--and does nothing.
|
|
*
|
|
* REALLOC (aElements, cElements, 17, 16)
|
|
*
|
|
* // The line above requested 17 elements, in increments of 16. aElements
|
|
* // has been reallocated to contain 32 elements, and cElements is
|
|
* // therefore 32.
|
|
*
|
|
* GlobalFree ((HGLOBAL)aElements); // All done!
|
|
* aElements = NULL;
|
|
* cElements = 0;
|
|
* }
|
|
*
|
|
*
|
|
*/
|
|
|
|
typedef signed long mysize_t;
|
|
|
|
#ifdef REALLOC
|
|
#undef REALLOC
|
|
#endif
|
|
#define REALLOC(_pData,_cOld,_cNew,_cDelta) \
|
|
ReallocFn (sizeof(*_pData), (void **)&_pData, &_cOld, _cNew, _cDelta)
|
|
|
|
#ifdef DivRoundUp
|
|
#undef DivRoundUp
|
|
#endif
|
|
#define DivRoundUp(_a,_b) ( (LONG)(((_a) + (_b) -1) / (_b)) )
|
|
|
|
#ifdef RoundUp
|
|
#undef RoundUp
|
|
#endif
|
|
#define RoundUp(_a,_b) ( DivRoundUp(_a,_b) * (LONG)_b )
|
|
|
|
BOOL ReallocFn (mysize_t cbElement,
|
|
LPVOID *ppOld, mysize_t *pcOld, mysize_t cNew, mysize_t cDelta)
|
|
{
|
|
LPVOID pNew;
|
|
mysize_t cbOld, cbNew;
|
|
|
|
// First check if we actually need to reallocate or not.
|
|
// It's possible that {ppOld} was already allocated with
|
|
// enough space.
|
|
//
|
|
if ( ((*ppOld) != NULL) && (cNew <= (*pcOld)) )
|
|
return TRUE;
|
|
|
|
// Oh well. Determine how much space we need, and how much
|
|
// is allocated now.
|
|
//
|
|
cNew = RoundUp (cNew, cDelta);
|
|
cbNew = cbElement * cNew;
|
|
cbOld = (ppOld == NULL) ? 0 : (cbElement * (*pcOld));
|
|
|
|
// Allocate the space and copy over the original contents.
|
|
// Zero-fill the remaining space.
|
|
//
|
|
if ((pNew = (LPVOID)GlobalAlloc (GMEM_FIXED, cbNew)) == NULL)
|
|
return FALSE;
|
|
|
|
if (cbOld != 0)
|
|
{
|
|
memcpy (pNew, *ppOld, cbOld);
|
|
GlobalFree ((HGLOBAL)(*ppOld));
|
|
}
|
|
|
|
memset (&((char*)pNew)[ cbOld ], 0x00, cbNew -cbOld);
|
|
|
|
// Finally, update the passed-in pointers and we're done.
|
|
//
|
|
*pcOld = cNew;
|
|
*ppOld = pNew;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* PIDRIVER ARRAY
|
|
*
|
|
* The AddIDrivers() routine LocalAlloc()'s a single IDRIVER structure
|
|
* for each installed device driver. Pointers to these structures are
|
|
* retained in the InstalledDriver array, and indices into this array
|
|
* are stored as the LPARAM values for each tree item. Each element
|
|
* in the array stores not only a pointer to the driver's IDRIVER structure,
|
|
* but also a DWORD which, as a combination of aDriverRoot[].dwBit values,
|
|
* reflects the tree root items under which the driver has tree items.
|
|
*
|
|
*/
|
|
|
|
typedef struct // InstalledDriver
|
|
{
|
|
PIDRIVER pIDriver; // Pointer to AddIDrivers()'s PIDRIVER structure
|
|
DWORD dwBits; // Combination of aDriverRoot[].dwBit flags
|
|
} InstalledDriver;
|
|
|
|
InstalledDriver *aInstalledDrivers = NULL;
|
|
mysize_t cInstalledDrivers = 0;
|
|
|
|
#define NOPIDRIVER ((LPARAM)-1)
|
|
|
|
|
|
|
|
/*
|
|
* CheckSectionAccess()
|
|
*
|
|
* See if we can read/write to a given section
|
|
*/
|
|
|
|
|
|
BOOL CheckSectionAccess(TCHAR *szIniFile, TCHAR *SectionName)
|
|
{
|
|
static TCHAR TestKey[] = TEXT("TestKey!!!");
|
|
static TCHAR TestData[] = TEXT("TestData");
|
|
static TCHAR ReturnData[50];
|
|
|
|
/*
|
|
* Check we can write, read back and delete our key
|
|
*/
|
|
|
|
return WritePrivateProfileString(SectionName,
|
|
TestKey,
|
|
TestData,
|
|
szIniFile) &&
|
|
|
|
GetPrivateProfileString(SectionName,
|
|
TestKey,
|
|
TEXT(""),
|
|
ReturnData,
|
|
sizeof(ReturnData) / sizeof(TCHAR),
|
|
szIniFile) == (DWORD)wcslen(TestData) &&
|
|
|
|
WritePrivateProfileString(SectionName,
|
|
TestKey,
|
|
NULL,
|
|
szIniFile);
|
|
}
|
|
|
|
|
|
/*
|
|
* CheckIniAccess()
|
|
*
|
|
* Checks access to our 2 .ini file sections - DRIVERS_SECTION and
|
|
* MCI_SECTION by just writing and reading some junk
|
|
*
|
|
* Basically if we don't have access to these sections we're not
|
|
* going to allow Add and Remove. The individual MCI drivers must
|
|
* take care not to put their data into non-writeable storage although
|
|
* this completely messes up the default parameters thing so we're going
|
|
* to put these into a well-known key in the win.ini file (ie per user).
|
|
*
|
|
*/
|
|
|
|
BOOL CheckIniAccess(void)
|
|
{
|
|
return CheckSectionAccess(szSysIni, szDrivers) &&
|
|
CheckSectionAccess(szSysIni, szMCI) &&
|
|
CheckSectionAccess(szControlIni, szUserDrivers) &&
|
|
CheckSectionAccess(szControlIni, szDriversDesc) &&
|
|
CheckSectionAccess(szControlIni, szRelatedDesc);
|
|
}
|
|
|
|
/*
|
|
* QueryRemoveDrivers()
|
|
*
|
|
* Ask the user if they're sure. If the Driver is one required by the
|
|
* system (ie not listed in [Userinstallable.drivers] in control.ini)
|
|
* warn the user of that too.
|
|
*/
|
|
|
|
BOOL QueryRemoveDrivers(HWND hDlg, LPTSTR szKey, LPTSTR szDesc)
|
|
{
|
|
TCHAR bufout[MAXSTR];
|
|
|
|
if (UserInstalled(szKey))
|
|
wsprintf(bufout, szRemoveOrNot, (LPTSTR)szDesc);
|
|
else
|
|
wsprintf(bufout, szRemoveOrNotStrict, (LPTSTR)szDesc);
|
|
|
|
return (MessageBox(hDlg, bufout, szRemove,
|
|
MB_ICONEXCLAMATION | MB_TASKMODAL | MB_YESNO) == IDYES );
|
|
}
|
|
|
|
/*
|
|
* GetProfile()
|
|
*
|
|
* Get private profile strings.
|
|
*/
|
|
|
|
LPTSTR GetProfile(LPTSTR pstrAppName, LPTSTR pstrKeyName, LPTSTR pstrIniFile,
|
|
LPTSTR pstrRet, int cbSize)
|
|
{
|
|
TCHAR szNULL[2];
|
|
|
|
szNULL[0] = TEXT('\0');
|
|
GetPrivateProfileString(pstrAppName, (pstrKeyName==NULL) ? NULL :
|
|
(LPTSTR)pstrKeyName, szNULL, pstrRet, cbSize/sizeof(TCHAR), pstrIniFile);
|
|
return(pstrRet);
|
|
}
|
|
|
|
/*********************************************************************
|
|
*
|
|
* AddIDrivers()
|
|
*
|
|
* Add drivers in the passed key strings list to the InstalledDrivers array
|
|
*
|
|
*********************************************************************/
|
|
|
|
void AddIDrivers(HWND hWnd, LPTSTR pstrKeys, LPTSTR pstrSection)
|
|
{
|
|
PIDRIVER pIDriver;
|
|
LPTSTR pstrKey;
|
|
LPTSTR pstrDesc;
|
|
|
|
pstrKey = pstrKeys;
|
|
pstrDesc = (LPTSTR)LocalAlloc(LPTR, (MAXSTR * sizeof(TCHAR)));
|
|
|
|
if (!pstrDesc) return;
|
|
|
|
/*
|
|
* parse key strings for profile, and make IDRIVER structs
|
|
*/
|
|
|
|
while ( *pstrKey )
|
|
{
|
|
pIDriver = (PIDRIVER)LocalAlloc(LPTR, sizeof(IDRIVER));
|
|
if ( pIDriver )
|
|
{
|
|
LPTSTR pstr;
|
|
|
|
if (*GetProfile(pstrSection, pstrKey, szSysIni, pIDriver->szFile,
|
|
sizeof(pIDriver->szFile)) == TEXT('\0'))
|
|
{
|
|
LocalFree((HANDLE)pIDriver);
|
|
goto nextkey;
|
|
}
|
|
|
|
for ( pstr=pIDriver->szFile; *pstr && (*pstr!=COMMA) &&
|
|
(*pstr!=SPACE); pstr++ )
|
|
;
|
|
*pstr = TEXT('\0');
|
|
|
|
#ifdef TRASHDRIVERDESC
|
|
if (bDescFileValid)
|
|
#endif
|
|
/*
|
|
* try to load the cached description
|
|
*/
|
|
|
|
GetProfile(szDriversDesc,
|
|
pIDriver->szFile,
|
|
szControlIni,
|
|
pIDriver->szDesc,
|
|
sizeof(pIDriver->szDesc));
|
|
|
|
/*
|
|
* if we failed, then try to get the information from
|
|
* mmdriver.inf or the exehdr
|
|
*/
|
|
|
|
if (pIDriver->szDesc[0] == TEXT('\0'))
|
|
{
|
|
int nResult = LoadDescFromFile(pIDriver, pstrKey, pstrDesc, MAXSTR);
|
|
if( nResult == DESC_ERROR
|
|
|| nResult == DESC_NOFILE )
|
|
{
|
|
LocalFree((HANDLE)pIDriver);
|
|
goto nextkey;
|
|
}
|
|
else
|
|
{
|
|
if (!*pstrDesc)
|
|
{
|
|
/*
|
|
* failed to load a description.
|
|
* The file isn't in setup.inf
|
|
* and doesn't have exehdr information
|
|
*/
|
|
|
|
lstrcpy(pIDriver->szDesc, pIDriver->szFile);
|
|
lstrcat(pIDriver->szDesc, szNoDesc);
|
|
}
|
|
else
|
|
lstrcpy(pIDriver->szDesc, pstrDesc);
|
|
|
|
WritePrivateProfileString(szDriversDesc, pIDriver->szFile,
|
|
pIDriver->szDesc, szControlIni);
|
|
}
|
|
}
|
|
|
|
wcsncpy(pIDriver->szAlias, pstrKey, sizeof(pIDriver->szAlias)/sizeof(TCHAR));
|
|
pIDriver->szAlias[sizeof(pIDriver->szAlias)/sizeof(TCHAR) - 1] = TEXT('\0');
|
|
wcscpy(pIDriver->wszAlias, pIDriver->szAlias);
|
|
|
|
wcsncpy(pIDriver->szSection, pstrSection,sizeof(pIDriver->szSection)/sizeof(TCHAR));
|
|
pIDriver->szSection[sizeof(pIDriver->szSection)/sizeof(TCHAR) - 1] = TEXT('\0');
|
|
wcscpy(pIDriver->wszSection, pIDriver->szSection);
|
|
|
|
pIDriver->KernelDriver = IsFileKernelDriver(pIDriver->szFile);
|
|
pIDriver->fQueryable = pIDriver->KernelDriver ? 0 : -1;
|
|
|
|
pIDriver->lp = 0L;
|
|
|
|
if (!AddIDriverToArray (pIDriver))
|
|
LocalFree((HANDLE)pIDriver);
|
|
}
|
|
else
|
|
break; //ERROR Low Memory
|
|
|
|
nextkey: while (*pstrKey++);
|
|
}
|
|
LocalFree((HANDLE)pstrDesc);
|
|
}
|
|
|
|
|
|
/*
|
|
* AddIDriverToArray - Adds the given PIDRIVER to the InstalledDrivers array
|
|
*
|
|
*/
|
|
|
|
BOOL AddIDriverToArray (PIDRIVER pIDriver)
|
|
{
|
|
mysize_t ii;
|
|
|
|
if (pIDriver == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Don't create duplicate entries in this array; one PIDRIVER
|
|
// per driver-file is sufficient.
|
|
//
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver != NULL)
|
|
{
|
|
if (!lstrcmpi (aInstalledDrivers[ ii ].pIDriver->szFile,
|
|
pIDriver->szFile))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// To reduce repetitive calls to GlobalAlloc(), we'll allocate
|
|
// space for an additional 50 InstalledDriver entries within
|
|
// the aInstalledDrivers array each time we run out of space.
|
|
//
|
|
#define nDriverEntriesToAllocAtONCE 50
|
|
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == NULL)
|
|
break;
|
|
}
|
|
|
|
if (ii >= cInstalledDrivers)
|
|
{
|
|
if (!REALLOC (aInstalledDrivers, // Array
|
|
cInstalledDrivers, // Current size of array
|
|
1+ii, // Requested size of array
|
|
nDriverEntriesToAllocAtONCE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
aInstalledDrivers[ ii ].pIDriver = pIDriver;
|
|
aInstalledDrivers[ ii ].dwBits = 0L;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* FindInstallableDriversSection()
|
|
*
|
|
*********************************************************************/
|
|
|
|
PINF FindInstallableDriversSection(PINF pinf)
|
|
{
|
|
PINF pinfFound;
|
|
|
|
pinfFound = infFindSection(pinf, szMDrivers32);
|
|
|
|
if (pinfFound == NULL) {
|
|
pinfFound = infFindSection(pinf, szMDrivers);
|
|
}
|
|
|
|
return pinfFound;
|
|
}
|
|
|
|
//NOTE: Returns nSize as a count of bytes, not characters (later calls expect this)
|
|
int GetINISectionSize(LPCTSTR pstrSection, LPCTSTR pstrFile)
|
|
{
|
|
int ncbSize = 0;
|
|
int ncbMaxSize = 0;
|
|
|
|
while (ncbSize >= ncbMaxSize)
|
|
{
|
|
TCHAR szNULL[2];
|
|
LPTSTR pStr = NULL;
|
|
|
|
szNULL[0] = TEXT('\0');
|
|
|
|
ncbMaxSize += SECTION; //allocate another 512 bytes
|
|
|
|
pStr = (LPTSTR)LocalAlloc(LPTR, ncbMaxSize);
|
|
|
|
if (!pStr)
|
|
{
|
|
//we're trying to allocate too much memory ...
|
|
//drop out and use the last smaller size that worked
|
|
break;
|
|
}
|
|
|
|
ncbSize = GetPrivateProfileString(pstrSection, NULL, szNULL, pStr, ncbMaxSize/sizeof(TCHAR), pstrFile);
|
|
ncbSize = (ncbSize+2) * sizeof(TCHAR); //convert to byte count, adding two chars
|
|
//to account for terminating null and API's truncation
|
|
|
|
LocalFree(pStr);
|
|
}
|
|
|
|
return (ncbSize);
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
*
|
|
* InitInstalled()
|
|
*
|
|
* Add the drivers installed in [DRIVERS] and [MCI] to the Installed
|
|
* Drivers list box.
|
|
*
|
|
*********************************************************************/
|
|
|
|
BOOL InitInstalled(HWND hWnd, LPTSTR pstrSection)
|
|
{
|
|
BOOL bSuccess=FALSE;
|
|
LPTSTR pstr;
|
|
int nSize = SECTION;
|
|
|
|
#ifdef TRASHDRIVERDESC
|
|
UINT wTime;
|
|
BOOL fForce;
|
|
TCHAR szOut[10];
|
|
|
|
wTime = LOWORD(GetFileDateTime(szControlIni)) >> 1;
|
|
if (fForce = (GetPrivateProfileInt((LPTSTR)szUserDrivers,
|
|
(LPTSTR)szLastQuery, 0, (LPTSTR)szControlIni) != wTime))
|
|
{
|
|
wsprintf(szOut, TEXT("%d"), wTime);
|
|
WritePrivateProfileString((LPTSTR)szUserDrivers, (LPTSTR)szLastQuery,
|
|
szOut, (LPTSTR)szControlIni);
|
|
WritePrivateProfileString((LPTSTR)szDriversDesc, NULL, NULL,
|
|
(LPTSTR)szControlIni);
|
|
bDescFileValid = FALSE;
|
|
}
|
|
else
|
|
bDescFileValid = TRUE;
|
|
#endif
|
|
|
|
nSize = GetINISectionSize(pstrSection, szSysIni);
|
|
|
|
pstr = (LPTSTR)LocalAlloc(LPTR, nSize);
|
|
if ( pstr )
|
|
{
|
|
if (*GetProfile(pstrSection, NULL, szSysIni, pstr, nSize ))
|
|
{
|
|
AddIDrivers(hWnd,pstr,pstrSection);
|
|
bSuccess = TRUE;
|
|
}
|
|
|
|
LocalFree((HANDLE)pstr);
|
|
}
|
|
|
|
return(bSuccess);
|
|
}
|
|
|
|
|
|
/*
|
|
* RefreshAdvDlgTree - Clears the Devices tree, and fills it back in
|
|
*
|
|
*/
|
|
|
|
void RefreshAdvDlgTree (void)
|
|
{
|
|
if (hAdvDlgTree != NULL)
|
|
{
|
|
SendMessage (hAdvDlgTree, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
FreeAdvDlgTree (hAdvDlgTree);
|
|
InitAdvDlgTree (hAdvDlgTree);
|
|
InitInstalled (GetParent (hAdvDlgTree), szDrivers);
|
|
InitInstalled (GetParent (hAdvDlgTree), szMCI);
|
|
FillTreeInAdvDlg (hAdvDlgTree, NULL);
|
|
|
|
SendMessage (hAdvDlgTree, WM_SETREDRAW, TRUE, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* FillTreeInAdvDlg - Adds TreeItems for each entry in aInstalledDrivers
|
|
*
|
|
* If pIDriver is specified, the first treeitem to mention that driver
|
|
* will be highlighted.
|
|
*
|
|
*/
|
|
|
|
BOOL FillTreeInAdvDlg (HWND hTree, PIDRIVER pIDriver)
|
|
{
|
|
if (!FillTreeFromWinMM (hTree))
|
|
return FALSE;
|
|
|
|
if (!FillTreeFromMSACM (hTree))
|
|
return FALSE;
|
|
|
|
if (!FillTreeFromMCI (hTree))
|
|
return FALSE;
|
|
|
|
if (!FillTreeFromMIDI (hTree))
|
|
return FALSE;
|
|
|
|
if (!FillTreeFromRemaining (hTree))
|
|
return FALSE;
|
|
|
|
if (pIDriver != NULL) // Do we have to highlight a pIDriver?
|
|
{
|
|
short idr;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
HTREEITEM hti;
|
|
|
|
if ((hti = aDriverRoot[ idr ].hti) == NULL)
|
|
continue;
|
|
|
|
for (hti = TreeView_GetChild (hTree, hti);
|
|
hti != NULL;
|
|
hti = TreeView_GetNextSibling (hTree, hti))
|
|
{
|
|
if (pIDriver == FindIDriverByTreeItem (hti))
|
|
{
|
|
TreeView_SelectItem (hTree, hti);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hti != NULL) // Found and selected a TreeItem?
|
|
break; // Then we're done!
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* FillTreeFromWinMM - Adds tree items for all WinMM-controlled MM devices
|
|
*
|
|
* This routine adds tree items under the following DriverClasses:
|
|
* dcWAVE - waveOut*
|
|
* dcMIXER - mixer*
|
|
* dcAUX - aux*
|
|
*
|
|
*/
|
|
|
|
BOOL FillTreeFromWinMM (HWND hTree)
|
|
{
|
|
UINT iDevice;
|
|
UINT cDevices;
|
|
WCHAR szDriver[ cchRESOURCE ];
|
|
|
|
// Add entries for each waveOut device
|
|
//
|
|
cDevices = waveOutGetNumDevs ();
|
|
for (iDevice = 0; iDevice < cDevices; ++iDevice)
|
|
{
|
|
if (waveOutMessage (HWAVEOUT_INDEX(iDevice),
|
|
DRV_QUERYFILENAME,
|
|
(DWORD_PTR)szDriver,
|
|
(DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
|
|
{
|
|
AddIDriverByName (hTree, szDriver, dcWAVE);
|
|
}
|
|
}
|
|
|
|
// Add entries for each mixer device
|
|
//
|
|
cDevices = mixerGetNumDevs ();
|
|
for (iDevice = 0; iDevice < cDevices; ++iDevice)
|
|
{
|
|
if (mixerMessage (HMIXER_INDEX(iDevice),
|
|
DRV_QUERYFILENAME,
|
|
(DWORD_PTR)szDriver,
|
|
(DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
|
|
{
|
|
AddIDriverByName (hTree, szDriver, dcMIXER);
|
|
}
|
|
}
|
|
|
|
// Add entries for each aux device
|
|
//
|
|
cDevices = auxGetNumDevs ();
|
|
for (iDevice = 0; iDevice < cDevices; ++iDevice)
|
|
{
|
|
if (auxOutMessage (iDevice,
|
|
DRV_QUERYFILENAME,
|
|
(DWORD_PTR)szDriver,
|
|
(DWORD_PTR)cchRESOURCE) == MMSYSERR_NOERROR)
|
|
{
|
|
AddIDriverByName (hTree, szDriver, dcAUX);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* FillTreeFromMSACM - Adds tree items for all MSACM-controlled MM devices
|
|
*
|
|
* This routine adds tree items under the following DriverClasses:
|
|
* dcACODEC - acmDriverEnum()
|
|
*
|
|
* Note that, since audio codecs are supposed to be sorted in the tree,
|
|
* all audio codec treeitems are first deleted from the tree (if there
|
|
* are any at this point) then all audio codecs are added in their
|
|
* sorted order.
|
|
*
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwPriority; // priority of this audio codec
|
|
PIDRIVER pIDriver; // matching driver file (or NULL)
|
|
WORD wMid; // manufacturer ID
|
|
WORD wPid; // product ID
|
|
TCHAR szDesc[ ACMDRIVERDETAILS_LONGNAME_CHARS ];
|
|
} AudioCodec;
|
|
|
|
AudioCodec *pCodecs;
|
|
mysize_t cCodecs;
|
|
|
|
extern BOOL gfLoadedACM; // From MSACMCPL.C
|
|
|
|
BOOL FillTreeFromMSACM (HWND hTree)
|
|
{
|
|
MMRESULT mmr;
|
|
short idr;
|
|
mysize_t ii;
|
|
|
|
if (!gfLoadedACM)
|
|
{
|
|
if (LoadACM())
|
|
gfLoadedACM = TRUE;
|
|
}
|
|
if (!gfLoadedACM)
|
|
return FALSE;
|
|
|
|
// Step one: get rid of any audio codecs listed in the tree
|
|
//
|
|
if ((idr = DriverClassToRootIndex (dcACODEC)) != -1)
|
|
{
|
|
if (aDriverRoot[ idr ].hti != NULL)
|
|
{
|
|
HTREEITEM hti;
|
|
|
|
while ((hti = TreeView_GetChild (hTree, aDriverRoot[ idr ].hti)) != 0)
|
|
{
|
|
TreeView_DeleteItem (hTree, hti);
|
|
|
|
if (hti == TreeView_GetChild (hTree, aDriverRoot[ idr ].hti))
|
|
break; // if it didn't delete, make sure we don't loop forever!
|
|
}
|
|
}
|
|
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
aInstalledDrivers[ ii ].dwBits &= ~aDriverRoot[ idr ].dwBit;
|
|
}
|
|
}
|
|
|
|
// Step two: query ACM to obtain the list of codecs
|
|
//
|
|
pCodecs = NULL;
|
|
cCodecs = 0;
|
|
|
|
mmr = (MMRESULT)acmDriverEnum (FillTreeFromMSACMQueryCallback,
|
|
0,
|
|
ACM_DRIVERENUMF_NOLOCAL | ACM_DRIVERENUMF_DISABLED);
|
|
|
|
// Step three: sort the list of codecs and add each to the tree
|
|
//
|
|
if ((mmr == MMSYSERR_NOERROR) && (pCodecs != NULL))
|
|
{
|
|
mysize_t iiDr;
|
|
|
|
qsort (pCodecs, (size_t)cCodecs, sizeof(AudioCodec),
|
|
FillTreeFromMSACMSortCallback);
|
|
|
|
// Assign lp=wMid+wPid for each audio codec we find
|
|
//
|
|
for (iiDr = 0; iiDr < cInstalledDrivers; ++iiDr)
|
|
{
|
|
if (aInstalledDrivers[ iiDr ].pIDriver == NULL)
|
|
continue;
|
|
if (aInstalledDrivers[ iiDr ].pIDriver->lp != 0L) // already did this
|
|
continue;
|
|
|
|
if (GuessDriverClass (aInstalledDrivers[ iiDr ].pIDriver) == dcACODEC)
|
|
{
|
|
HANDLE hDriver;
|
|
|
|
hDriver = OpenDriver (aInstalledDrivers[iiDr].pIDriver->wszAlias,
|
|
aInstalledDrivers[iiDr].pIDriver->wszSection,
|
|
0L);
|
|
if (hDriver != NULL)
|
|
{
|
|
ACMDRIVERDETAILSW add;
|
|
memset ((TCHAR *)&add, 0x00, sizeof(add));
|
|
add.cbStruct = sizeof(add);
|
|
SendDriverMessage (hDriver, ACMDM_DRIVER_DETAILS, (LONG_PTR)&add, 0);
|
|
CloseDriver (hDriver, 0L, 0L);
|
|
|
|
aInstalledDrivers[ iiDr ].pIDriver->lp = MAKELONG( add.wMid,
|
|
add.wPid );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Search for installed drivers with matching lp=wMid+wPid's
|
|
//
|
|
for (iiDr = 0; iiDr < cInstalledDrivers; ++iiDr)
|
|
{
|
|
if (aInstalledDrivers[ iiDr ].pIDriver == NULL)
|
|
continue;
|
|
|
|
if ((aInstalledDrivers[ iiDr ].pIDriver->szAlias[0] == TEXT('\0')) ||
|
|
(GuessDriverClass (aInstalledDrivers[iiDr].pIDriver) == dcACODEC))
|
|
{
|
|
for (ii = 0; ii < cCodecs; ++ii)
|
|
{
|
|
if (pCodecs[ ii ].dwPriority == 0)
|
|
continue;
|
|
|
|
if ( (pCodecs[ ii ].wMid ==
|
|
LOWORD( aInstalledDrivers[ iiDr ].pIDriver->lp )) &&
|
|
(pCodecs[ ii ].wPid ==
|
|
HIWORD( aInstalledDrivers[ iiDr ].pIDriver->lp )) )
|
|
{
|
|
pCodecs[ ii ].pIDriver = aInstalledDrivers[ iiDr ].pIDriver;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add each in-use entry in pCodecs to the treeview
|
|
//
|
|
for (ii = 0; ii < cCodecs; ++ii)
|
|
{
|
|
if (pCodecs[ ii ].dwPriority == 0)
|
|
continue;
|
|
|
|
// The PCM converter, for instance, won't have a matching
|
|
// PID. So create a bogus one--the lack of an szAlias
|
|
// will let us know it's bogus--and insert it into the
|
|
// aInstalledDrivers array.
|
|
//
|
|
if (pCodecs[ ii ].pIDriver == NULL)
|
|
{
|
|
PIDRIVER pid = (PIDRIVER)LocalAlloc(LPTR, sizeof(IDRIVER));
|
|
|
|
if (pid != NULL)
|
|
{
|
|
memset (pid, 0x00, sizeof(IDRIVER));
|
|
pid->lp = MAKELONG( pCodecs[ ii ].wMid, pCodecs[ ii ].wPid );
|
|
lstrcpy (pid->szDesc, pCodecs[ ii ].szDesc);
|
|
|
|
if (!AddIDriverToArray (pid))
|
|
LocalFree ((HLOCAL)pid);
|
|
else
|
|
{
|
|
pCodecs[ ii ].pIDriver = pid;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pCodecs[ ii ].pIDriver != NULL)
|
|
{
|
|
AddIDriver (hTree, pCodecs[ ii ].pIDriver, dcACODEC);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
//
|
|
if (pCodecs != NULL)
|
|
{
|
|
GlobalFree ((HGLOBAL)pCodecs);
|
|
pCodecs = NULL;
|
|
cCodecs = 0;
|
|
}
|
|
|
|
return (mmr == MMSYSERR_NOERROR) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* FillTreeFromMCI - Adds tree items for all MCI devices
|
|
*
|
|
* This routine adds tree items under the following DriverClasses:
|
|
* dcMCI - mciSendCommand
|
|
*
|
|
*/
|
|
|
|
BOOL FillTreeFromMCI (HWND hTree)
|
|
{
|
|
MCI_SYSINFO_PARMS mciSysInfo;
|
|
TCHAR szAlias[ cchRESOURCE ];
|
|
|
|
// How many MCI devices does WinMM know about?
|
|
//
|
|
memset ((TCHAR *)&mciSysInfo, 0x00, sizeof(mciSysInfo));
|
|
mciSysInfo.lpstrReturn = szAlias;
|
|
mciSysInfo.dwRetSize = cchLENGTH(szAlias);
|
|
mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;
|
|
|
|
if (mciSendCommand (MCI_ALL_DEVICE_ID,
|
|
MCI_SYSINFO,
|
|
MCI_SYSINFO_QUANTITY,
|
|
(DWORD_PTR)&mciSysInfo) == 0)
|
|
{
|
|
DWORD iDevice;
|
|
DWORD cDevices;
|
|
|
|
cDevices = *((DWORD *)(mciSysInfo.lpstrReturn));
|
|
|
|
// Get the name of each MCI device in turn.
|
|
//
|
|
for (iDevice = 0; iDevice < cDevices; ++iDevice)
|
|
{
|
|
mysize_t ii;
|
|
|
|
memset ((TCHAR *)&mciSysInfo, 0x00, sizeof(mciSysInfo));
|
|
mciSysInfo.lpstrReturn = szAlias;
|
|
mciSysInfo.dwRetSize = cchLENGTH(szAlias);
|
|
mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;
|
|
mciSysInfo.dwNumber = 1+iDevice; // note: 1-based, not 0-based!
|
|
|
|
if (mciSendCommand (MCI_ALL_DEVICE_ID,
|
|
MCI_SYSINFO,
|
|
MCI_SYSINFO_NAME,
|
|
(DWORD_PTR)&mciSysInfo) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Got an alias--search the InstalledDrivers array
|
|
// and try to find a matching PIDRIVER.
|
|
//
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == NULL)
|
|
continue;
|
|
if (!lstrcmpi (aInstalledDrivers[ ii ].pIDriver->szAlias, szAlias))
|
|
{
|
|
#ifdef GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES
|
|
MCI_OPEN_PARMS mciOpen;
|
|
MCI_INFO_PARMS mciInfo;
|
|
MCIERROR rc;
|
|
|
|
// It's an installed, functioning, happy MCI device.
|
|
// Open it up and see what it calls itself; update the
|
|
// description in the PIDRIVER (what's in there was
|
|
// obtained from the registry, thus from MMDRIVER.INF,
|
|
// and we instead want what Media Player lists in its
|
|
// Device menu).
|
|
//
|
|
memset ((TCHAR *)&mciOpen, 0x00, sizeof(mciOpen));
|
|
mciOpen.lpstrDeviceType = szAlias;
|
|
|
|
rc = mciSendCommand (0,MCI_OPEN,MCI_OPEN_TYPE,(DWORD)&mciOpen);
|
|
if (rc == MCIERR_MUST_USE_SHAREABLE)
|
|
{
|
|
rc = mciSendCommand (0, MCI_OPEN,
|
|
MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE,
|
|
(DWORD)&mciOpen);
|
|
}
|
|
if (rc == 0)
|
|
{
|
|
TCHAR szDesc[ cchRESOURCE ];
|
|
szDesc[0] = 0;
|
|
|
|
mciInfo.lpstrReturn = szDesc;
|
|
mciInfo.dwRetSize = cchLENGTH(szDesc);
|
|
|
|
if (mciSendCommand (mciOpen.wDeviceID,
|
|
MCI_INFO,
|
|
MCI_INFO_PRODUCT,
|
|
(DWORD)&mciInfo) == 0)
|
|
{
|
|
lstrcpy (aInstalledDrivers[ ii ].pIDriver->szDesc, szDesc);
|
|
}
|
|
|
|
mciSendCommand (mciOpen.wDeviceID, MCI_CLOSE, 0L, 0);
|
|
}
|
|
#endif // GET_MCI_DEVICE_DESCRIPTIONS_FROM_THEIR_DEVICES
|
|
|
|
AddIDriver (hTree, aInstalledDrivers[ ii ].pIDriver, dcMCI);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* FillTreeFromMIDI - Adds tree items for all MIDI devices and instruments
|
|
*
|
|
* This routine adds tree items under the following DriverClasses:
|
|
* dcMIDI - LoadInstruments() provides necessary data
|
|
*
|
|
*/
|
|
|
|
BOOL FillTreeFromMIDI (HWND hTree)
|
|
{
|
|
MCMIDI mcm;
|
|
UINT iiRoot;
|
|
int idrMIDI;
|
|
|
|
if ((idrMIDI = DriverClassToRootIndex (dcMIDI)) == -1)
|
|
return FALSE;
|
|
|
|
// First load in all relevant information regarding MIDI
|
|
// instruments. Fortunately, all that work is encapsulated
|
|
// nicely within one routine.
|
|
//
|
|
memset (&mcm, 0x00, sizeof(mcm));
|
|
LoadInstruments (&mcm, FALSE);
|
|
|
|
// Each entry in mcm's api array is one of three things:
|
|
// - a parent (say, a sound card)
|
|
// - a child (say, an external instrument)
|
|
// - the "(none)" thing that we want to skip
|
|
//
|
|
// Add each parent to the tree, and when we find a parent,
|
|
// add all its children.
|
|
//
|
|
for (iiRoot = 0; iiRoot < mcm.nInstr; ++iiRoot)
|
|
{
|
|
TCHAR szName[ MAXSTR ];
|
|
LPTSTR pch;
|
|
PIDRIVER pid;
|
|
|
|
if (mcm.api[ iiRoot ] == NULL)
|
|
continue;
|
|
if (mcm.api[ iiRoot ]->piParent != NULL)
|
|
continue;
|
|
if (mcm.api[ iiRoot ]->szKey[0] == TEXT('\0'))
|
|
continue;
|
|
|
|
// Found a parent! If we can match it to an installed driver,
|
|
// add it to the tree. Note that mcm.api[]->szKey will
|
|
// be of the form "MMDRV.DLL<0000>"--we need to strip off
|
|
// the "<0000>" before we can match this thing to a PIDRIVER.
|
|
//
|
|
lstrcpy (szName, mcm.api[ iiRoot ]->szKey);
|
|
if ((pch = lstrchr (szName, TEXT('<'))) != NULL)
|
|
*pch = TEXT('\0');
|
|
|
|
if ((pid = FindIDriverByName (szName)) != NULL)
|
|
{
|
|
HTREEITEM hti;
|
|
UINT ii;
|
|
TV_ITEM tvi;
|
|
|
|
if ((hti = AddIDriver (hTree, pid, dcMIDI)) == NULL)
|
|
continue;
|
|
|
|
#if 0
|
|
tvi.mask = TVIF_TEXT;
|
|
tvi.hItem = hti;
|
|
tvi.pszText = mcm.api[ iiRoot ]->szFriendly;
|
|
TreeView_SetItem(hTree, &tvi);
|
|
#endif
|
|
|
|
// We've added this parent. See if it has any children,
|
|
// and if so, stick 'em in the tree.
|
|
//
|
|
for (ii = 0; ii < mcm.nInstr; ++ii)
|
|
{
|
|
PINSTRUM lp;
|
|
TV_INSERTSTRUCT ti;
|
|
|
|
if (mcm.api[ ii ] == NULL)
|
|
continue;
|
|
if (mcm.api[ ii ]->piParent != mcm.api[ iiRoot ])
|
|
continue;
|
|
|
|
// Yep--it's got a parent. Allocate a second copy
|
|
// of this PINSTRUM; that copy will be our LPARAM value.
|
|
//
|
|
if ((lp = (PINSTRUM)LocalAlloc(LPTR,sizeof (INSTRUM))) == NULL)
|
|
continue;
|
|
memcpy ((TCHAR *)lp, (TCHAR *)mcm.api[ ii ], sizeof (INSTRUM));
|
|
|
|
// Now add a treeitem for this instrument.
|
|
//
|
|
ti.hParent = hti;
|
|
ti.hInsertAfter = TVI_LAST;
|
|
ti.item.mask = TVIF_TEXT | TVIF_PARAM |
|
|
TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
ti.item.iImage = (int)idrMIDI;
|
|
ti.item.iSelectedImage = (int)idrMIDI;
|
|
ti.item.pszText = mcm.api[ ii ]->szFriendly;
|
|
ti.item.lParam = (LPARAM)lp;
|
|
|
|
if (!TreeView_InsertItem (hTree, &ti))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Done--cleanup and we're out of here.
|
|
//
|
|
FreeInstruments (&mcm);
|
|
|
|
if (mcm.hkMidi)
|
|
RegCloseKey (mcm.hkMidi);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// To reduce repetitive calls to GlobalAlloc(), we'll allocate
|
|
// space for an additional 10 AudioCodec entries within
|
|
// the pCodecs array each time we run out of space.
|
|
//
|
|
#define nAudioCodecEntriesToAllocAtONCE 10
|
|
|
|
BOOL CALLBACK FillTreeFromMSACMQueryCallback (HACMDRIVERID hadid,
|
|
DWORD_PTR dwUser,
|
|
DWORD fdwSupport)
|
|
{
|
|
short ii;
|
|
AudioCodec *pac;
|
|
ACMDRIVERDETAILS add;
|
|
|
|
// Find or create a place in which to store information
|
|
// about this codec
|
|
//
|
|
for (ii = 0; ii < cCodecs; ++ii)
|
|
{
|
|
if (pCodecs[ ii ].dwPriority == 0)
|
|
break;
|
|
}
|
|
if (ii >= cCodecs)
|
|
{
|
|
if (!REALLOC (pCodecs, cCodecs, 1+ii, nAudioCodecEntriesToAllocAtONCE))
|
|
return FALSE;
|
|
}
|
|
pac = &pCodecs[ ii ]; // for shorthand
|
|
|
|
// Find out about this codec
|
|
//
|
|
memset ((TCHAR *)&add, 0x00, sizeof(add));
|
|
add.cbStruct = sizeof(add);
|
|
if (acmDriverDetails (hadid, &add, 0) == MMSYSERR_NOERROR)
|
|
{
|
|
acmMetrics ((HACMOBJ)hadid,ACM_METRIC_DRIVER_PRIORITY,&pac->dwPriority);
|
|
|
|
lstrcpy (pac->szDesc, add.szLongName);
|
|
|
|
pac->wMid = add.wMid;
|
|
pac->wPid = add.wPid;
|
|
|
|
pac->pIDriver = NULL;
|
|
}
|
|
|
|
return TRUE; // keep counting
|
|
}
|
|
|
|
|
|
int __cdecl FillTreeFromMSACMSortCallback (const void *p1, const void *p2)
|
|
{
|
|
if (((AudioCodec *)p1)->dwPriority == 0)
|
|
return 1;
|
|
if (((AudioCodec *)p2)->dwPriority == 0)
|
|
return -1;
|
|
return ((AudioCodec *)p1)->dwPriority - ((AudioCodec *)p2)->dwPriority;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* FillTreeFromRemaining - Adds tree items for all remaining MM devices
|
|
*
|
|
* This routine adds a single tree item for each entry in the aInstalledDrivers
|
|
* array which is not already represented somewhere in the tree. The
|
|
* classification is based on the driver's alias--if that fails, it is lumped
|
|
* under dcOTHER.
|
|
*
|
|
*/
|
|
|
|
BOOL FillTreeFromRemaining (HWND hTree)
|
|
{
|
|
mysize_t ii;
|
|
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
UINT iiSkipCheck;
|
|
|
|
if (aInstalledDrivers[ ii ].pIDriver == NULL)
|
|
continue;
|
|
if (aInstalledDrivers[ ii ].pIDriver->szAlias[0] == TEXT('\0'))
|
|
continue;
|
|
|
|
// (don't do this for any not-to-be-displayed drivers)
|
|
//
|
|
for (iiSkipCheck = 0; iiSkipCheck < nDriversToSKIP; iiSkipCheck++)
|
|
{
|
|
if (!FileNameCmp ((LPTSTR)aDriversToSKIP[ iiSkipCheck ],
|
|
(LPTSTR)aInstalledDrivers[ ii ].pIDriver->szFile))
|
|
break;
|
|
}
|
|
if (iiSkipCheck < nDriversToSKIP)
|
|
continue;
|
|
|
|
// Zip through the {drivers,drivers32,mci,mci32} sections, to
|
|
// try to classify this driver. If we find a classification
|
|
// for which we haven't already added an entry in the tree,
|
|
// add another.
|
|
//
|
|
FillTreeFromRemainingBySection (hTree,
|
|
ii,
|
|
REGSTR_PATH_DRIVERS,
|
|
dcINVALID);
|
|
|
|
FillTreeFromRemainingBySection (hTree,
|
|
ii,
|
|
REGSTR_PATH_DRIVERS32,
|
|
dcINVALID);
|
|
|
|
FillTreeFromRemainingBySection (hTree,
|
|
ii,
|
|
REGSTR_PATH_MCI,
|
|
dcMCI);
|
|
|
|
FillTreeFromRemainingBySection (hTree,
|
|
ii,
|
|
REGSTR_PATH_MCI32,
|
|
dcMCI);
|
|
|
|
// If the dwBits element is zero, then this driver hasn't
|
|
// already been assigned a treeitem elsewhere. In that event,
|
|
// call AddIDriver() with dcOTHER--to tell it to lump this
|
|
// driver under "Other Drivers".
|
|
//
|
|
if (aInstalledDrivers[ ii ].dwBits == 0)
|
|
{
|
|
AddIDriver (hTree, aInstalledDrivers[ ii ].pIDriver, dcOTHER);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void FillTreeFromRemainingBySection (HWND hTree,
|
|
long iiDriver,
|
|
LPCTSTR pszSection,
|
|
DriverClass dcSection)
|
|
{
|
|
HKEY hk;
|
|
UINT ii;
|
|
|
|
if (RegOpenKey (HKEY_LOCAL_MACHINE, pszSection, &hk))
|
|
return;
|
|
|
|
for (ii = 0; ; ++ii)
|
|
{
|
|
TCHAR szLHS[ cchRESOURCE ];
|
|
TCHAR szRHS[ cchRESOURCE ];
|
|
DWORD dw1;
|
|
DWORD dw2;
|
|
DWORD dw3;
|
|
|
|
dw1 = cchRESOURCE;
|
|
dw3 = cchRESOURCE;
|
|
if (RegEnumValue (hk, ii, szLHS, &dw1,
|
|
0, &dw2, (LPBYTE)szRHS, &dw3) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!FileNameCmp (szRHS, aInstalledDrivers[ iiDriver ].pIDriver->szFile))
|
|
{
|
|
DriverClass dc;
|
|
|
|
if ((dc = dcSection) == dcINVALID)
|
|
dc = GuessDriverClassFromAlias (szLHS);
|
|
|
|
if ((dc == dcINVALID) || (dc == dcOTHER))
|
|
continue;
|
|
|
|
(void)AddIDriver (hTree, aInstalledDrivers[ iiDriver ].pIDriver, dc);
|
|
}
|
|
}
|
|
|
|
RegCloseKey (hk);
|
|
}
|
|
|
|
|
|
#ifdef FIX_BUG_15451
|
|
HWND MakeThisCPLLookLikeTheOldCPL (HWND hWndCPL)
|
|
{
|
|
TCHAR szTitle[ cchRESOURCE ];
|
|
HWND hWndOldCPL = NULL;
|
|
|
|
GetWindowText (hWndCPL, szTitle, cchRESOURCE);
|
|
|
|
for (hWndOldCPL = GetWindow (hWndCPL, GW_HWNDFIRST);
|
|
hWndOldCPL != NULL;
|
|
hWndOldCPL = GetWindow (hWndOldCPL, GW_HWNDNEXT))
|
|
{
|
|
TCHAR szTitleTest[ cchRESOURCE ];
|
|
GetWindowText (hWndOldCPL, szTitleTest, cchRESOURCE);
|
|
if ( (!lstrcmpi (szTitle, szTitleTest)) && (hWndCPL != hWndOldCPL) )
|
|
{
|
|
RECT rOld;
|
|
GetWindowRect (hWndOldCPL, &rOld);
|
|
SetWindowPos (hWndCPL, hWndOldCPL, rOld.left, rOld.top,
|
|
0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
|
|
SetWindowPos (hWndOldCPL, hWndCPL, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
}
|
|
|
|
return hWndOldCPL;
|
|
}
|
|
|
|
|
|
HWND MakeThisDialogLookLikeTheOldDialog (HWND hDlg)
|
|
{
|
|
TCHAR szTitle[ cchRESOURCE ];
|
|
RECT rOld;
|
|
POINT pt;
|
|
HWND hWndOldDlg;
|
|
|
|
GetWindowText (hDlg, szTitle, cchRESOURCE);
|
|
|
|
for (hWndOldDlg = GetWindow (hDlg, GW_HWNDFIRST);
|
|
hWndOldDlg != NULL;
|
|
hWndOldDlg = GetWindow (hWndOldDlg, GW_HWNDNEXT))
|
|
{
|
|
TCHAR szTitleTest[ cchRESOURCE ];
|
|
GetWindowText (hWndOldDlg, szTitleTest, cchRESOURCE);
|
|
if ( (!lstrcmpi (szTitle, szTitleTest)) && (hDlg != hWndOldDlg) )
|
|
{
|
|
GetWindowRect (hWndOldDlg, &rOld);
|
|
|
|
SetWindowPos (hDlg, NULL, rOld.left, rOld.top, 0, 0,
|
|
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
|
return hWndOldDlg;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL WaitForNewCPLWindow (HWND hWndMyDlg)
|
|
{
|
|
TCHAR szTitle[ cchRESOURCE ];
|
|
HWND hWnd;
|
|
DWORD tickStart;
|
|
|
|
#define msecMAXWAIT 5000
|
|
|
|
hWndMyDlg = GetParent (hWndMyDlg); // (hWndMyDlg was a property sheet)
|
|
|
|
GetWindowText (hWndMyDlg, szTitle, cchRESOURCE);
|
|
|
|
for (tickStart = GetTickCount();
|
|
GetTickCount() - tickStart < msecMAXWAIT;)
|
|
{
|
|
MSG msg;
|
|
|
|
for (hWnd = GetWindow (hWndMyDlg, GW_HWNDFIRST);
|
|
hWnd != NULL;
|
|
hWnd = GetWindow (hWnd, GW_HWNDNEXT))
|
|
{
|
|
TCHAR szTitleTest[ cchRESOURCE ];
|
|
if (!IsWindowVisible (hWnd))
|
|
continue;
|
|
GetWindowText (hWnd, szTitleTest, cchRESOURCE);
|
|
if ( (!lstrcmpi (szTitle, szTitleTest)) && (hWnd != hWndMyDlg) )
|
|
{
|
|
PropSheet_PressButton (hWndMyDlg, PSBTN_CANCEL);
|
|
hWnd = GetParent (GetParent (hAdvDlgTree));
|
|
PropSheet_PressButton (hWnd, PSBTN_CANCEL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
{
|
|
if (GetMessage (&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
|
|
|
|
// Prevent any more REMOVE button presses
|
|
// Otherwise one can get stacked up and cause trouble,
|
|
// particularly if it is assocated with a driver that
|
|
// is automatically removed. We have to use a static
|
|
// as any focus changes cause the button to change state.
|
|
//
|
|
static long fWorking = 0;
|
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
*
|
|
* AdvDlg ()
|
|
*
|
|
* Display list of installed installable drivers. Return TRUE/FALSE
|
|
* indicating if should restart windows.
|
|
*
|
|
********************************************************************/
|
|
|
|
const static DWORD aAdvDlgHelpIds[] = { // Context Help IDs
|
|
IDC_ADV_TREE, IDH_GENERIC_DEVICES,
|
|
ID_ADV_PROP, IDH_ADV_PROPERTIES,
|
|
ID_ADV_REMOVE, IDH_MMCPL_DEVPROP_REMOVE,
|
|
0, 0
|
|
};
|
|
|
|
void MapDriverClass(DWORD_PTR dwSetupClass)
|
|
{
|
|
g_dcFilterClass = dcINVALID;
|
|
|
|
switch (dwSetupClass)
|
|
{
|
|
case IS_MS_MMMCI :
|
|
{
|
|
g_dcFilterClass = dcMCI;
|
|
}
|
|
break;
|
|
|
|
case IS_MS_MMVID :
|
|
{
|
|
g_dcFilterClass = dcVCODEC;
|
|
}
|
|
break;
|
|
|
|
case IS_MS_MMACM :
|
|
{
|
|
g_dcFilterClass = dcACODEC;
|
|
}
|
|
break;
|
|
|
|
case IS_MS_MMVCD :
|
|
{
|
|
g_dcFilterClass = dcVIDCAP;
|
|
}
|
|
break;
|
|
|
|
case IS_MS_MMDRV :
|
|
{
|
|
g_dcFilterClass = dcLEGACY;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
INT_PTR AdvDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HANDLE hWndI, hWnd;
|
|
PIDRIVER pIDriver;
|
|
DWORD_PTR dwType = 0;
|
|
|
|
switch ( uMsg )
|
|
{
|
|
case WM_INITDIALOG:
|
|
#ifdef FIX_BUG_15451
|
|
if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
|
|
{
|
|
MakeThisCPLLookLikeTheOldCPL (GetParent(hDlg));
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
|
|
wsStartWait();
|
|
|
|
if (lParam)
|
|
{
|
|
dwType = ((LPPROPSHEETPAGE) lParam)->lParam;
|
|
}
|
|
|
|
MapDriverClass(dwType);
|
|
|
|
hWndI = GetDlgItem(hDlg, IDC_ADV_TREE);
|
|
SendMessage(hWndI,WM_SETREDRAW, FALSE, 0L);
|
|
|
|
InitAdvDlgTree (hWndI); // initialize the treeview display
|
|
|
|
/*
|
|
* Handle the fact that we may not be able to update our .ini
|
|
* sections
|
|
*
|
|
*/
|
|
|
|
IniFileWriteAllowed = CheckIniAccess();
|
|
|
|
// Note nasty sneaky hack: using (A|B) instead of (A&&B)
|
|
// makes both functions evaluate in either success or
|
|
// failure cases.
|
|
//
|
|
IniFileReadAllowed = ( InitInstalled (hDlg, szDrivers) |
|
|
InitInstalled (hDlg, szMCI) );
|
|
|
|
FillTreeInAdvDlg (GetDlgItem (hDlg, IDC_ADV_TREE), NULL);
|
|
|
|
wsEndWait();
|
|
|
|
if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
|
|
{
|
|
TCHAR szCantAdd[120];
|
|
EnableWindow(GetDlgItem(hDlg, ID_ADV_ADD),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, ID_ADV_REMOVE),FALSE);
|
|
LoadString(myInstance,IDS_CANTADD,szCantAdd,sizeof(szCantAdd)/sizeof(TCHAR));
|
|
MessageBox(hDlg, szCantAdd, szError,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
}
|
|
|
|
|
|
SendMessage (hWndI, WM_SETREDRAW, TRUE, 0L);
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
hWndI = GetDlgItem(hDlg, IDC_ADV_TREE);
|
|
hWndMain = hDlg;
|
|
|
|
pIDriver = GetSelectedIDriver (hWndI);
|
|
|
|
switch ( LOWORD(wParam ))
|
|
{
|
|
case ID_ADV_PROP:
|
|
{
|
|
HTREEITEM htiCur = TreeView_GetSelection (hWndI);
|
|
DriverClass dc = GuessDriverClassFromTreeItem (htiCur);
|
|
|
|
if (fWorking)
|
|
break;
|
|
|
|
++fWorking; // Just starting an operation
|
|
|
|
if( dc == dcJOY )
|
|
// We want to run "control joy.cpl" when the joystick devices
|
|
// are highlight and the user clicks Properties buttons.
|
|
RunJoyControlPanel();
|
|
else
|
|
ShowDeviceProperties (hDlg, TreeView_GetSelection(hWndI));
|
|
|
|
--fWorking; // Finished with this operation
|
|
}
|
|
break;
|
|
|
|
case ID_WHATSTHIS:
|
|
{
|
|
WinHelp((HWND)GetDlgItem (hDlg, IDC_ADV_TREE),
|
|
gszWindowsHlp, HELP_WM_HELP,
|
|
(UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
|
|
}
|
|
break;
|
|
|
|
case ID_ADV_REMOVE:
|
|
{
|
|
HWND hTree = GetDlgItem (hDlg, IDC_ADV_TREE);
|
|
HTREEITEM htiCur = TreeView_GetSelection (hTree);
|
|
DriverClass dc = GuessDriverClassFromTreeItem (htiCur);
|
|
PIDRIVER pid;
|
|
LONG_PTR Status;
|
|
|
|
if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
|
|
break; // (button should be disabled)
|
|
|
|
if( dc == dcJOY ) {
|
|
RunJoyControlPanel();
|
|
break;
|
|
}
|
|
|
|
if (TreeView_GetParent (hAdvDlgTree, htiCur) &&
|
|
TreeView_GetGrandParent (hAdvDlgTree, htiCur) &&
|
|
(GuessDriverClassFromTreeItem (
|
|
TreeView_GetGrandParent (hAdvDlgTree, htiCur)
|
|
) == dcMIDI))
|
|
{
|
|
TV_ITEM tvi;
|
|
PINSTRUM pin;
|
|
|
|
tvi.mask = TVIF_PARAM;
|
|
tvi.hItem = htiCur;
|
|
tvi.lParam = 0;
|
|
TreeView_GetItem(hAdvDlgTree, &tvi);
|
|
|
|
if ((pin = (PINSTRUM)tvi.lParam) != NULL)
|
|
{
|
|
RemoveInstrumentByKeyName (pin->szKey);
|
|
RefreshAdvDlgTree ();
|
|
KickMapper (hDlg);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ((pid = FindIDriverByTreeItem (htiCur)) == NULL)
|
|
break;
|
|
|
|
if (dc == dcLEGACY)
|
|
{
|
|
dc = GuessDriverClass(pid);
|
|
}
|
|
|
|
if (pid->szAlias[0] == TEXT('\0'))
|
|
{
|
|
TCHAR szCantRemove[ cchRESOURCE ];
|
|
GetString(szCantRemove, IDS_ACMREMOVEFAIL);
|
|
MessageBox(hDlg, szCantRemove, szError,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
break;
|
|
}
|
|
|
|
if (fWorking)
|
|
break;
|
|
++fWorking; // Just starting an operation
|
|
|
|
if (QueryRemoveDrivers (hDlg, pid->szAlias, pid->szDesc))
|
|
{
|
|
if ((Status = PostRemove (pid, TRUE)) != DRVCNF_CANCEL)
|
|
{
|
|
switch (dc)
|
|
{
|
|
case dcMIDI:
|
|
break;
|
|
case dcACODEC:
|
|
acmDeleteCodec (LOWORD(pid->lp),
|
|
HIWORD(pid->lp));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
iRestartMessage = IDS_RESTART_REM;
|
|
|
|
if (Status == DRVCNF_RESTART)
|
|
{
|
|
DialogBox (myInstance,
|
|
MAKEINTRESOURCE(DLG_RESTART),
|
|
hDlg,
|
|
RestartDlg);
|
|
}
|
|
}
|
|
}
|
|
|
|
--fWorking; // Finished with this operation
|
|
}
|
|
break;
|
|
|
|
case ID_ADV_ADD:
|
|
{
|
|
HTREEITEM htiCur = TreeView_GetSelection (hWndI);
|
|
DriverClass dc = GuessDriverClassFromTreeItem (htiCur);
|
|
|
|
if ((!IniFileReadAllowed) || (!IniFileWriteAllowed))
|
|
break; // (button should be disabled)
|
|
|
|
if( dc == dcJOY ) {
|
|
RunJoyControlPanel();
|
|
break;
|
|
}
|
|
|
|
if (fWorking)
|
|
break;
|
|
++fWorking; // Just starting an operation
|
|
|
|
bCopyEvenIfOlder = FALSE;
|
|
|
|
DialogBox(myInstance, MAKEINTRESOURCE(DLG_KNOWN), hDlg,
|
|
AvailableDriversDlg);
|
|
|
|
bCopyEvenIfOlder = FALSE;
|
|
|
|
--fWorking; // Finished with this operation
|
|
}
|
|
break;
|
|
|
|
|
|
case ID_ADV_TSHOOT:
|
|
{
|
|
TCHAR szCommand[ MAX_PATH ];
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
LoadString(myInstance,IDS_TSHOOT, szCommand, sizeof(szCommand)/sizeof(TCHAR));
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_NORMAL;
|
|
if (CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) {
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef FIX_BUG_15451
|
|
case ID_INIT:
|
|
if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
|
|
{
|
|
HTREEITEM hti;
|
|
|
|
if ((hti = FindTreeItemByDriverName (
|
|
szDriverWhichNeedsSettings)) != 0)
|
|
{
|
|
TreeView_Expand (hAdvDlgTree,
|
|
TreeView_GetParent(hAdvDlgTree,hti),
|
|
TVE_EXPAND);
|
|
TreeView_SelectItem(hAdvDlgTree,hti);
|
|
FORWARD_WM_COMMAND(hDlg,ID_ADV_PROP,0,0,PostMessage);
|
|
}
|
|
else
|
|
{
|
|
szDriverWhichNeedsSettings[0] = 0;
|
|
}
|
|
}
|
|
break;
|
|
#endif // FIX_BUG_15451
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *lpnm = (NMHDR *)lParam;
|
|
LPNM_TREEVIEW lpnmtv = (LPNM_TREEVIEW)lParam;
|
|
|
|
switch (lpnm->code)
|
|
{
|
|
case PSN_KILLACTIVE:
|
|
FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
FORWARD_WM_COMMAND (hDlg, ID_APPLY, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
FORWARD_WM_COMMAND (hDlg, ID_INIT, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_RESET:
|
|
FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case NM_DBLCLK:
|
|
// show properties or expand/collapse tree node.
|
|
//
|
|
if (lpnm->idFrom == (UINT)IDC_ADV_TREE)
|
|
{
|
|
HWND hTree = GetDlgItem (hDlg, IDC_ADV_TREE);
|
|
HTREEITEM htiCur = TreeView_GetSelection (hTree);
|
|
TV_HITTESTINFO tvht;
|
|
|
|
if (!htiCur)
|
|
break;
|
|
|
|
GetCursorPos (&tvht.pt);
|
|
ScreenToClient (hTree, &tvht.pt);
|
|
TreeView_HitTest (hTree, &tvht);
|
|
|
|
if ( (tvht.flags & TVHT_ONITEM) &&
|
|
(TreeView_GetChild (hTree, htiCur) == NULL) &&
|
|
(IsWindowEnabled (GetDlgItem(hDlg,ID_ADV_PROP))) )
|
|
{
|
|
FORWARD_WM_COMMAND(hDlg,ID_ADV_PROP,0,0,PostMessage);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NM_RCLICK:
|
|
TreeContextMenu (hDlg, GetDlgItem (hDlg, IDC_ADV_TREE));
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
// The TreeView has its own right-click handling, and presents a
|
|
// "What's This?" automatically--so don't handle WM_CONTEXTMENU
|
|
// for that control.
|
|
//
|
|
case WM_CONTEXTMENU:
|
|
if (wParam != (WPARAM)GetDlgItem (hDlg, IDC_ADV_TREE))
|
|
{
|
|
WinHelp((HWND)wParam, gszWindowsHlp, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp(((LPHELPINFO)lParam)->hItemHandle, gszWindowsHlp,
|
|
HELP_WM_HELP, (UINT_PTR)(LPTSTR)aAdvDlgHelpIds);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
FreeAdvDlgTree (GetDlgItem (hDlg, IDC_ADV_TREE));
|
|
return FALSE;
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
*** TreeContextMenu
|
|
*
|
|
* This function displays the context menu that pops up when the
|
|
* user right clicks on any of the tree view items.
|
|
*
|
|
*/
|
|
void TreeContextMenu (HWND hWnd, HWND hKeyTreeWnd)
|
|
{
|
|
DWORD MessagePos;
|
|
POINT MessagePoint;
|
|
TV_HITTESTINFO TVHitTestInfo;
|
|
HMENU hContextMenu;
|
|
HMENU hContextPopupMenu;
|
|
TV_ITEM TVItem;
|
|
int MenuCommand;
|
|
TCHAR szCollapse[32];
|
|
|
|
// dont bring up a menu unless click is on the item.
|
|
//
|
|
MessagePos = GetMessagePos();
|
|
MessagePoint.x = GET_X_LPARAM(MessagePos);
|
|
MessagePoint.y = GET_Y_LPARAM(MessagePos);
|
|
|
|
TVHitTestInfo.pt = MessagePoint;
|
|
ScreenToClient(hKeyTreeWnd, &TVHitTestInfo.pt);
|
|
TVItem.hItem = TreeView_HitTest(hKeyTreeWnd, &TVHitTestInfo);
|
|
|
|
if (TVItem.hItem == NULL)
|
|
return;
|
|
|
|
hContextMenu = LoadMenu(ghInstance, MAKEINTRESOURCE(POPUP_TREE_CONTEXT));
|
|
if (hContextMenu == NULL)
|
|
return;
|
|
|
|
hContextPopupMenu = GetSubMenu (hContextMenu, 0);
|
|
|
|
TVItem.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM;
|
|
TreeView_GetItem(hKeyTreeWnd, &TVItem);
|
|
|
|
// show collapse item because we are expanded?
|
|
//
|
|
if (TVItem.state & TVIS_EXPANDED)
|
|
{
|
|
LoadString(ghInstance, IDS_COLLAPSE, szCollapse, sizeof(szCollapse)/sizeof(TCHAR));
|
|
ModifyMenu(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND | MF_STRING,
|
|
ID_TOGGLE, szCollapse);
|
|
}
|
|
SetMenuDefaultItem (hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND);
|
|
|
|
if (TVItem.cChildren == 0) //gray expand/collaps if no children
|
|
{
|
|
SetMenuDefaultItem(hContextPopupMenu, ID_ADV_PROP, MF_BYCOMMAND);
|
|
EnableMenuItem(hContextPopupMenu, ID_TOGGLE, MF_GRAYED |MF_BYCOMMAND);
|
|
}
|
|
|
|
TreeView_SelectItem (hKeyTreeWnd, TVItem.hItem);
|
|
MenuCommand = TrackPopupMenuEx (hContextPopupMenu,
|
|
TPM_RETURNCMD | TPM_RIGHTBUTTON |
|
|
TPM_LEFTALIGN | TPM_TOPALIGN,
|
|
MessagePoint.x, MessagePoint.y,
|
|
hWnd, NULL);
|
|
|
|
DestroyMenu (hContextMenu);
|
|
FORWARD_WM_COMMAND(hWnd, MenuCommand, 0, 0, SendMessage);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* *
|
|
* *
|
|
* LB_AVAILABLE Dialog Routines *
|
|
* *
|
|
* *
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* DLG: LB_AVAILABLE
|
|
*
|
|
* InitAvailable()
|
|
*
|
|
* Add the available drivers from mmdriver.inf to the passed list box.
|
|
* The format of [Installable.drivers] in setup.inf is:
|
|
* profile=disk#:driverfile,"type1,type2","Installable driver Description","vxd1.386,vxd2.386","opt1,2,3"
|
|
*
|
|
* for example:
|
|
*
|
|
* driver1=6:sndblst.drv,"midi,wave","SoundBlaster MIDI and Waveform drivers","vdmad.386,vadmad.386","3,260"
|
|
*/
|
|
|
|
BOOL InitAvailable(HWND hWnd, int iLine)
|
|
{
|
|
PINF pinf;
|
|
BOOL bInitd=FALSE;
|
|
LPTSTR pstrKey;
|
|
int iIndex;
|
|
LONG lResult;
|
|
TCHAR szDesc[MAX_INF_LINE_LEN];
|
|
|
|
SendMessage(hWnd,WM_SETREDRAW, FALSE, 0L);
|
|
|
|
/*
|
|
* Parse the list of keywords and load their strings
|
|
*/
|
|
|
|
for (pinf = FindInstallableDriversSection(NULL); pinf; pinf = infNextLine(pinf))
|
|
{
|
|
//
|
|
// found at least one keyname!
|
|
//
|
|
pstrKey = (LPTSTR)LocalAlloc(LPTR, (MAX_SYS_INF_LEN * sizeof(TCHAR)));
|
|
if( pstrKey == NULL )
|
|
break;
|
|
|
|
if( ERROR_SUCCESS == infParseField(pinf, 0, pstrKey, MAX_SYS_INF_LEN)
|
|
&& ERROR_SUCCESS == infParseField(pinf, 3, szDesc, SIZEOF(szDesc)) )
|
|
{
|
|
/*
|
|
* add the installable driver's description to listbox, and filename as data
|
|
*/
|
|
if ( (iIndex = (int)SendMessage(hWnd, LB_ADDSTRING, 0, (LONG_PTR)(LPTSTR)szDesc)) != LB_ERR )
|
|
{
|
|
SendMessage(hWnd, LB_SETITEMDATA, iIndex, (LONG_PTR)pstrKey);
|
|
bInitd = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LocalFree( pstrKey );
|
|
pstrKey = NULL;
|
|
}
|
|
}
|
|
|
|
if (iLine == UNLIST_LINE)
|
|
{
|
|
//
|
|
// Add the "Install unlisted..." choice to the top of the list
|
|
// box.
|
|
LoadString(myInstance, IDS_UPDATED, szDesc, sizeof(szDesc)/sizeof(TCHAR));
|
|
if ((iIndex = (int)(LONG)SendMessage(hWnd, LB_INSERTSTRING, 0, (LPARAM)(LPTSTR)szDesc)) != LB_ERR)
|
|
SendMessage(hWnd, LB_SETITEMDATA, (WPARAM)iIndex, (LPARAM)0);
|
|
}
|
|
if (bInitd)
|
|
|
|
SendMessage(hWnd, LB_SETCURSEL, 0, 0L );
|
|
|
|
|
|
SendMessage(hWnd,WM_SETREDRAW, TRUE, 0L);
|
|
return(bInitd);
|
|
}
|
|
|
|
|
|
/*
|
|
* DLG: LB_AVAILABLE
|
|
*
|
|
* RemoveAvailable()
|
|
*
|
|
* Remove all drivers from the listbox and free all storage associated with
|
|
* the keyname
|
|
*/
|
|
|
|
void RemoveAvailable(HWND hWnd)
|
|
{
|
|
int iIndex;
|
|
HWND hWndA;
|
|
LPTSTR pstrKey;
|
|
|
|
hWndA = GetDlgItem(hWnd, LB_AVAILABLE);
|
|
iIndex = (int)SendMessage(hWndA, LB_GETCOUNT, 0, 0L);
|
|
while ( iIndex-- > 0)
|
|
{
|
|
if (( (pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex,
|
|
0L)) != (LPTSTR)LB_ERR ) && pstrKey)
|
|
LocalFree((HLOCAL)pstrKey);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* DLG: LB_AVAILABLE
|
|
*
|
|
* AvailableDriversDlg()
|
|
*
|
|
* List the available installable drivers or return FALSE if there are none.
|
|
*/
|
|
|
|
const static DWORD aAvailDlgHelpIds[] = { // Context Help IDs
|
|
LB_AVAILABLE, IDH_ADD_DRIVER_LIST,
|
|
ID_DRVSTRING, IDH_ADD_DRIVER_LIST,
|
|
|
|
0, 0
|
|
};
|
|
|
|
INT_PTR AvailableDriversDlg(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPTSTR pstrKey; //-jyg- added
|
|
|
|
HWND hWndA;
|
|
int iIndex;
|
|
|
|
switch ( uMsg )
|
|
{
|
|
case WM_INITDIALOG:
|
|
ShowWindow(hWnd, TRUE);
|
|
wsStartWait();
|
|
if (pinfOldDefault)
|
|
{
|
|
infSetDefault(pinfOldDefault);
|
|
pinfOldDefault = NULL;
|
|
}
|
|
|
|
if ( !InitAvailable(hWndA = GetDlgItem(hWnd, LB_AVAILABLE), UNLIST_LINE))
|
|
{
|
|
/*
|
|
* We weren't able to find the [installable.drivers] section
|
|
* of the
|
|
* mmdriver.inf OR it was corrupt. Go ahead and query the
|
|
* user to find an oemsetup.inf to make our default. This
|
|
* is a bad state.
|
|
*/
|
|
EndDialog(hWnd, FALSE);
|
|
bFindOEM = TRUE;
|
|
wcscpy(szDrv, szOemInf);
|
|
if (DialogBox(myInstance, MAKEINTRESOURCE(DLG_INSERTDISK),
|
|
hWnd, AddDriversDlg) == TRUE)
|
|
PostMessage(hWnd, WM_INITDIALOG, 0, 0L);
|
|
else
|
|
pinfOldDefault = infSetDefault(pinfOldDefault);
|
|
|
|
bFindOEM = FALSE;
|
|
}
|
|
wsEndWait();
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch ( LOWORD(wParam ))
|
|
{
|
|
case LB_AVAILABLE:
|
|
|
|
// Hm... We've picked it.
|
|
|
|
if ( HIWORD(wParam) == LBN_DBLCLK )
|
|
SendMessage(hWnd, WM_COMMAND, IDOK, 0L);
|
|
break;
|
|
|
|
case IDOK:
|
|
|
|
/*
|
|
* We've made our selection
|
|
*/
|
|
|
|
hWndA = GetDlgItem(hWnd, LB_AVAILABLE);
|
|
|
|
if ( (iIndex = (int)SendMessage(hWndA, LB_GETCURSEL, 0, 0L)) != LB_ERR)
|
|
{
|
|
if (!iIndex)
|
|
{
|
|
/*
|
|
* The first entry is for OEMs
|
|
*/
|
|
|
|
INT_PTR iFound;
|
|
bBadOemSetup = FALSE;
|
|
|
|
bCopyEvenIfOlder = TRUE;
|
|
bFindOEM = TRUE;
|
|
hMesgBoxParent = hWnd;
|
|
while ((iFound = DialogBox(myInstance,
|
|
MAKEINTRESOURCE(DLG_INSERTDISK), hWnd,
|
|
AddDriversDlg)) == 2);
|
|
if (iFound == 1)
|
|
{
|
|
RemoveAvailable(hWnd);
|
|
SendDlgItemMessage(hWnd, LB_AVAILABLE,
|
|
LB_RESETCONTENT, 0, 0L);
|
|
PostMessage(hWnd, WM_INITDIALOG, 0, 0L);
|
|
}
|
|
bFindOEM = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The user selected an entry from our .inf
|
|
*/
|
|
|
|
wsStartWait();
|
|
|
|
/*
|
|
* The data associated with the list item is
|
|
* the driver key name (field 0 in the inf file).
|
|
*/
|
|
|
|
pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex, 0L);
|
|
bCopyingRelated = FALSE;
|
|
bQueryExist = TRUE;
|
|
|
|
if (InstallDrivers(hWndMain, hWnd, pstrKey))
|
|
{
|
|
RefreshAdvDlgTree ();
|
|
wsEndWait();
|
|
|
|
|
|
/*
|
|
* If bRestart is true then the system must
|
|
* be restarted to activate these changes
|
|
*/
|
|
|
|
if (bRestart)
|
|
{
|
|
iRestartMessage= IDS_RESTART_ADD;
|
|
DialogBox(myInstance,
|
|
MAKEINTRESOURCE(DLG_RESTART), hWnd,
|
|
RestartDlg);
|
|
}
|
|
}
|
|
else
|
|
wsEndWait();
|
|
|
|
bRestart = FALSE;
|
|
bRelated = FALSE;
|
|
}
|
|
}
|
|
EndDialog(hWnd, FALSE);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hWnd, FALSE);
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND)wParam, gszWindowsHlp, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPTSTR)aAvailDlgHelpIds);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp(((LPHELPINFO)lParam)->hItemHandle, gszWindowsHlp,
|
|
HELP_WM_HELP, (UINT_PTR)(LPTSTR)aAvailDlgHelpIds);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
//
|
|
// free the strings added as DATAITEM to the avail list
|
|
|
|
RemoveAvailable(hWnd);
|
|
return(FALSE);
|
|
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL DriversDllInitialize( IN PVOID hInstance
|
|
, IN DWORD ulReason
|
|
, IN PCONTEXT pctx OPTIONAL
|
|
)
|
|
{
|
|
if (ulReason != DLL_PROCESS_ATTACH)
|
|
return TRUE;
|
|
|
|
myInstance = hInstance;
|
|
LoadString(myInstance, IDS_CLOSE, aszClose, sizeof(aszClose)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_DRIVERDESC, szDriversDesc, sizeof(szDriversDesc)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_FILE_ERROR, szFileError, sizeof(szFileError)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_INSTALLDRIVERS, szMDrivers, sizeof(szMDrivers)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_INSTALLDRIVERS32, szMDrivers32, sizeof(szMDrivers)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_RELATEDDESC, szRelatedDesc, sizeof(szRelatedDesc)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_USERINSTALLDRIVERS, szUserDrivers, sizeof(szUserDrivers)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_UNLISTED, (LPTSTR)szUnlisted, sizeof(szUnlisted)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_KNOWN, szKnown, sizeof(szKnown)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_OEMSETUP, szOemInf, sizeof(szOemInf)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_SYSTEM, szSystem, sizeof(szSystem)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_OUT_OF_REMOVE_SPACE, szOutOfRemoveSpace, sizeof(szOutOfRemoveSpace)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_NO_DESCRIPTION, szNoDesc, sizeof(szNoDesc)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_ERRORBOX, szError, sizeof(szError)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_REMOVEORNOT, szRemoveOrNot, sizeof(szRemoveOrNot)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_REMOVEORNOTSTRICT, szRemoveOrNotStrict, sizeof(szRemoveOrNotStrict)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_SETUPINF, szSetupInf, sizeof(szSetupInf)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_APPNAME, szAppName, sizeof(szAppName)/sizeof(TCHAR));
|
|
|
|
LoadString(myInstance, IDS_DRIVERS, szDrivers, sizeof(szDrivers)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_REMOVE, szRemove, sizeof(szRemove)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_CONTROLINI, szControlIni, sizeof(szControlIni)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_SYSINI, szSysIni, sizeof(szSysIni)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_MCI, szMCI, sizeof(szMCI)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_DEFDRIVE, szDirOfSrc, sizeof(szDirOfSrc)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_CONTROL_HLP_FILE, szDriversHlp, sizeof(szDriversHlp)/sizeof(TCHAR));
|
|
LoadString(myInstance, IDS_LASTQUERY, szLastQuery, sizeof(szLastQuery)/sizeof(TCHAR));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void DeleteCPLCache(void)
|
|
{
|
|
HKEY hKeyCache;
|
|
|
|
if (ERROR_SUCCESS ==
|
|
RegOpenKey(HKEY_CURRENT_USER,
|
|
TEXT("Control Panel\\Cache\\multimed.cpl"),
|
|
&hKeyCache)) {
|
|
for ( ; ; ) {
|
|
TCHAR Name[MAX_PATH+1]; // This is the max size that RegEnumKey()+NULL can return
|
|
|
|
if (ERROR_SUCCESS ==
|
|
RegEnumKey(hKeyCache,
|
|
0,
|
|
Name,
|
|
ARRAYSIZE(Name))) {
|
|
HKEY hSubKey;
|
|
|
|
RegDeleteKey(hKeyCache, Name);
|
|
} else {
|
|
break; // leave loop
|
|
}
|
|
}
|
|
|
|
RegDeleteKey(hKeyCache, NULL);
|
|
RegCloseKey(hKeyCache);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** RestartDlg()
|
|
**
|
|
** Offer user the choice to (not) restart windows.
|
|
*/
|
|
INT_PTR RestartDlg(HWND hDlg, unsigned uiMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uiMessage)
|
|
{
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDCANCEL:
|
|
//
|
|
// don't restart windows
|
|
//
|
|
EndDialog(hDlg, FALSE);
|
|
break;
|
|
|
|
case IDOK:
|
|
//
|
|
// do restart windows, *dont* dismiss dialog incase
|
|
// the user canceled it.
|
|
//
|
|
ReBoot(hDlg);
|
|
SetActiveWindow(hDlg);
|
|
//EndDialog(hDlg, TRUE);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
case WM_INITDIALOG:
|
|
/*
|
|
** Delete the control panel's cache so it will get it
|
|
** right!
|
|
*/
|
|
|
|
DeleteCPLCache();
|
|
|
|
|
|
if (iRestartMessage)
|
|
{
|
|
TCHAR szMesg1[300];
|
|
TCHAR szMesg2[300];
|
|
|
|
LoadString(myInstance, iRestartMessage, szMesg1, sizeof(szMesg1)/sizeof(TCHAR));
|
|
wsprintf(szMesg2, szMesg1, (LPTSTR)szRestartDrv);
|
|
SetDlgItemText(hDlg, IDS_RESTARTTEXT, (LPTSTR)szMesg2);
|
|
|
|
if (iRestartMessage == IDS_RESTART_NOSOUND)
|
|
{
|
|
PostMessage (hDlg, WM_NEXTDLGCTL,
|
|
(WPARAM)GetDlgItem(hDlg,IDOK), (LPARAM)TRUE);
|
|
}
|
|
}
|
|
return TRUE;
|
|
|
|
case WM_KEYUP:
|
|
if (wParam == VK_F3)
|
|
//
|
|
// don't restart windows
|
|
//
|
|
EndDialog(hDlg, FALSE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* UserInstalled()
|
|
*
|
|
*
|
|
*/
|
|
|
|
BOOL UserInstalled(LPTSTR szKey)
|
|
{
|
|
TCHAR buf[MAXSTR];
|
|
LPTSTR lpstr = NULL;
|
|
ZeroMemory (buf, sizeof (buf)); // make prefix happy.
|
|
|
|
lpstr = GetProfile (szUserDrivers, (LPTSTR)szKey, szControlIni, buf, sizeof(buf));
|
|
if (lpstr && *lpstr != TEXT('\0'))
|
|
return(TRUE);
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* AddUnlistedDlg()
|
|
*
|
|
* The following function processes requests by the user to install unlisted
|
|
* or updated drivers.
|
|
*
|
|
* PARAMETERS: The normal Dialog box parameters
|
|
* RETURN VALUE: The usual Dialog box return value
|
|
*/
|
|
|
|
INT_PTR AddUnlistedDlg(HWND hDlg, unsigned nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (nMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND hListDrivers;
|
|
BOOL bFoundDrivers;
|
|
|
|
wsStartWait();
|
|
hListDrivers = GetDlgItem(hDlg, LB_UNLISTED);
|
|
|
|
/* Search for drivers */
|
|
bFoundDrivers = InitAvailable(hListDrivers, NO_UNLIST_LINE);
|
|
if (!bFoundDrivers)
|
|
{
|
|
//
|
|
// We weren't able to find the MMDRIVERS section of the
|
|
// setup.inf OR it was corrupt. Go ahead and query the
|
|
// user to find an oemsetup.inf to make our default. This
|
|
// is a bad state.
|
|
//
|
|
|
|
INT_PTR iFound;
|
|
|
|
bFindOEM = TRUE;
|
|
bBadOemSetup = TRUE;
|
|
while ((iFound = DialogBox(myInstance,
|
|
MAKEINTRESOURCE(DLG_INSERTDISK), hMesgBoxParent,
|
|
AddDriversDlg)) == 2);
|
|
bFindOEM = FALSE;
|
|
if (iFound == 1)
|
|
{
|
|
SendDlgItemMessage(hDlg, LB_AVAILABLE,
|
|
LB_RESETCONTENT, 0, 0L);
|
|
PostMessage(hDlg, WM_INITDIALOG, 0, 0L);
|
|
}
|
|
EndDialog(hDlg, FALSE);
|
|
}
|
|
SendMessage(hListDrivers, LB_SETCURSEL, 0, 0L);
|
|
wsEndWait();
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDH_DLG_ADD_UNKNOWN:
|
|
goto DoHelp;
|
|
|
|
case LB_UNLISTED:
|
|
if (HIWORD(wParam) != LBN_DBLCLK)
|
|
break;
|
|
|
|
// else Fall through here
|
|
case IDOK:
|
|
{
|
|
HWND hWndA;
|
|
int iIndex;
|
|
LPTSTR pstrKey;
|
|
|
|
hWndA = GetDlgItem(hDlg, LB_UNLISTED);
|
|
if ( (iIndex = (int)SendMessage(hWndA, LB_GETCURSEL, 0, 0L))
|
|
!= LB_ERR)
|
|
{
|
|
wsStartWait();
|
|
pstrKey = (LPTSTR)SendMessage(hWndA, LB_GETITEMDATA, iIndex, 0L);
|
|
bCopyingRelated = FALSE;
|
|
bQueryExist = TRUE;
|
|
if (InstallDrivers(hWndMain, hDlg, pstrKey))
|
|
{
|
|
RefreshAdvDlgTree ();
|
|
wsEndWait();
|
|
|
|
if (bRestart)
|
|
{
|
|
iRestartMessage= IDS_RESTART_ADD;
|
|
DialogBox(myInstance, MAKEINTRESOURCE(DLG_RESTART),
|
|
hDlg, RestartDlg);
|
|
}
|
|
}
|
|
else
|
|
wsEndWait();
|
|
bRelated = FALSE;
|
|
bRestart = FALSE;
|
|
}
|
|
EndDialog(hDlg, FALSE);
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, wParam);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
DoHelp:
|
|
WinHelp (hDlg, gszWindowsHlp, HELP_CONTEXT, IDH_MMCPL_DEVPROP_ENABLE);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
/*
|
|
* ReBoot()
|
|
*
|
|
* Restart the system. If this fails we put up a message box
|
|
*/
|
|
|
|
void ReBoot(HWND hDlg)
|
|
{
|
|
DWORD Error;
|
|
BOOLEAN WasEnabled;
|
|
|
|
/*
|
|
* We must adjust our privilege level to be allowed to restart the
|
|
* system
|
|
*/
|
|
|
|
RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
/*
|
|
* Try to reboot the system
|
|
*/
|
|
|
|
if (!ExitWindowsEx(EWX_REBOOT, 0xFFFFFFFF)) {
|
|
|
|
Error = GetLastError();
|
|
|
|
/*
|
|
* Put up a message box if we failed
|
|
*/
|
|
|
|
if (Error != NO_ERROR) {
|
|
TCHAR szCantRestart[80];
|
|
LoadString(myInstance,
|
|
Error == ERROR_PRIVILEGE_NOT_HELD ||
|
|
Error == ERROR_NOT_ALL_ASSIGNED ||
|
|
Error == ERROR_ACCESS_DENIED ?
|
|
IDS_CANNOT_RESTART_PRIVILEGE :
|
|
IDS_CANNOT_RESTART_UNKNOWN,
|
|
szCantRestart,
|
|
sizeof(szCantRestart)/sizeof(TCHAR));
|
|
|
|
MessageBox(hDlg, szCantRestart, szError,
|
|
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void OpenDriverError(HWND hDlg, LPTSTR szDriver, LPTSTR szFile)
|
|
{
|
|
TCHAR szMesg[MAXSTR];
|
|
TCHAR szMesg2[MAXSTR];
|
|
|
|
LoadString(myInstance, IDS_INSTALLING_DRIVERS, szMesg, sizeof(szMesg)/sizeof(TCHAR));
|
|
wsprintf(szMesg2, szMesg, szDriver, szFile);
|
|
MessageBox(hDlg, szMesg2, szError, MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
*** AddIDriver - Adds a treeitem referencing the given PIDRIVER
|
|
*
|
|
* Note that the listed PIDRIVER should already have been added to the
|
|
* aInstalledDrivers array (via AddIDriverToArray()) before calling this
|
|
* routine.
|
|
*
|
|
*/
|
|
|
|
HTREEITEM AddIDriver (HWND hTree, PIDRIVER pIDriver, DriverClass dc)
|
|
{
|
|
short idr;
|
|
TV_INSERTSTRUCT ti;
|
|
HTREEITEM hti;
|
|
short ii;
|
|
TCHAR szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
|
|
TCHAR szExt[ _MAX_EXT +1 ];
|
|
TCHAR szDesc[ cchRESOURCE ];
|
|
|
|
// don't add an entry for one of the to-be-skipped drivers
|
|
//
|
|
lsplitpath (pIDriver->szFile, NULL, NULL, szFile, szExt);
|
|
|
|
if (szExt[0] != TEXT('\0'))
|
|
lstrcat (szFile, szExt);
|
|
|
|
//check to see if we're trying to put a PNP driver into the legacy tree
|
|
if (g_dcFilterClass == dcLEGACY)
|
|
{
|
|
if ((dc == dcWAVE) ||
|
|
(dc == dcMIDI) ||
|
|
(dc == dcMIXER) ||
|
|
(dc == dcAUX))
|
|
{
|
|
if (IsPnPDriver(szFile))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dc != dcMIDI)
|
|
{
|
|
for (ii = 0; ii < nDriversToSKIP; ii++)
|
|
{
|
|
if (!lstrcmpi (szFile, aDriversToSKIP[ ii ]))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// If we were given a DriverClass, then the caller has
|
|
// specified where we should create an entry--add the "Audio for"
|
|
// (etc) tag before the description, and add it.
|
|
//
|
|
// Otherwise, determine where this driver belongs in the tree
|
|
//
|
|
if (dc != dcINVALID)
|
|
{
|
|
TCHAR szTag[ cchRESOURCE ];
|
|
|
|
switch (dc)
|
|
{
|
|
case dcWAVE: GetString (szTag, IDS_AUDIOFOR);
|
|
break;
|
|
case dcMIDI: GetString (szTag, IDS_MIDIFOR);
|
|
break;
|
|
case dcMIXER: GetString (szTag, IDS_MIXERFOR);
|
|
break;
|
|
case dcAUX: GetString (szTag, IDS_AUXFOR);
|
|
break;
|
|
default: lstrcpy (szTag, TEXT("%s"));
|
|
break;
|
|
}
|
|
|
|
wsprintf (szDesc, szTag, pIDriver->szDesc);
|
|
}
|
|
else
|
|
{
|
|
if ((dc = GuessDriverClass (pIDriver)) == dcINVALID)
|
|
return FALSE;
|
|
|
|
lstrcpy (szDesc, pIDriver->szDesc);
|
|
}
|
|
|
|
// map that classification into an index within the
|
|
// root entries of the tree (aDriverRoot[])
|
|
//
|
|
if ((idr = DriverClassToRootIndex (dc)) == -1)
|
|
return FALSE;
|
|
|
|
// if this driver already has an entry under this DriverClass,
|
|
// then don't add another.
|
|
//
|
|
for (ii =0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == pIDriver)
|
|
break;
|
|
}
|
|
if (ii >= cInstalledDrivers)
|
|
{
|
|
ii = (short)NOPIDRIVER;
|
|
}
|
|
else if (aInstalledDrivers[ ii ].dwBits & aDriverRoot[ idr ].dwBit)
|
|
{
|
|
return FALSE; // Already have an entry here!
|
|
}
|
|
|
|
// since not all roots need exist all the time, make sure
|
|
// this classification HAS a root in the tree
|
|
//
|
|
if (!EnsureRootIndexExists (hTree, idr))
|
|
return FALSE;
|
|
|
|
// finally, insert an item into the tree for this driver
|
|
// note that for audio codecs to be sorted properly, they must
|
|
// be added via this routine in their appropriate order--ie,
|
|
// call this routine for the highest-priority codec first.
|
|
//
|
|
ti.hParent = aDriverRoot[ idr ].hti;
|
|
ti.hInsertAfter = (dc == dcACODEC) ? TVI_LAST : TVI_SORT;
|
|
ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
ti.item.iImage = (int)idr;
|
|
ti.item.iSelectedImage = (int)idr;
|
|
ti.item.pszText = szDesc;
|
|
ti.item.lParam = ii;
|
|
|
|
if ((hti = TreeView_InsertItem (hTree, &ti)) == NULL)
|
|
return FALSE;
|
|
|
|
if (ii != NOPIDRIVER)
|
|
aInstalledDrivers[ ii ].dwBits |= aDriverRoot[ idr ].dwBit;
|
|
|
|
return hti;
|
|
}
|
|
|
|
|
|
BOOL AddIDriverByName (HWND hTree, LPCWSTR wszFile, DriverClass dc)
|
|
{
|
|
LPTSTR pch;
|
|
TCHAR tszFile[ max(cchRESOURCE, MAX_PATH) ];
|
|
PIDRIVER pid;
|
|
|
|
#ifdef UNICODE
|
|
lstrcpy (tszFile, wszFile);
|
|
#else
|
|
wcstombs (tszFile, wszFile, cchRESOURCE);
|
|
#endif
|
|
|
|
// Strip off any trailing whitespace
|
|
//
|
|
if (tszFile[0] == TEXT('\0'))
|
|
return FALSE;
|
|
|
|
for (pch = &tszFile[ lstrlen(tszFile)-1 ];
|
|
pch >= tszFile && (*pch == TEXT('\t') || *pch == TEXT(' '));
|
|
--pch)
|
|
;
|
|
*(1+pch) = TEXT('\0');
|
|
|
|
// If this is MMDRV.DLL, then it's possibly providing the
|
|
// user-mode component for kernel-mode drivers. Since it's
|
|
// apparently impossible to determine the name of the .SYS
|
|
// file which is providing a "\\.\WaveIn0" device (etc),
|
|
// we'll use a hack: Check around for anyone registered
|
|
// under the alias "Kernel", and use that.
|
|
//
|
|
if (!lstrcmpi (tszFile, cszMMDRVDLL))
|
|
{
|
|
mysize_t ii;
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == NULL)
|
|
continue;
|
|
if (!lstrnicmp (aInstalledDrivers[ ii ].pIDriver->szAlias,
|
|
cszAliasKERNEL,
|
|
lstrlen(cszAliasKERNEL)))
|
|
{
|
|
lstrcpy (tszFile, aInstalledDrivers[ ii ].pIDriver->szFile);
|
|
break;
|
|
}
|
|
}
|
|
if (ii >= cInstalledDrivers)
|
|
return FALSE;
|
|
}
|
|
|
|
// Find the driver in the aInstalledDriver array, and add
|
|
// an entry for it in the tree.
|
|
//
|
|
if ((pid = FindIDriverByName (tszFile)) == NULL)
|
|
return FALSE;
|
|
|
|
if (AddIDriver (hTree, pid, dc) == NULL)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*** RemoveIDriver - Removes (and optionally frees) an IDRIVER from hAdvDlgTree
|
|
*
|
|
*/
|
|
|
|
void RemoveIDriver (HWND hTree, PIDRIVER pIDriver, BOOL fFreeToo)
|
|
{
|
|
mysize_t ii;
|
|
short idr;
|
|
HTREEITEM hti;
|
|
|
|
// Find each TreeItem which references this entry.
|
|
//
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if ((hti = aDriverRoot[ idr ].hti) == NULL)
|
|
continue;
|
|
|
|
hti = TreeView_GetChild (hTree, hti);
|
|
while (hti != NULL)
|
|
{
|
|
if (pIDriver != FindIDriverByTreeItem (hti))
|
|
{
|
|
hti = TreeView_GetNextSibling (hTree, hti);
|
|
continue;
|
|
}
|
|
|
|
// We found a tree item which uses this driver, so delete the
|
|
// item. Also note that this may cause the driver's parent
|
|
// node to no longer be necessary.
|
|
//
|
|
|
|
TreeView_DeleteItem (hTree, hti);
|
|
hti = TreeView_GetChild (hTree, aDriverRoot[ idr ].hti);
|
|
|
|
if (!aDriverRoot[ idr ].fAlwaysMake) // may no longer need parent?
|
|
{
|
|
if (hti == NULL) // parent now has no children?
|
|
{
|
|
TreeView_DeleteItem (hTree, aDriverRoot[ idr ].hti);
|
|
aDriverRoot[ idr ].hti = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if we can find the given pIDriver within the
|
|
// aInstalledDriver array.
|
|
//
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == pIDriver)
|
|
{
|
|
aInstalledDrivers[ ii ].dwBits = 0L; // no longer in tree at all
|
|
|
|
if (fFreeToo)
|
|
{
|
|
LocalFree ((HANDLE)aInstalledDrivers[ ii ].pIDriver);
|
|
aInstalledDrivers[ ii ].pIDriver = NULL;
|
|
}
|
|
|
|
break; // There's only one entry in this array for each pIDriver
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef FIX_BUG_15451
|
|
HTREEITEM FindTreeItemByDriverName (LPTSTR pszName)
|
|
{
|
|
PIDRIVER pid;
|
|
short idr;
|
|
HTREEITEM hti;
|
|
|
|
if ((pid = FindIDriverByName (pszName)) == NULL)
|
|
return (HTREEITEM)0;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if ((hti = aDriverRoot[ idr ].hti) == NULL)
|
|
continue;
|
|
|
|
for (hti = TreeView_GetChild (hAdvDlgTree, hti);
|
|
hti != NULL;
|
|
hti = TreeView_GetNextSibling (hAdvDlgTree, hti))
|
|
{
|
|
if (pid == FindIDriverByTreeItem (hti))
|
|
{
|
|
return hti;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (HTREEITEM)0;
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
|
|
|
|
PIDRIVER FindIDriverByTreeItem (HTREEITEM hti)
|
|
{
|
|
TV_ITEM tvi;
|
|
|
|
tvi.mask = TVIF_PARAM;
|
|
tvi.hItem = hti;
|
|
TreeView_GetItem (hAdvDlgTree, &tvi);
|
|
|
|
if ( (tvi.lParam < 0) ||
|
|
(tvi.lParam >= cInstalledDrivers) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return aInstalledDrivers[ tvi.lParam ].pIDriver;
|
|
}
|
|
|
|
|
|
/*
|
|
*** FindIDriverByName - Returns the first found IDRIVER structure with a name
|
|
*
|
|
*/
|
|
|
|
PIDRIVER FindIDriverByName (LPTSTR szFile)
|
|
{
|
|
mysize_t ii;
|
|
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver == NULL)
|
|
continue;
|
|
if (aInstalledDrivers[ ii ].pIDriver->szAlias[0] == TEXT('\0'))
|
|
continue;
|
|
|
|
if (!FileNameCmp (aInstalledDrivers[ ii ].pIDriver->szFile, szFile))
|
|
return aInstalledDrivers[ ii ].pIDriver;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PIDRIVER FindIDriverByResource (PIRESOURCE pir)
|
|
{
|
|
return FindIDriverByName (FileName( pir->szFile ));
|
|
}
|
|
|
|
|
|
/*
|
|
*** GetSelectedIDriver - Returns the IDRIVER structure the user has selected
|
|
*
|
|
*/
|
|
|
|
PIDRIVER GetSelectedIDriver (HWND hTree)
|
|
{
|
|
HTREEITEM htiCur = TreeView_GetSelection (hTree);
|
|
|
|
if (htiCur == NULL)
|
|
return NULL;
|
|
|
|
return FindIDriverByTreeItem (htiCur);
|
|
}
|
|
|
|
|
|
/*
|
|
*** DriverClassToRootIndex - obtain idr for which {aDriverRoot[idr].dc == dc}
|
|
*
|
|
* The array index for aDriverRoot[] is NOT a DriverClass--that is,
|
|
* aDriverRoot[ PickAnyDC ].dc is not necessarily equal to PickAnyDC.
|
|
* Given a DC, this routine finds the index into aDriverRoot which references
|
|
* that DC.
|
|
*
|
|
*/
|
|
|
|
short DriverClassToRootIndex (DriverClass dc)
|
|
{
|
|
short idr;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if (aDriverRoot[ idr ].dc == dc)
|
|
return idr;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
*** GetDriverClass - guess a DriverClass based on an IDRIVER structure
|
|
*
|
|
* The registry has several different aliases which it uses in the LHS
|
|
* of HKLM\software\microsoft\windowsnt\drivers,drivers32,etc to indicate
|
|
* the classification of a particular driver. These include:
|
|
*
|
|
* AUX, MIDI, MIDIMAPPER, MIXER, MSACM.*, VIDC.* WAVE, WAVEMAPPER
|
|
*
|
|
* as well as others--the full array of known entries is tracked within
|
|
* aDriverKeywords[]. In addition, any of these may be followed by a
|
|
* string of digits by which they are distinguished. This routine parses
|
|
* these keywords and returns a corresponding DriverClass enum.
|
|
*
|
|
*/
|
|
|
|
DriverClass GuessDriverClass (PIDRIVER pid)
|
|
{
|
|
#ifdef FIX_BUG_15451
|
|
return GuessDriverClassFromAlias (pid->szAlias);
|
|
}
|
|
|
|
|
|
|
|
DriverClass GuessDriverClassFromAlias (LPTSTR pszAlias)
|
|
{
|
|
#endif // FIX_BUG_15451
|
|
TCHAR szAlias[ cchRESOURCE ];
|
|
TCHAR *pch;
|
|
short ii;
|
|
|
|
#ifdef FIX_BUG_15451
|
|
lstrcpy (szAlias, pszAlias); // Make a local copy so we can munge it
|
|
#else // FIX_BUG_15451
|
|
lstrcpy (szAlias, pid->szAlias); // Make a local copy so we can munge it
|
|
#endif // FIX_BUG_15451
|
|
|
|
if ((pch = lstrchr (szAlias, TEXT('.'))) != NULL)
|
|
*pch = TEXT('0');
|
|
|
|
for (ii = 0; ii < nDriverKEYWORDS; ii++)
|
|
{
|
|
if (!lstrnicmp (szAlias,
|
|
aDriverKeyword[ii].psz,
|
|
lstrlen (aDriverKeyword[ii].psz)))
|
|
{
|
|
return aDriverKeyword[ii].dc;
|
|
}
|
|
}
|
|
|
|
return dcOTHER;
|
|
}
|
|
|
|
|
|
DriverClass GuessDriverClassFromTreeItem (HTREEITEM hti)
|
|
{
|
|
short idr;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if (hti == aDriverRoot[idr].hti)
|
|
return aDriverRoot[idr].dc;
|
|
}
|
|
|
|
return (g_dcFilterClass);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*** EnsureRootIndexExists - makes sure a given parent exists in hAdvDlgTree
|
|
*
|
|
*/
|
|
|
|
BOOL EnsureRootIndexExists (HWND hTree, short idr)
|
|
{
|
|
TV_INSERTSTRUCT ti;
|
|
TCHAR szDesc[ cchRESOURCE ];
|
|
HWND hwndParent = NULL;
|
|
HWND hwndName = NULL;
|
|
|
|
// If we already HAVE a root in the tree, we're done.
|
|
//
|
|
if (aDriverRoot[ idr ].hti != NULL)
|
|
return TRUE;
|
|
|
|
if (g_dcFilterClass != dcINVALID)
|
|
{
|
|
if (g_dcFilterClass == dcLEGACY)
|
|
{
|
|
if ((aDriverRoot[ idr ].dc != dcWAVE) &&
|
|
(aDriverRoot[ idr ].dc != dcMIDI) &&
|
|
(aDriverRoot[ idr ].dc != dcMIXER) &&
|
|
(aDriverRoot[ idr ].dc != dcAUX))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (aDriverRoot[ idr ].dc != g_dcFilterClass)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
aDriverRoot[idr].hti = TVI_ROOT;
|
|
|
|
LoadString (myInstance, aDriverRoot[idr].idDesc, szDesc, cchRESOURCE);
|
|
|
|
if ((g_dcFilterClass == dcINVALID) || (g_dcFilterClass == dcLEGACY))
|
|
{
|
|
ti.hInsertAfter = TVI_LAST;
|
|
ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
ti.item.iImage = idr;
|
|
ti.item.iSelectedImage = idr;
|
|
ti.item.pszText = szDesc;
|
|
ti.item.lParam = NOPIDRIVER;
|
|
|
|
if (aDriverRoot[idr].dc == dcINVALID)
|
|
ti.hParent = TVI_ROOT;
|
|
else
|
|
ti.hParent = AdvDlgFindTopLevel ();
|
|
|
|
if ((aDriverRoot[idr].hti = TreeView_InsertItem (hTree, &ti)) == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_dcFilterClass != dcINVALID)
|
|
{
|
|
hwndParent = GetParent(hTree);
|
|
if (hwndParent)
|
|
{
|
|
hwndName = GetDlgItem(hwndParent,IDC_DEVICECLASS);
|
|
if (hwndName)
|
|
{
|
|
if (g_dcFilterClass == dcLEGACY)
|
|
{
|
|
LoadString (myInstance, IDS_WAVE_HEADER, szDesc, cchRESOURCE);
|
|
}
|
|
|
|
SetWindowText(hwndName,szDesc);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
**** AdvDlgFindTopLevel - Finds the HTREEITEM associated with the tree root
|
|
*
|
|
* If there's a "Multimedia Devices" tree item under which the other roots
|
|
* are collected, this will return that item. Otherwise, it returns TVI_ROOT.
|
|
*
|
|
*/
|
|
|
|
HTREEITEM AdvDlgFindTopLevel (void)
|
|
{
|
|
short idr;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if (aDriverRoot[idr].dc == dcINVALID)
|
|
return aDriverRoot[idr].hti;
|
|
}
|
|
|
|
return TVI_ROOT;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
**** InitAdvDlgTree - Prepares the AdvDlg's treeview to display devices
|
|
*
|
|
*/
|
|
|
|
BOOL InitAdvDlgTree (HWND hTree)
|
|
{
|
|
int cxIcon, cyIcon;
|
|
short idr;
|
|
UINT uFlags;
|
|
DWORD dwLayout;
|
|
|
|
#ifdef UNICODE
|
|
TreeView_SetUnicodeFormat(hTree,TRUE);
|
|
#endif
|
|
|
|
// Make sure we start with a clean slate
|
|
//
|
|
hAdvDlgTree = hTree;
|
|
SendMessage (hTree, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
aDriverRoot[ idr ].hti = NULL;
|
|
aDriverRoot[ idr ].dwBit = ((DWORD)1) << idr;
|
|
}
|
|
|
|
// Create an imagelist for the icons in the treeview
|
|
//
|
|
cxIcon = (int)GetSystemMetrics (SM_CXSMICON);
|
|
cyIcon = (int)GetSystemMetrics (SM_CYSMICON);
|
|
uFlags = ILC_MASK | ILC_COLOR32;
|
|
|
|
if (GetProcessDefaultLayout(&dwLayout) &&
|
|
(dwLayout & LAYOUT_RTL))
|
|
{
|
|
uFlags |= ILC_MIRROR;
|
|
}
|
|
|
|
if ((hImageList = ImageList_Create (cxIcon, cyIcon,
|
|
uFlags, nDriverROOTS, 1)) == NULL)
|
|
return FALSE;
|
|
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
HICON hi = LoadImage (myInstance,
|
|
MAKEINTRESOURCE( aDriverRoot[idr].idIcon ),
|
|
IMAGE_ICON, cxIcon, cyIcon, LR_DEFAULTCOLOR);
|
|
ImageList_AddIcon (hImageList, hi);
|
|
}
|
|
|
|
TreeView_SetImageList (hTree, hImageList, TVSIL_NORMAL);
|
|
|
|
|
|
if (g_dcFilterClass == dcINVALID)
|
|
{
|
|
// Create the root nodes that are supposed to exist
|
|
// even without children (note that not all are)
|
|
//
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if (aDriverRoot[ idr ].dc == dcINVALID)
|
|
{
|
|
if (!EnsureRootIndexExists (hTree, idr))
|
|
return FALSE;
|
|
}
|
|
}
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
if (aDriverRoot[ idr ].dc != dcINVALID && aDriverRoot[ idr ].fAlwaysMake)
|
|
{
|
|
if (!EnsureRootIndexExists (hTree, idr))
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
// Expand the tree somewhat, so the user doesn't get
|
|
// greeted by a blank page
|
|
//
|
|
TreeView_Expand (hTree, AdvDlgFindTopLevel(), TVE_EXPAND);
|
|
|
|
SendMessage (hTree, WM_SETREDRAW, TRUE, 0L);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
**** FreeAdvDlgTree - Removes and frees all items within the AdvDlg treeview
|
|
*
|
|
*/
|
|
|
|
void FreeAdvDlgTree (HWND hTree)
|
|
{
|
|
short idr;
|
|
|
|
// Delete all leaf nodes
|
|
//
|
|
for (idr = 0; idr < nDriverROOTS; idr++)
|
|
{
|
|
HTREEITEM hti;
|
|
|
|
if (aDriverRoot[idr].dc == dcINVALID)
|
|
continue;
|
|
if (aDriverRoot[idr].hti == NULL)
|
|
continue;
|
|
|
|
while ((hti = TreeView_GetChild (hTree, aDriverRoot[idr].hti)) != NULL)
|
|
{
|
|
if (aDriverRoot[ idr ].dc == dcMIDI)
|
|
{
|
|
HTREEITEM htiInstrument;
|
|
|
|
while ((htiInstrument = TreeView_GetChild (hTree, hti)) != NULL)
|
|
{
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_PARAM;
|
|
tvi.hItem = htiInstrument;
|
|
tvi.lParam = 0;
|
|
|
|
TreeView_GetItem(hTree, &tvi);
|
|
|
|
if (tvi.lParam != 0)
|
|
LocalFree ((HANDLE)tvi.lParam);
|
|
|
|
TreeView_DeleteItem (hTree, htiInstrument);
|
|
}
|
|
}
|
|
|
|
TreeView_DeleteItem (hTree, hti);
|
|
}
|
|
}
|
|
|
|
// Delete everything else
|
|
//
|
|
TreeView_DeleteAllItems (hTree);
|
|
|
|
// Delete the tree's image list
|
|
//
|
|
if (hImageList)
|
|
{
|
|
TreeView_SetImageList (hTree, NULL, TVSIL_NORMAL);
|
|
ImageList_Destroy (hImageList);
|
|
hImageList = NULL;
|
|
}
|
|
|
|
// Delete the InstalledDrivers array
|
|
//
|
|
if (aInstalledDrivers != NULL)
|
|
{
|
|
mysize_t ii;
|
|
for (ii = 0; ii < cInstalledDrivers; ++ii)
|
|
{
|
|
if (aInstalledDrivers[ ii ].pIDriver != NULL)
|
|
{
|
|
LocalFree ((HANDLE)aInstalledDrivers[ ii ].pIDriver);
|
|
aInstalledDrivers[ ii ].pIDriver = NULL;
|
|
}
|
|
}
|
|
|
|
GlobalFree ((HGLOBAL)aInstalledDrivers);
|
|
aInstalledDrivers = NULL;
|
|
cInstalledDrivers = 0;
|
|
}
|
|
}
|
|
|
|
|
|
int lstrnicmp (LPTSTR pszA, LPTSTR pszB, size_t cch)
|
|
{
|
|
#ifdef UNICODE
|
|
size_t cchA, cchB;
|
|
TCHAR *pch;
|
|
|
|
for (cchA = 1, pch = pszA; cchA < cch; cchA++, pch++)
|
|
{
|
|
if (*pch == TEXT('\0'))
|
|
break;
|
|
}
|
|
for (cchB = 1, pch = pszB; cchB < cch; cchB++, pch++)
|
|
{
|
|
if (*pch == TEXT('\0'))
|
|
break;
|
|
}
|
|
|
|
return (CompareStringW (GetThreadLocale(), NORM_IGNORECASE,
|
|
pszA, cchA, pszB, cchB)
|
|
)-2; // CompareStringW returns {1,2,3} instead of {-1,0,1}.
|
|
#else
|
|
return _strnicmp (pszA, pszB, cch);
|
|
#endif
|
|
}
|
|
|
|
|
|
LPTSTR lstrchr (LPTSTR pszTarget, TCHAR ch)
|
|
{
|
|
size_t ich;
|
|
if (pszTarget == NULL)
|
|
return NULL;
|
|
for (ich = 0; pszTarget[ich] != TEXT('\0'); ich++)
|
|
{
|
|
if (pszTarget[ich] == ch)
|
|
return &pszTarget[ ich ];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void lsplitpath (LPTSTR pszSource,
|
|
LPTSTR pszDrive, LPTSTR pszPath, LPTSTR pszName, LPTSTR pszExt)
|
|
{
|
|
LPTSTR pszLastSlash = NULL;
|
|
LPTSTR pszLastDot = NULL;
|
|
LPTSTR pch;
|
|
size_t cchCopy;
|
|
|
|
/*
|
|
* NOTE: This routine was snitched out of USERPRI.LIB 'cause the
|
|
* one in there doesn't split the extension off the name properly.
|
|
*
|
|
* We assume that the path argument has the following form, where any
|
|
* or all of the components may be missing.
|
|
*
|
|
* <drive><dir><fname><ext>
|
|
*
|
|
* and each of the components has the following expected form(s)
|
|
*
|
|
* drive:
|
|
* 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
|
|
* ':'
|
|
* dir:
|
|
* 0 to _MAX_DIR-1 characters in the form of an absolute path
|
|
* (leading '/' or '\') or relative path, the last of which, if
|
|
* any, must be a '/' or '\'. E.g -
|
|
* absolute path:
|
|
* \top\next\last\ ; or
|
|
* /top/next/last/
|
|
* relative path:
|
|
* top\next\last\ ; or
|
|
* top/next/last/
|
|
* Mixed use of '/' and '\' within a path is also tolerated
|
|
* fname:
|
|
* 0 to _MAX_FNAME-1 characters not including the '.' character
|
|
* ext:
|
|
* 0 to _MAX_EXT-1 characters where, if any, the first must be a
|
|
* '.'
|
|
*
|
|
*/
|
|
|
|
// extract drive letter and :, if any
|
|
//
|
|
if (*(pszSource + _MAX_DRIVE - 2) == TEXT(':'))
|
|
{
|
|
if (pszDrive)
|
|
{
|
|
lstrncpy (pszDrive, pszSource, _MAX_DRIVE-1);
|
|
pszDrive[ _MAX_DRIVE-1 ] = TEXT('\0');
|
|
}
|
|
pszSource += _MAX_DRIVE-1;
|
|
}
|
|
else if (pszDrive)
|
|
{
|
|
*pszDrive = TEXT('\0');
|
|
}
|
|
|
|
// extract path string, if any. pszSource now points to the first
|
|
// character of the path, if any, or the filename or extension, if
|
|
// no path was specified. Scan ahead for the last occurence, if
|
|
// any, of a '/' or '\' path separator character. If none is found,
|
|
// there is no path. We will also note the last '.' character found,
|
|
// if any, to aid in handling the extension.
|
|
//
|
|
for (pch = pszSource; *pch != TEXT('\0'); pch++)
|
|
{
|
|
if (*pch == TEXT('/') || *pch == TEXT('\\'))
|
|
pszLastSlash = pch;
|
|
else if (*pch == TEXT('.'))
|
|
pszLastDot = pch;
|
|
}
|
|
|
|
// if we found a '\\' or '/', fill in pszPath
|
|
//
|
|
if (pszLastSlash)
|
|
{
|
|
if (pszPath)
|
|
{
|
|
cchCopy = (size_t)min((UINT)_MAX_DIR-1, (pszLastSlash-pszSource) + 1);
|
|
lstrncpy (pszPath, pszSource, cchCopy);
|
|
pszPath[ cchCopy ] = 0;
|
|
}
|
|
pszSource = pszLastSlash +1;
|
|
}
|
|
else if (pszPath)
|
|
{
|
|
*pszPath = TEXT('\0');
|
|
}
|
|
|
|
// extract file name and extension, if any. Path now points to
|
|
// the first character of the file name, if any, or the extension
|
|
// if no file name was given. Dot points to the '.' beginning the
|
|
// extension, if any.
|
|
//
|
|
|
|
if (pszLastDot && (pszLastDot >= pszSource))
|
|
{
|
|
// found the marker for an extension -
|
|
// copy the file name up to the '.'.
|
|
//
|
|
if (pszName)
|
|
{
|
|
cchCopy = (size_t)min( (UINT)_MAX_DIR-1, (pszLastDot-pszSource) );
|
|
lstrncpy (pszName, pszSource, cchCopy);
|
|
pszName[ cchCopy ] = 0;
|
|
}
|
|
|
|
// now we can get the extension
|
|
//
|
|
if (pszExt)
|
|
{
|
|
lstrncpy (pszExt, pszLastDot, _MAX_EXT -1);
|
|
pszExt[ _MAX_EXT-1 ] = TEXT('\0');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// found no extension, give empty extension and copy rest of
|
|
// string into fname.
|
|
//
|
|
if (pszName)
|
|
{
|
|
lstrncpy (pszName, pszSource, _MAX_FNAME -1);
|
|
pszName[ _MAX_FNAME -1 ] = TEXT('\0');
|
|
}
|
|
|
|
if (pszExt)
|
|
{
|
|
*pszExt = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void lstrncpy (LPTSTR pszTarget, LPTSTR pszSource, size_t cch)
|
|
{
|
|
size_t ich;
|
|
for (ich = 0; ich < cch; ich++)
|
|
{
|
|
if ((pszTarget[ich] = pszSource[ich]) == TEXT('\0'))
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* DEVICE PROPERTY SHEETS _____________________________________________________
|
|
*
|
|
*/
|
|
|
|
// General flag macros
|
|
//
|
|
#define SetFlag(obj, f) do {obj |= (f);} while (0)
|
|
#define ToggleFlag(obj, f) do {obj ^= (f);} while (0)
|
|
#define ClearFlag(obj, f) do {obj &= ~(f);} while (0)
|
|
#define IsFlagSet(obj, f) (BOOL)(((obj) & (f)) == (f))
|
|
#define IsFlagClear(obj, f) (BOOL)(((obj) & (f)) != (f))
|
|
|
|
BOOL InstrumentToResource (PIRESOURCE, HTREEITEM);
|
|
BOOL DriverToResource (HWND,PIRESOURCE,PIDRIVER,DriverClass);
|
|
DriverClass OldClassIDToDriverClass (int);
|
|
void FreeClassNode (PCLASSNODE);
|
|
PIDRIVER FindIDriverByResource (PIRESOURCE);
|
|
void EnableDriverService (PIRESOURCE, BOOL);
|
|
|
|
BOOL PASCAL DoDevPropCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
|
|
STATIC void SetDevStatus(int iStatus, HWND hDlg);
|
|
INT_PTR CALLBACK ACMDlg(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
|
|
|
|
|
/*
|
|
*** DriverToResource - create a PIRESOURCE structure from a PIDRIVER structure
|
|
*
|
|
* The Win95 code uses PIRESOURCEs (among other structures) to keep track of
|
|
* the item in the Advanced tab's treeview; the old WinNT code used PIDRIVER
|
|
* structures to keep track of the items in its listbox. The tree now uses
|
|
* PIDRIVER structures, but we create PIRESOURCE structures out of 'em before
|
|
* passing control off to the Win95 properties dialogs, which came through
|
|
* largely unmodified.
|
|
*
|
|
* BTW, I retained the PIDRIVER structures as the main treeview structure
|
|
* because it prevented difficulty in porting over the Install/Remove Driver
|
|
* code from the old NT code.
|
|
*
|
|
*/
|
|
|
|
BOOL DriverToResource (HWND hPar, PIRESOURCE pir, PIDRIVER pid, DriverClass dc)
|
|
{
|
|
if (dc == dcINVALID)
|
|
{
|
|
if ((dc = GuessDriverClass (pid)) == dcINVALID)
|
|
return FALSE;
|
|
}
|
|
|
|
if ((pir->pcn = (PCLASSNODE)LocalAlloc (LPTR, sizeof(CLASSNODE))) == NULL)
|
|
return FALSE;
|
|
|
|
if (!DriverClassToClassNode (pir->pcn, dc))
|
|
{
|
|
LocalFree ((HANDLE)pir->pcn);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dc == dcACODEC)
|
|
pir->iNode = 3; // 1=class, 2=device, 3=acm, 4=instmt
|
|
// else if (dc == dcINSTRUMENT)
|
|
// pir->iNode = 4; // 1=class, 2=device, 3=acm, 4=instmt
|
|
else
|
|
pir->iNode = 2; // 1=class, 2=device, 3=acm, 4=instmt
|
|
|
|
lstrcpy (pir->szFriendlyName, pid->szDesc);
|
|
lstrcpy (pir->szDesc, pid->szDesc);
|
|
lstrcpy (pir->szFile, pid->szFile);
|
|
lstrcpy (pir->szDrvEntry, pid->szAlias);
|
|
lstrcpy (pir->szClass, pir->pcn->szClass);
|
|
|
|
pid->fQueryable = IsConfigurable (pid, hPar);
|
|
pir->fQueryable = (short)pid->fQueryable;
|
|
pir->iClassID = (short)DriverClassToOldClassID (dc);
|
|
pir->szParam[0] = 0;
|
|
pir->dnDevNode = 0;
|
|
pir->hDriver = NULL;
|
|
|
|
// Find fStatus, which despite its name is really a series of
|
|
// flags--in Win95 it's composed of DEV_* flags (from the old
|
|
// mmcpl.h), but those are tied with PNP. Here, we use the
|
|
// dwStatus* flags:
|
|
//
|
|
pir->fStatus = (int)GetDriverStatus (pid);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL InstrumentToResource (PIRESOURCE pir, HTREEITEM hti)
|
|
{
|
|
TV_ITEM tvi;
|
|
PINSTRUM pin;
|
|
|
|
tvi.mask = TVIF_PARAM;
|
|
tvi.hItem = hti;
|
|
tvi.lParam = 0;
|
|
TreeView_GetItem(hAdvDlgTree, &tvi);
|
|
|
|
if ((pin = (PINSTRUM)tvi.lParam) == NULL)
|
|
return FALSE;
|
|
|
|
|
|
if ((pir->pcn = (PCLASSNODE)LocalAlloc (LPTR, sizeof(CLASSNODE))) == NULL)
|
|
return FALSE;
|
|
|
|
if (!DriverClassToClassNode (pir->pcn, dcMIDI))
|
|
{
|
|
LocalFree ((HANDLE)pir->pcn);
|
|
return FALSE;
|
|
}
|
|
|
|
pir->iNode = 4; // 1=class, 2=device, 3=acm, 4=instmt
|
|
|
|
lstrcpy (pir->szFriendlyName, pin->szFriendly);
|
|
lstrcpy (pir->szDesc, pin->szKey);
|
|
// lstrcpy (pir->szFile, TEXT("unused"));
|
|
// lstrcpy (pir->szDrvEntry, TEXT("unused"));
|
|
lstrcpy (pir->szClass, pir->pcn->szClass);
|
|
|
|
pir->fQueryable = FALSE;
|
|
pir->iClassID = MIDI_ID;
|
|
pir->szParam[0] = 0;
|
|
pir->dnDevNode = 0;
|
|
pir->hDriver = NULL;
|
|
pir->fStatus = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL DriverClassToClassNode (PCLASSNODE pcn, DriverClass dc)
|
|
{
|
|
short idr;
|
|
short ii;
|
|
int cxIcon, cyIcon;
|
|
|
|
if ((idr = DriverClassToRootIndex (dc)) == -1)
|
|
return FALSE;
|
|
|
|
pcn->iNode = 1; // 1=class, 2=device, 3=acm, 4=instmt
|
|
|
|
GetString (pcn->szClassName, aDriverRoot[idr].idDesc);
|
|
pcn->szClass[0] = TEXT('\0');
|
|
|
|
for (ii = 0; ii < nKeywordDESCS; ii++)
|
|
{
|
|
if (aKeywordDesc[ii].dc == dc)
|
|
{
|
|
lstrcpy (pcn->szClass, aKeywordDesc[ii].psz);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cxIcon = (int)GetSystemMetrics (SM_CXICON);
|
|
cyIcon = (int)GetSystemMetrics (SM_CYICON);
|
|
|
|
pcn->hIcon = LoadImage (myInstance,
|
|
MAKEINTRESOURCE( aDriverRoot[ idr ].idIcon ),
|
|
IMAGE_ICON, cxIcon, cyIcon, LR_DEFAULTCOLOR);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int DriverClassToOldClassID (DriverClass dc)
|
|
{
|
|
switch (dc)
|
|
{
|
|
case dcWAVE: return WAVE_ID; break;
|
|
case dcMIDI: return MIDI_ID; break;
|
|
case dcMIXER: return MIXER_ID; break;
|
|
case dcAUX: return AUX_ID; break;
|
|
case dcMCI: return MCI_ID; break;
|
|
case dcACODEC: return ACM_ID; break;
|
|
case dcVCODEC: return ICM_ID; break;
|
|
case dcVIDCAP: return VIDCAP_ID; break;
|
|
case dcJOY: return JOYSTICK_ID; break;
|
|
default: return JOYSTICK_ID; break;
|
|
}
|
|
}
|
|
|
|
|
|
DriverClass OldClassIDToDriverClass (int ii)
|
|
{
|
|
switch (ii)
|
|
{
|
|
case WAVE_ID: return dcWAVE; break;
|
|
case MIDI_ID: return dcMIDI; break;
|
|
case MIXER_ID: return dcMIXER; break;
|
|
case AUX_ID: return dcAUX; break;
|
|
case MCI_ID: return dcMCI; break;
|
|
case ACM_ID: return dcACODEC; break;
|
|
case ICM_ID: return dcVCODEC; break;
|
|
case VIDCAP_ID: return dcVIDCAP; break;
|
|
case JOYSTICK_ID: return dcJOY; break;
|
|
default: return dcOTHER;
|
|
}
|
|
}
|
|
|
|
|
|
void FreeIResource (PIRESOURCE pir)
|
|
{
|
|
if (pir->pcn != NULL)
|
|
{
|
|
FreeClassNode (pir->pcn);
|
|
LocalFree ((HANDLE)pir->pcn);
|
|
pir->pcn = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void FreeClassNode (PCLASSNODE pcn)
|
|
{
|
|
if (pcn->hIcon != NULL)
|
|
{
|
|
DestroyIcon (pcn->hIcon);
|
|
pcn->hIcon = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD GetDriverStatus (PIDRIVER pid)
|
|
{
|
|
DWORD dwStatus;
|
|
SC_HANDLE scManager;
|
|
SC_HANDLE scDriver;
|
|
TCHAR szName[ cchRESOURCE ];
|
|
|
|
dwStatus = 0;
|
|
|
|
lsplitpath (pid->szFile, NULL, NULL, szName, NULL);
|
|
|
|
// First step: determine if the driver has a service
|
|
//
|
|
if ((scManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
|
|
{
|
|
if ((scDriver = OpenService (scManager, szName, GENERIC_READ)) != NULL)
|
|
{
|
|
QUERY_SERVICE_CONFIG qsc;
|
|
SERVICE_STATUS ss;
|
|
DWORD cbReq;
|
|
void *pqsc;
|
|
|
|
SetFlag (dwStatus, dwStatusHASSERVICE);
|
|
|
|
// Great! It has a service. Find out if the service
|
|
// is actively running, and whether it is disabled.
|
|
//
|
|
if (QueryServiceConfig (scDriver, &qsc, sizeof(qsc), &cbReq))
|
|
{
|
|
if (qsc.dwStartType != SERVICE_DISABLED)
|
|
{
|
|
SetFlag (dwStatus, dwStatusSvcENABLED);
|
|
}
|
|
}
|
|
else if ((pqsc = (void *)LocalAlloc (LPTR, cbReq)) != NULL)
|
|
{
|
|
if (QueryServiceConfig (scDriver,
|
|
(QUERY_SERVICE_CONFIG *)pqsc,
|
|
cbReq, &cbReq))
|
|
{
|
|
if ( ((QUERY_SERVICE_CONFIG *)pqsc)->dwStartType
|
|
!= SERVICE_DISABLED)
|
|
{
|
|
SetFlag (dwStatus, dwStatusSvcENABLED);
|
|
}
|
|
}
|
|
|
|
LocalFree ((HANDLE)pqsc);
|
|
}
|
|
|
|
if (QueryServiceStatus (scDriver, &ss))
|
|
{
|
|
if ((ss.dwCurrentState != SERVICE_STOPPED) &&
|
|
(ss.dwCurrentState != SERVICE_STOP_PENDING))
|
|
{
|
|
SetFlag (dwStatus, dwStatusSvcSTARTED);
|
|
}
|
|
}
|
|
|
|
CloseServiceHandle (scDriver);
|
|
}
|
|
|
|
CloseServiceHandle (scManager);
|
|
}
|
|
|
|
// If no service, see if we can talk to the driver itself
|
|
//
|
|
if (!IsFlagSet (dwStatus, dwStatusHASSERVICE))
|
|
{
|
|
HANDLE hDriver;
|
|
|
|
if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) != NULL)
|
|
{
|
|
SetFlag (dwStatus, dwStatusDRIVEROK);
|
|
|
|
CloseDriver (hDriver, 0L, 0L);
|
|
}
|
|
}
|
|
|
|
// If it's a wave device, can we map through it?
|
|
//
|
|
if (GetMappable (pid))
|
|
{
|
|
SetFlag (dwStatus, dwStatusMAPPABLE);
|
|
}
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
void GetTreeItemNodeDesc (LPTSTR pszTarget, PIRESOURCE pir)
|
|
{
|
|
lstrcpy (pszTarget, pir->szFriendlyName);
|
|
}
|
|
|
|
|
|
void GetTreeItemNodeID (LPTSTR pszTarget, PIRESOURCE pir)
|
|
{
|
|
DriverClass dc;
|
|
CLASSNODE cn;
|
|
|
|
*pszTarget = 0; // In case we fail later
|
|
|
|
dc = OldClassIDToDriverClass (pir->iClassID);
|
|
if (!DriverClassToClassNode (&cn, dc))
|
|
return;
|
|
|
|
switch (pir->iNode) // 1=class, 2=device, 3=acm, 4=instmt
|
|
{
|
|
case 1: // class
|
|
lstrcpy (pszTarget, cn.szClass);
|
|
break;
|
|
|
|
case 2: // instrument
|
|
wsprintf (pszTarget, TEXT("%s\\%s"), cn.szClass, pir->szDrvEntry);
|
|
break;
|
|
|
|
case 4: // instrument
|
|
lstrcpy (pszTarget, pir->szDesc);
|
|
break;
|
|
|
|
default:
|
|
lstrcpy (pszTarget, pir->szDesc);
|
|
break;
|
|
}
|
|
|
|
FreeClassNode (&cn);
|
|
}
|
|
|
|
|
|
void ShowDeviceProperties (HWND hPar, HTREEITEM hti)
|
|
{
|
|
IRESOURCE ir;
|
|
CLASSNODE cn;
|
|
DEVTREENODE dtn;
|
|
short idr;
|
|
TCHAR szTitle[ cchRESOURCE ];
|
|
TCHAR szTab[ cchRESOURCE ];
|
|
DriverClass dc;
|
|
PIDRIVER pid;
|
|
|
|
if (hti == NULL)
|
|
return;
|
|
|
|
if (TreeView_GetParent (hAdvDlgTree, hti) &&
|
|
TreeView_GetGrandParent (hAdvDlgTree, hti) &&
|
|
(GuessDriverClassFromTreeItem (
|
|
TreeView_GetGrandParent (hAdvDlgTree, hti)
|
|
) == dcMIDI))
|
|
{
|
|
if (InstrumentToResource (&ir, hti))
|
|
{
|
|
ShowMidiPropSheet (NULL,
|
|
ir.szFriendlyName,
|
|
hPar,
|
|
MIDI_INSTRUMENT_PROP,
|
|
ir.szFriendlyName,
|
|
hti,
|
|
(LPARAM)&ir,
|
|
(LPARAM)hAdvDlgTree);
|
|
|
|
FreeIResource (&ir);
|
|
}
|
|
return;
|
|
}
|
|
else if ((pid = FindIDriverByTreeItem (hti)) != NULL)
|
|
{
|
|
dc = GuessDriverClassFromTreeItem (TreeView_GetParent(hAdvDlgTree,hti));
|
|
if (dc == dcINVALID)
|
|
{
|
|
if ((dc = GuessDriverClass (pid)) == dcINVALID)
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((dc = GuessDriverClassFromTreeItem (hti)) == dcINVALID)
|
|
return;
|
|
}
|
|
|
|
if (g_dcFilterClass != dcINVALID)
|
|
{
|
|
if ((dc == dcOTHER) || (dc == dcINVALID))
|
|
{
|
|
dc = g_dcFilterClass;
|
|
}
|
|
}
|
|
|
|
if ((idr = DriverClassToRootIndex (dc)) == -1)
|
|
return;
|
|
|
|
if (pid == NULL) // Just want class properties?
|
|
{
|
|
if (!DriverClassToClassNode (&cn, dc))
|
|
return;
|
|
|
|
if (dc == dcMIDI) // MIDI class properties?
|
|
{
|
|
GetString (szTitle, aDriverRoot[idr].idDesc);
|
|
|
|
ShowMidiPropSheet (NULL,
|
|
szTitle,
|
|
hPar,
|
|
MIDI_CLASS_PROP,
|
|
szTitle,
|
|
hti,
|
|
(LPARAM)&cn,
|
|
(LPARAM)hAdvDlgTree);
|
|
}
|
|
else // Generic class properties? (nothing to do, really)
|
|
{
|
|
GetString (szTab, IDS_GENERAL);
|
|
GetString (szTitle, aDriverRoot[idr].idDesc);
|
|
|
|
dtn.lParam = (LPARAM)&cn;
|
|
dtn.hwndTree = hAdvDlgTree;
|
|
|
|
ShowPropSheet (szTab,
|
|
DevPropDlg,
|
|
DLG_DEV_PROP,
|
|
hPar,
|
|
szTitle,
|
|
(LPARAM)&dtn);
|
|
}
|
|
|
|
FreeClassNode (&cn);
|
|
}
|
|
else
|
|
{
|
|
switch (dc)
|
|
{
|
|
case dcACODEC:
|
|
GetString (szTab, IDS_GENERAL);
|
|
|
|
ShowPropSheet (szTab,
|
|
ACMDlg,
|
|
DLG_ACMDEV_PROP,
|
|
hPar,
|
|
pid->szDesc,
|
|
pid->lp);
|
|
|
|
// Re-sort the Audio Codec entries, in case their priorities
|
|
// have changed. Then find treeview item for this codec,
|
|
// and select it.
|
|
//
|
|
{
|
|
HTREEITEM hti;
|
|
short idr;
|
|
|
|
SendMessage (hAdvDlgTree, WM_SETREDRAW, FALSE, 0L);
|
|
FillTreeFromMSACM (hAdvDlgTree);
|
|
SendMessage (hAdvDlgTree, WM_SETREDRAW, TRUE, 0L);
|
|
|
|
if ((idr = DriverClassToRootIndex (dcACODEC)) != -1)
|
|
{
|
|
if ((hti = aDriverRoot[ idr ].hti) != NULL)
|
|
{
|
|
for (hti = TreeView_GetChild (hAdvDlgTree, hti);
|
|
hti != NULL;
|
|
hti = TreeView_GetNextSibling (hAdvDlgTree, hti))
|
|
{
|
|
if (pid == FindIDriverByTreeItem (hti))
|
|
{
|
|
TreeView_SelectItem (hAdvDlgTree, hti);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case dcMIDI:
|
|
if (!DriverToResource (hPar, &ir, pid, dc))
|
|
break;
|
|
|
|
GetString (szTab, IDS_GENERAL);
|
|
|
|
dtn.lParam = (LPARAM)&ir;
|
|
dtn.hwndTree = hAdvDlgTree;
|
|
|
|
ShowWithMidiDevPropSheet (szTab,
|
|
DevPropDlg,
|
|
DLG_DEV_PROP,
|
|
hPar,
|
|
pid->szDesc,
|
|
hti,
|
|
(LPARAM)&dtn,
|
|
(LPARAM)&ir,
|
|
(LPARAM)hAdvDlgTree);
|
|
|
|
FreeIResource (&ir);
|
|
break;
|
|
|
|
case dcWAVE:
|
|
if (!DriverToResource (hPar, &ir, pid, dc))
|
|
break;
|
|
|
|
GetString (szTab, IDS_GENERAL);
|
|
|
|
dtn.lParam = (LPARAM)&ir;
|
|
dtn.hwndTree = hAdvDlgTree;
|
|
|
|
ShowPropSheet (szTab,
|
|
DevPropDlg,
|
|
DLG_WAVDEV_PROP,
|
|
hPar,
|
|
pid->szDesc,
|
|
(LPARAM)&dtn);
|
|
|
|
FreeIResource (&ir);
|
|
break;
|
|
|
|
default:
|
|
if (!DriverToResource (hPar, &ir, pid, dc))
|
|
break;
|
|
|
|
GetString (szTab, IDS_GENERAL);
|
|
|
|
dtn.lParam = (LPARAM)&ir;
|
|
dtn.hwndTree = hAdvDlgTree;
|
|
|
|
ShowPropSheet (szTab,
|
|
DevPropDlg,
|
|
DLG_DEV_PROP,
|
|
hPar,
|
|
pid->szDesc,
|
|
(LPARAM)&dtn);
|
|
|
|
FreeIResource (&ir);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#include "medhelp.h"
|
|
|
|
const static DWORD aDevPropHelpIds[] = { // Context Help IDs
|
|
ID_DEV_SETTINGS, IDH_MMCPL_DEVPROP_SETTINGS,
|
|
IDC_DEV_ICON, NO_HELP,
|
|
IDC_DEV_DESC, NO_HELP,
|
|
IDC_DEV_STATUS, NO_HELP,
|
|
IDC_ENABLE, IDH_MMCPL_DEVPROP_ENABLE,
|
|
IDC_DISABLE, IDH_MMCPL_DEVPROP_DISABLE,
|
|
IDC_DONOTMAP, IDH_MMCPL_DEVPROP_DONT_MAP,
|
|
0, 0
|
|
};
|
|
|
|
/*
|
|
***************************************************************
|
|
* DlgProc for device Property sheet.
|
|
***************************************************************
|
|
*/
|
|
INT_PTR CALLBACK DevPropDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
NMHDR FAR *lpnm;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NOTIFY:
|
|
lpnm = (NMHDR FAR *)lParam;
|
|
switch(lpnm->code)
|
|
{
|
|
case PSN_KILLACTIVE:
|
|
FORWARD_WM_COMMAND(hDlg, IDOK, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
FORWARD_WM_COMMAND(hDlg, ID_APPLY, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
FORWARD_WM_COMMAND(hDlg, ID_INIT, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_RESET:
|
|
FORWARD_WM_COMMAND(hDlg, IDCANCEL, 0, 0, SendMessage);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
PIRESOURCE pIResource;
|
|
PCLASSNODE pcn;
|
|
PDEVTREENODE pdtn = (PDEVTREENODE)(((LPPROPSHEETPAGE)lParam)->lParam);
|
|
|
|
SetWindowLongPtr(hDlg, DWLP_USER, ((LPPROPSHEETPAGE)lParam)->lParam);
|
|
|
|
if (*((short *)(pdtn->lParam)) == 1)
|
|
{
|
|
HWND hwndTree = pdtn->hwndTree;
|
|
HTREEITEM htiCur = TreeView_GetSelection(hwndTree);
|
|
TCHAR sz[cchRESOURCE];
|
|
TV_ITEM tvi;
|
|
|
|
tvi.mask = TVIF_CHILDREN;
|
|
tvi.hItem = htiCur;
|
|
TreeView_GetItem(hwndTree, &tvi);
|
|
|
|
pcn = (PCLASSNODE)(pdtn->lParam);
|
|
//set class icon.
|
|
SendDlgItemMessage(hDlg, IDC_DEV_ICON, STM_SETICON, (WPARAM)pcn->hIcon , 0L);
|
|
SetWindowText(GetDlgItem(hDlg, IDC_DEV_DESC), pcn->szClassName);
|
|
DestroyWindow(GetDlgItem(hDlg, IDC_ENABLE));
|
|
DestroyWindow(GetDlgItem(hDlg, IDC_DISABLE));
|
|
DestroyWindow(GetDlgItem(hDlg, ID_DEV_SETTINGS));
|
|
|
|
GetString (sz, (tvi.cChildren) ? IDS_NOPROP : IDS_NODEVS);
|
|
SetWindowText(GetDlgItem(hDlg, IDC_DEV_STATUS), sz);
|
|
}
|
|
else
|
|
{
|
|
pIResource = (PIRESOURCE)(pdtn->lParam);
|
|
SendDlgItemMessage(hDlg, IDC_DEV_ICON, STM_SETICON, (WPARAM)pIResource->pcn->hIcon , 0L);
|
|
SetWindowText(GetDlgItem(hDlg, IDC_DEV_DESC), pIResource->szDesc);
|
|
if (!IsFlagSet(pIResource->fStatus, dwStatusHASSERVICE) &&
|
|
!IsFlagSet(pIResource->fStatus, dwStatusDRIVEROK))
|
|
{
|
|
SetDevStatus(pIResource->fStatus, hDlg);
|
|
}
|
|
else
|
|
{
|
|
if (pIResource->iClassID == WAVE_ID)
|
|
{
|
|
CheckDlgButton (hDlg,
|
|
IDC_DONOTMAP,
|
|
IsFlagClear(pIResource->fStatus,
|
|
dwStatusMAPPABLE));
|
|
}
|
|
|
|
if (!pIResource->fQueryable || pIResource->fQueryable == -1)
|
|
EnableWindow(GetDlgItem(hDlg, ID_DEV_SETTINGS), FALSE);
|
|
if (!IsFlagSet (pIResource->fStatus, dwStatusHASSERVICE))
|
|
{
|
|
DestroyWindow(GetDlgItem(hDlg, IDC_ENABLE));
|
|
DestroyWindow(GetDlgItem(hDlg, IDC_DISABLE));
|
|
}
|
|
else
|
|
{
|
|
TCHAR szStatusStr[MAXSTR];
|
|
DriverClass dc;
|
|
short idr;
|
|
|
|
dc = OldClassIDToDriverClass (pIResource->iClassID);
|
|
idr = DriverClassToRootIndex (dc);
|
|
|
|
if (idr == -1)
|
|
{
|
|
DestroyWindow (GetDlgItem(hDlg, IDC_ENABLE));
|
|
DestroyWindow (GetDlgItem(hDlg, IDC_DISABLE));
|
|
}
|
|
else
|
|
{
|
|
GetString (szStatusStr, aDriverRoot[idr].idEnable);
|
|
SetDlgItemText(hDlg, IDC_ENABLE, szStatusStr);
|
|
GetString (szStatusStr, aDriverRoot[idr].idDisable);
|
|
SetDlgItemText(hDlg, IDC_DISABLE, szStatusStr);
|
|
}
|
|
}
|
|
}
|
|
SetDevStatus(pIResource->fStatus, hDlg);
|
|
}
|
|
|
|
#ifdef FIX_BUG_15451
|
|
if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
|
|
{
|
|
MakeThisDialogLookLikeTheOldDialog (GetParent(hDlg));
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
case WM_DROPFILES:
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp ((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(UINT_PTR) (LPTSTR) aDevPropHelpIds);
|
|
return TRUE;
|
|
|
|
case WM_HELP:
|
|
{
|
|
LPHELPINFO lphi = (LPVOID) lParam;
|
|
WinHelp (lphi->hItemHandle, NULL, HELP_WM_HELP,
|
|
(UINT_PTR) (LPTSTR) aDevPropHelpIds);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
HANDLE_WM_COMMAND(hDlg, wParam, lParam, DoDevPropCommand);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
***************************************************************
|
|
*
|
|
***************************************************************
|
|
*/
|
|
BOOL PASCAL DoDevPropCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
PDEVTREENODE pdtn = (PDEVTREENODE)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
PIRESOURCE pIResource;
|
|
static int fDevStatus;
|
|
|
|
if (!pdtn)
|
|
return FALSE;
|
|
pIResource = (PIRESOURCE)(pdtn->lParam);
|
|
|
|
switch (id)
|
|
{
|
|
|
|
case ID_APPLY:
|
|
if ((pIResource->iNode == 2) && (fDevStatus != pIResource->fStatus))
|
|
{
|
|
if ( (IsFlagSet (fDevStatus, dwStatusMAPPABLE)) !=
|
|
(IsFlagSet (pIResource->fStatus, dwStatusMAPPABLE)) )
|
|
{
|
|
SetMappable (pIResource,
|
|
(BOOL)IsFlagSet (fDevStatus, dwStatusMAPPABLE));
|
|
}
|
|
|
|
if ( (IsFlagSet (fDevStatus, dwStatusHASSERVICE)) &&
|
|
(IsFlagSet (fDevStatus, dwStatusSvcENABLED) !=
|
|
IsFlagSet (pIResource->fStatus, dwStatusSvcENABLED)) )
|
|
{
|
|
#if 0 // TODO: Multiportmidi
|
|
if ( (pIResource->iClassID == MIDI_ID) &&
|
|
(IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI)) )
|
|
{
|
|
EnableMultiPortMIDI (pIResource,
|
|
IsFlagSet (fDevStatus,
|
|
dwStatusSvcENABLED));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
EnableDriverService (pIResource,
|
|
IsFlagSet (fDevStatus,
|
|
dwStatusSvcENABLED));
|
|
}
|
|
}
|
|
DisplayMessage(hDlg, IDS_CHANGESAVED, IDS_RESTART, MB_OK);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDOK:
|
|
return TRUE;
|
|
case IDCANCEL:
|
|
break;
|
|
|
|
case ID_INIT:
|
|
if (pIResource->iNode == 2)
|
|
fDevStatus = pIResource->fStatus;
|
|
#ifdef FIX_BUG_15451
|
|
if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
|
|
{
|
|
FORWARD_WM_COMMAND(hDlg,ID_DEV_SETTINGS,0,0,PostMessage);
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
break;
|
|
|
|
case IDC_DONOTMAP:
|
|
if(Button_GetCheck(GetDlgItem(hDlg, IDC_DONOTMAP)))
|
|
ClearFlag(fDevStatus, dwStatusMAPPABLE);
|
|
else
|
|
SetFlag(fDevStatus, dwStatusMAPPABLE);
|
|
PropSheet_Changed(GetParent(hDlg),hDlg);
|
|
break;
|
|
|
|
case IDC_ENABLE:
|
|
if (IsFlagSet (fDevStatus, dwStatusHASSERVICE))
|
|
{
|
|
SetFlag(fDevStatus, dwStatusSvcENABLED);
|
|
SetDevStatus(fDevStatus, hDlg);
|
|
PropSheet_Changed(GetParent(hDlg),hDlg);
|
|
#if 0 // TODO: Multiportmidi
|
|
if (IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI))
|
|
{
|
|
DisplayMessage(hDlg, IDS_ENABLE, IDS_ENABLEMULTIPORTMIDI, MB_OK);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case IDC_DISABLE:
|
|
if (IsFlagSet (fDevStatus, dwStatusHASSERVICE))
|
|
{
|
|
ClearFlag(fDevStatus, dwStatusSvcENABLED);
|
|
SetDevStatus(fDevStatus, hDlg);
|
|
PropSheet_Changed(GetParent(hDlg),hDlg);
|
|
#if 0 // TODO: Multiportmidi
|
|
if (IsFlagSet(fDevStatus, DEV_MULTIPORTMIDI))
|
|
{
|
|
DisplayMessage(hDlg, IDS_DISABLE, IDS_DISABLEMULTIPORTMIDI, MB_OK);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case ID_DEV_SETTINGS:
|
|
#ifdef FIX_BUG_15451
|
|
if (szDriverWhichNeedsSettings[0] != TEXT('\0'))
|
|
{
|
|
ConfigureDriver (hDlg, szDriverWhichNeedsSettings);
|
|
szDriverWhichNeedsSettings[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
PIDRIVER pid;
|
|
|
|
if ((pid = FindIDriverByResource (pIResource)) == NULL)
|
|
break;
|
|
|
|
ShowDriverSettings (hDlg, pid->szFile);
|
|
}
|
|
#else // FIX_BUG_15451
|
|
{
|
|
PIDRIVER pid;
|
|
HANDLE hDriver;
|
|
|
|
if ((pid = FindIDriverByResource (pIResource)) == NULL)
|
|
break;
|
|
|
|
if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) == 0)
|
|
{
|
|
OpenDriverError(hDlg, pid->szDesc, pid->szFile);
|
|
}
|
|
else
|
|
{
|
|
DRVCONFIGINFO DrvConfigInfo;
|
|
InitDrvConfigInfo(&DrvConfigInfo, pid);
|
|
if ((SendDriverMessage(
|
|
hDriver,
|
|
DRV_CONFIGURE,
|
|
(LONG)hDlg,
|
|
(LONG)(LPDRVCONFIGINFO)&DrvConfigInfo) ==
|
|
DRVCNF_RESTART))
|
|
{
|
|
iRestartMessage= 0;
|
|
DialogBox(myInstance,
|
|
MAKEINTRESOURCE(DLG_RESTART), hDlg, RestartDlg);
|
|
}
|
|
CloseDriver(hDriver, 0L, 0L);
|
|
}
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
***************************************************************
|
|
* Check the status flag for the device and display the appropriate text the
|
|
* the device properties prop sheet.
|
|
***************************************************************
|
|
*/
|
|
STATIC void SetDevStatus(int iStatus, HWND hDlg)
|
|
{
|
|
HWND hwndS = GetDlgItem(hDlg, IDC_DEV_STATUS);
|
|
TCHAR szStatus[cchRESOURCE];
|
|
|
|
if (IsFlagSet (iStatus, dwStatusHASSERVICE))
|
|
{
|
|
if (IsFlagSet (iStatus, dwStatusSvcENABLED))
|
|
{
|
|
if (IsFlagSet (iStatus, dwStatusSvcSTARTED))
|
|
GetString (szStatus, IDS_DEVENABLEDOK);
|
|
else
|
|
GetString (szStatus, IDS_DEVENABLEDNOTOK);
|
|
|
|
CheckRadioButton (hDlg, IDC_ENABLE, IDC_DISABLE, IDC_ENABLE);
|
|
}
|
|
else // service has been disabled
|
|
{
|
|
if (IsFlagSet (iStatus, dwStatusSvcSTARTED))
|
|
GetString (szStatus, IDS_DEVDISABLEDOK);
|
|
else
|
|
GetString (szStatus, IDS_DEVDISABLED);
|
|
|
|
CheckRadioButton(hDlg, IDC_ENABLE, IDC_DISABLE, IDC_DISABLE);
|
|
}
|
|
|
|
SetWindowText(hwndS, szStatus);
|
|
}
|
|
else // driver does not have a service, and thus can't be disabled
|
|
{
|
|
if (IsFlagSet (iStatus, dwStatusDRIVEROK))
|
|
GetString (szStatus, IDS_DEVENABLEDOK);
|
|
else
|
|
GetString (szStatus, IDS_DEVENABLEDNODRIVER);
|
|
|
|
CheckRadioButton(hDlg, IDC_ENABLE, IDC_DISABLE, IDC_ENABLE);
|
|
SetWindowText(hwndS, szStatus);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*** EnableDriverService - enable or disable a service-based driver
|
|
*
|
|
* If Enable, the service will be set to start==system_start.
|
|
* If !Enable, the service will be set to start==disabled.
|
|
*
|
|
*/
|
|
|
|
void EnableDriverService (PIRESOURCE pir, BOOL fEnable)
|
|
{
|
|
SC_HANDLE scManager;
|
|
SC_HANDLE scDriver;
|
|
TCHAR szName[ cchRESOURCE ];
|
|
|
|
lsplitpath (pir->szFile, NULL, NULL, szName, NULL);
|
|
|
|
// First step: determine if the driver has a service
|
|
//
|
|
if ((scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) != NULL)
|
|
{
|
|
if ((scDriver = OpenService (scManager, szName, SERVICE_ALL_ACCESS)) != 0)
|
|
{
|
|
QUERY_SERVICE_CONFIG qsc;
|
|
SERVICE_STATUS ss;
|
|
DWORD cbReq;
|
|
void *pqsc;
|
|
|
|
ChangeServiceConfig (scDriver,
|
|
SERVICE_NO_CHANGE,
|
|
(fEnable) ? SERVICE_SYSTEM_START
|
|
: SERVICE_DISABLED,
|
|
SERVICE_NO_CHANGE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
CloseServiceHandle (scDriver);
|
|
}
|
|
|
|
CloseServiceHandle (scManager);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
***************************************************************
|
|
* SetMappable
|
|
*
|
|
* Sets the "Mappable" value for wave devices in the registry.
|
|
* The registry key is created if necessary
|
|
*
|
|
***************************************************************
|
|
*/
|
|
|
|
BOOL SetMappable (PIRESOURCE pIResource, BOOL fMappable)
|
|
{
|
|
TCHAR szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
|
|
TCHAR szExt[ _MAX_EXT +1 ];
|
|
TCHAR szRegKey[MAX_PATH+1];
|
|
DWORD dwMappable;
|
|
HKEY hKey;
|
|
|
|
dwMappable = (fMappable) ? 1 : 0;
|
|
|
|
lsplitpath (pIResource->szFile, NULL, NULL, szFile, szExt);
|
|
if (szExt[0] != TEXT('\0'))
|
|
lstrcat (szFile, szExt);
|
|
|
|
wsprintf (szRegKey, TEXT("%s\\%s"), REGSTR_PATH_WAVEMAPPER, szFile);
|
|
|
|
if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (RegSetValueEx (hKey,
|
|
REGSTR_VALUE_MAPPABLE,
|
|
(DWORD)0,
|
|
REG_DWORD,
|
|
(void *)&dwMappable,
|
|
sizeof(dwMappable)) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey (hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
if (fMappable)
|
|
{
|
|
SetFlag(pIResource->fStatus, dwStatusMAPPABLE);
|
|
}
|
|
else
|
|
{
|
|
ClearFlag(pIResource->fStatus, dwStatusMAPPABLE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL GetMappable (PIDRIVER pIDriver)
|
|
{
|
|
TCHAR szFile[ _MAX_FNAME +1 +_MAX_EXT +1 ];
|
|
TCHAR szExt[ _MAX_EXT +1 ];
|
|
TCHAR szRegKey[MAX_PATH+1];
|
|
DWORD dwMappable;
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
HKEY hKey;
|
|
|
|
lsplitpath (pIDriver->szFile, NULL, NULL, szFile, szExt);
|
|
if (szExt[0] != TEXT('\0'))
|
|
lstrcat (szFile, szExt);
|
|
|
|
wsprintf (szRegKey, TEXT("%s\\%s"), REGSTR_PATH_WAVEMAPPER, szFile);
|
|
|
|
if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
dwSize = sizeof(dwMappable);
|
|
if (RegQueryValueEx (hKey,
|
|
REGSTR_VALUE_MAPPABLE,
|
|
NULL,
|
|
&dwType,
|
|
(void *)&dwMappable,
|
|
&dwSize) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey (hKey);
|
|
return TRUE;
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
return (dwMappable) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
#ifdef FIX_BUG_15451
|
|
|
|
// FixDriverService - work around known problems with sound drivers
|
|
//
|
|
// If there was a functioning kernel-mode service in place before the
|
|
// config dialog was presented, then there should still be one
|
|
// afterwards. However, there are two known problems with the
|
|
// currently-release drivers:
|
|
//
|
|
// 1) The service may not have shut down properly, and is stuck
|
|
// in a STOP_PENDING state(*). If this is the case, we need to
|
|
// ensure that the service is set to load on SYSTEM_START, and
|
|
// tell the user that the machine has to be rebooted before sound
|
|
// will work again.
|
|
//
|
|
// 2) The service may have failed to restart properly, and is
|
|
// stopped(**). If this is the case, and LoadType!=0, try setting
|
|
// LoadType=1 and starting the service.
|
|
//
|
|
// (*) -- bug #15451 in NT/SUR, where pending IRPs and open mixer
|
|
// handles prevent the service from shutting down
|
|
//
|
|
// (**) -- bug #XXXXX in NT/SUR, where some RISC machines stop the
|
|
// service, set LoadType=1, and fail to restart the service
|
|
// after you cancel their config dialog
|
|
//
|
|
BOOL FixDriverService (PIDRIVER pid)
|
|
{
|
|
SC_HANDLE scManager;
|
|
SC_HANDLE scDriver;
|
|
SERVICE_STATUS ss;
|
|
BOOL rc = FALSE;
|
|
TCHAR szName[ cchRESOURCE ];
|
|
|
|
lsplitpath (pid->szFile, NULL, NULL, szName, NULL);
|
|
|
|
// First step: open the service...even if it's hosed, we should
|
|
// still be able to do this.
|
|
//
|
|
if ((scManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ((scDriver = OpenService (scManager, szName, SERVICE_ALL_ACCESS)) == 0)
|
|
{
|
|
CloseServiceHandle (scManager);
|
|
return FALSE;
|
|
}
|
|
|
|
// Now check its status. Look for STOP_PENDING and STOPPED states.
|
|
//
|
|
if (QueryServiceStatus (scDriver, &ss))
|
|
{
|
|
if (ss.dwCurrentState == SERVICE_STOP_PENDING)
|
|
{
|
|
// The service didn't stop properly--we'll have to reboot.
|
|
// Make sure the service is configured such that it will start
|
|
// properly when we restart.
|
|
//
|
|
ChangeServiceConfig (scDriver,
|
|
SERVICE_NO_CHANGE,
|
|
SERVICE_SYSTEM_START, // Enable this puppy!
|
|
SERVICE_NO_CHANGE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Tell the caller that a reboot is mandatory
|
|
rc = TRUE;
|
|
}
|
|
else if (ss.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
TCHAR szKey[ cchRESOURCE ];
|
|
HKEY hkParams;
|
|
|
|
// The service stopped, but didn't restart properly--it could
|
|
// just be the LoadType problem. To fix this, we'll need
|
|
// to enumerate all the keys underneath the key
|
|
// HKLM\System\CurrentControlSet\Services\(thisdriver)\Parameters,
|
|
// and look for ...\Parameters\*\LoadType==(DWORD)1.
|
|
//
|
|
// First step is to open HKLM\System\CCS\Services\(driver)\Parms.
|
|
//
|
|
wsprintf (szKey, TEXT("%s\\%s\\Parameters"),
|
|
REGSTR_PATH_SERVICES,
|
|
szName);
|
|
|
|
if (RegOpenKey (HKEY_LOCAL_MACHINE, szKey, &hkParams) == 0)
|
|
{
|
|
DWORD cSubKeys;
|
|
DWORD iSubKey;
|
|
BOOL fFixedLoadType = FALSE;
|
|
|
|
// Find out how many subkeys there are under here--the
|
|
// keys we want are named "Device0" through "Device(n-1)"
|
|
//
|
|
RegQueryInfoKey (hkParams,
|
|
NULL, // lpClass
|
|
NULL, // lpcbClass
|
|
NULL, // lpReserved
|
|
&cSubKeys, // Whoops! We want this.
|
|
NULL, // lpcbMaxSubKeyLen
|
|
NULL, // lpcbMaxClassLen
|
|
NULL, // lpcValues
|
|
NULL, // lpcbMaxValueNameLen
|
|
NULL, // lpcbMaxValueLen
|
|
NULL, // lpcbSecurityDescriptor
|
|
NULL); // lpftLastWriteTime
|
|
|
|
// Open each subkey in turn, and look for a LoadType=
|
|
// which is bogus.
|
|
//
|
|
for (iSubKey = 0; iSubKey < cSubKeys; ++iSubKey)
|
|
{
|
|
HKEY hk;
|
|
TCHAR szSubKey[ cchRESOURCE ];
|
|
wsprintf (szSubKey, TEXT("Device%lu"), (LONG)iSubKey);
|
|
|
|
if (RegOpenKey (hkParams, szSubKey, &hk) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwLoadType;
|
|
DWORD dwType;
|
|
DWORD dwSize = sizeof(dwType);
|
|
|
|
if (RegQueryValueEx (hk,
|
|
cszRegValueLOADTYPE,
|
|
NULL,
|
|
&dwType,
|
|
(void *)&dwLoadType,
|
|
&dwSize) == 0)
|
|
{
|
|
if (dwLoadType == 1)
|
|
{
|
|
dwLoadType = 0;
|
|
fFixedLoadType = TRUE;
|
|
|
|
RegSetValueEx (hk,
|
|
cszRegValueLOADTYPE,
|
|
0,
|
|
REG_DWORD,
|
|
(void *)&dwLoadType,
|
|
sizeof(dwLoadType));
|
|
}
|
|
}
|
|
|
|
RegCloseKey (hk);
|
|
}
|
|
}
|
|
|
|
// If we fixed a LoadType value, try to restart the service.
|
|
//
|
|
if (fFixedLoadType)
|
|
{
|
|
if (StartService (scDriver, 0, NULL))
|
|
{
|
|
ChangeServiceConfig (scDriver,
|
|
SERVICE_NO_CHANGE,
|
|
SERVICE_SYSTEM_START,
|
|
SERVICE_NO_CHANGE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Clean up. We'll return TRUE if a reboot is necessary, and FALSE
|
|
// otherwise.
|
|
//
|
|
CloseServiceHandle (scDriver);
|
|
CloseServiceHandle (scManager);
|
|
return rc;
|
|
}
|
|
|
|
|
|
void ConfigureDriver (HWND hDlg, LPTSTR pszName)
|
|
{
|
|
PIDRIVER pid;
|
|
HANDLE hDriver;
|
|
BOOL fShowTrayVol;
|
|
BOOL fRestartDialog = FALSE;
|
|
|
|
if ((pid = FindIDriverByName (pszName)) == NULL)
|
|
return;
|
|
|
|
fShowTrayVol = GetTrayVolumeEnabled();
|
|
if (fShowTrayVol)
|
|
SetTrayVolumeEnabled(FALSE);
|
|
|
|
if ((hDriver = OpenDriver (pid->wszAlias, pid->wszSection, 0L)) == 0)
|
|
{
|
|
OpenDriverError(hDlg, pid->szDesc, pid->szFile);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwStatus = GetDriverStatus (pid);
|
|
BOOL fHadService = IsFlagSet (dwStatus, dwStatusSvcSTARTED) &&
|
|
IsFlagSet (dwStatus, dwStatusHASSERVICE);
|
|
|
|
DRVCONFIGINFO DrvConfigInfo;
|
|
InitDrvConfigInfo(&DrvConfigInfo, pid);
|
|
if ((SendDriverMessage(
|
|
hDriver,
|
|
DRV_CONFIGURE,
|
|
(LONG_PTR)hDlg,
|
|
(LONG_PTR)(LPDRVCONFIGINFO)&DrvConfigInfo) ==
|
|
DRVCNF_RESTART))
|
|
{
|
|
iRestartMessage = 0;
|
|
fRestartDialog = TRUE;
|
|
}
|
|
CloseDriver(hDriver, 0L, 0L);
|
|
|
|
// If there was a functioning kernel-mode service in place before the
|
|
// config dialog was presented, then we should verify that there is
|
|
// still one in place now. See FixDriverService() for details.
|
|
//
|
|
if (fHadService)
|
|
{
|
|
dwStatus = GetDriverStatus (pid);
|
|
|
|
if (!IsFlagSet (dwStatus, dwStatusSvcSTARTED) ||
|
|
!IsFlagSet (dwStatus, dwStatusHASSERVICE))
|
|
{
|
|
if (FixDriverService (pid))
|
|
{
|
|
iRestartMessage = IDS_RESTART_NOSOUND;
|
|
fRestartDialog = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (fShowTrayVol)
|
|
SetTrayVolumeEnabled(TRUE);
|
|
|
|
if (fRestartDialog)
|
|
{
|
|
DialogBox(myInstance,MAKEINTRESOURCE(DLG_RESTART),hDlg,RestartDlg);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL fDeviceHasMixers (LPTSTR pszName)
|
|
{
|
|
HKEY hk;
|
|
UINT ii;
|
|
BOOL rc = FALSE;
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_DRIVERS32, &hk))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (ii = 0; ; ++ii)
|
|
{
|
|
TCHAR szLHS[ cchRESOURCE ];
|
|
TCHAR szRHS[ cchRESOURCE ];
|
|
DWORD dw1;
|
|
DWORD dw2;
|
|
DWORD dw3;
|
|
|
|
dw1 = cchRESOURCE;
|
|
dw3 = cchRESOURCE;
|
|
if (RegEnumValue (hk, ii, szLHS, &dw1,
|
|
0, &dw2, (LPBYTE)szRHS, &dw3) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( (GuessDriverClassFromAlias (szLHS) == dcMIXER) &&
|
|
(!FileNameCmp (pszName, szRHS)) )
|
|
{
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RegCloseKey (hk);
|
|
return rc;
|
|
}
|
|
#endif // FIX_BUG_15451
|
|
|
|
|
|
TCHAR c_tszControlExeS[] = TEXT("control.exe %s");
|
|
|
|
BOOL RunJoyControlPanel(void)
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
TCHAR tsz[MAX_PATH];
|
|
BOOL fRtn;
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
wsprintf(tsz, c_tszControlExeS, TEXT("joy.cpl"));
|
|
if (CreateProcess(0, tsz, 0, 0, 0, 0, 0, 0, &si, &pi)) {
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
fRtn = TRUE;
|
|
} else {
|
|
fRtn = FALSE;
|
|
}
|
|
|
|
return fRtn;
|
|
}
|