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

1334 lines
42 KiB
C

#pragma warning( disable: 4103)
#include "mmcpl.h"
#include <cpl.h>
#define NOSTATUSBAR
#include <commctrl.h>
#include <prsht.h>
#include <regstr.h>
#include <infstr.h>
#include <devguid.h>
#include "draw.h"
#include "utils.h"
#include "drivers.h"
#include "sulib.h"
#include "medhelp.h"
#include <tchar.h>
#define GetString(_psz,_id) LoadString(myInstance,(_id),(_psz),sizeof((_psz))/sizeof(TCHAR))
// Global info struct. One instance for the whole dialog
typedef struct _OUR_PROP_PARAMS
{
HDEVINFO DeviceInfoSet;
PSP_DEVINFO_DATA DeviceInfoData;
HKEY hkDrv; // Key to classguid\0000
HKEY hkDrivers; // Key to classguid\0000\Drivers
BOOL bClosing; // Set to TRUE while dialog is closing
TCHAR szSubClasses[256]; // Subclasses to process
} OUR_PROP_PARAMS, *POUR_PROP_PARAMS;
typedef enum
{
NodeTypeRoot,
NodeTypeClass,
NodeTypeDriver
} NODETYPE;
// Tree node. One per node on tree.
typedef struct _DMTREE_NODE;
typedef BOOL (*PFNCONFIG) (HWND ParentHwnd, struct _DMTREE_NODE *pTreeNode);
typedef BOOL (*PFNQUERYCONFIG)(HWND ParentHwnd, struct _DMTREE_NODE *pTreeNode);
typedef struct _DMTREE_NODE
{
NODETYPE NodeType; // Type of node
PFNCONFIG pfnConfig; // Ptr to config function
PFNQUERYCONFIG pfnQueryConfig; // Ptr to query config function
int QueryConfigInfo; // Data for config function
TCHAR szDescription[MAXSTR]; // Node description
TCHAR szDriver[MAXSTR]; // Driver name of this node
WCHAR wszDriver[MAXSTR]; // Wide char driver name
TCHAR szAlias[MAXSTR]; // Alias
WCHAR wszAlias[MAXSTR]; // Wide char alias
DriverClass dc; // Legacy-style driver class, if available
HTREEITEM hti; // For use with MIDI prop sheet callback
} DMTREE_NODE, *PDMTREE_NODE;
INT_PTR APIENTRY DmAdvPropPageDlgProc(IN HWND hDlg,
IN UINT uMessage,
IN WPARAM wParam,
IN LPARAM lParam
);
UINT CALLBACK DmAdvPropPageDlgCallback(HWND hwnd,
UINT uMsg,
LPPROPSHEETPAGE ppsp
);
BOOL DmAdvPropPage_OnCommand(
HWND ParentHwnd,
int ControlId,
HWND ControlHwnd,
UINT NotifyCode
);
BOOL DmAdvPropPage_OnContextMenu(
HWND HwndControl,
WORD Xpos,
WORD Ypos
);
BOOL DmAdvPropPage_OnHelp(
HWND ParentHwnd,
LPHELPINFO HelpInfo
);
BOOL DmAdvPropPage_OnInitDialog(
HWND ParentHwnd,
HWND FocusHwnd,
LPARAM Lparam
);
BOOL DmAdvPropPage_OnNotify(
HWND ParentHwnd,
LPNMHDR NmHdr
);
void DmAdvPropPage_OnPropertiesClicked(
HWND ParentHwnd,
POUR_PROP_PARAMS Params
);
BOOL DmOverrideResourcesPage(LPVOID Info,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam,
POUR_PROP_PARAMS Params
);
BOOL AddCDROMPropertyPage( HDEVINFO hDeviceInfoSet,
PSP_DEVINFO_DATA pDeviceInfoData,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam
);
BOOL AddSpecialPropertyPage( DWORD SpecialDriverType,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam
);
BOOL DmInitDeviceTree(HWND hwndTree, POUR_PROP_PARAMS Params);
BOOL DmAdvPropPage_OnDestroy(
HWND ParentHwnd,
LPNMHDR NmHdr
);
void DoProperties(HWND ParentHwnd, HWND hWndI, HTREEITEM htiCur);
BOOL QueryConfigDriver(HWND ParentHwnd, PDMTREE_NODE pTreeNode)
{
HANDLE hDriver;
if (pTreeNode->NodeType!=NodeTypeDriver)
{
return FALSE;
}
if (pTreeNode->QueryConfigInfo==0) // if 0, the we haven't checked yet
{
INT_PTR IsConfigurable;
// open the driver
hDriver = OpenDriver(pTreeNode->wszDriver, NULL, 0L);
if (!hDriver)
{
return FALSE;
}
// Send the DRV_CONFIGURE message to the driver
IsConfigurable = SendDriverMessage(hDriver,
DRV_QUERYCONFIGURE,
0L,
0L);
CloseDriver(hDriver, 0L, 0L);
// 1->Is configurable, -1->Not configurable
pTreeNode->QueryConfigInfo = IsConfigurable ? 1 : -1;
}
return (pTreeNode->QueryConfigInfo>0);
}
BOOL PNPDriverToIResource(PDMTREE_NODE pTreeNode, IRESOURCE* pir)
{
IDRIVER tempIDriver;
if ((pir->pcn = (PCLASSNODE)LocalAlloc (LPTR, sizeof(CLASSNODE))) == NULL)
{
return FALSE;
}
if (!DriverClassToClassNode(pir->pcn, pTreeNode->dc))
{
LocalFree ((HANDLE)pir->pcn);
return FALSE;
}
pir->iNode = 2; // 1=class, 2=device, 3=acm, 4=instmt
lstrcpy (pir->szFriendlyName, pTreeNode->szDescription);
lstrcpy (pir->szDesc, pTreeNode->szDescription);
lstrcpy (pir->szFile, pTreeNode->szDriver);
lstrcpy (pir->szDrvEntry, pTreeNode->szAlias);
lstrcpy (pir->szClass, pir->pcn->szClass);
pir->fQueryable = (short)QueryConfigDriver(NULL, pTreeNode);
pir->iClassID = (short)DriverClassToOldClassID(pTreeNode->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:
//
ZeroMemory(&tempIDriver,sizeof(IDRIVER));
lstrcpy(tempIDriver.wszAlias,pTreeNode->wszAlias);
lstrcpy(tempIDriver.szAlias,pTreeNode->szAlias);
lstrcpy(tempIDriver.wszFile,pTreeNode->wszDriver);
lstrcpy(tempIDriver.szFile,pTreeNode->szDriver);
lstrcpy(tempIDriver.szDesc,pTreeNode->szDescription);
lstrcpy(tempIDriver.szSection,wcsstr(pTreeNode->szDescription, TEXT("MCI")) ? szMCI : szDrivers);
lstrcpy(tempIDriver.wszSection,wcsstr(pTreeNode->szDescription, TEXT("MCI")) ? szMCI : szDrivers);
pir->fStatus = (int)GetDriverStatus (&tempIDriver);
return TRUE;
}
BOOL ConfigDriver(HWND ParentHwnd, PDMTREE_NODE pTreeNode)
{
//need to pop up the legacy properties dialog
IRESOURCE ir;
DEVTREENODE dtn;
TCHAR szTab[ cchRESOURCE ];
if ((pTreeNode->NodeType == NodeTypeDriver) && (pTreeNode->dc != dcINVALID))
{
if (PNPDriverToIResource(pTreeNode, &ir))
{
GetString (szTab, IDS_GENERAL);
dtn.lParam = (LPARAM)&ir;
dtn.hwndTree = ParentHwnd;
//must call this function twice to fill in the array of PIDRIVERs in drivers.c
//otherwise, many of the "settings" calls won't work
InitInstalled (GetParent (ParentHwnd), szDrivers);
InitInstalled (GetParent (ParentHwnd), szMCI);
switch (pTreeNode->dc)
{
case dcMIDI :
ShowWithMidiDevPropSheet (szTab,
DevPropDlg,
DLG_DEV_PROP,
ParentHwnd,
pTreeNode->szDescription,
pTreeNode->hti,
(LPARAM)&dtn,
(LPARAM)&ir,
(LPARAM)ParentHwnd);
break;
case dcWAVE :
ShowPropSheet (szTab,
DevPropDlg,
DLG_WAVDEV_PROP,
ParentHwnd,
pTreeNode->szDescription,
(LPARAM)&dtn);
break;
default:
ShowPropSheet (szTab,
DevPropDlg,
DLG_DEV_PROP,
ParentHwnd,
pTreeNode->szDescription,
(LPARAM)&dtn);
break;
} //end switch
FreeIResource (&ir);
}
}
return (FALSE);
}
const static DWORD aDMPropHelpIds[] = { // Context Help IDs
IDC_ADV_TREE, IDH_GENERIC_DEVICES,
ID_ADV_PROP, IDH_ADV_PROPERTIES,
0, 0
};
//******************************************************************************
//* Subtype code
//******************************************************************************
//
// Subtype info. Array of one per device class subtype
typedef struct _SUBTYPE_INFO
{
TCHAR *szClass;
DWORD DescId;
DWORD IconId;
PFNCONFIG pfnConfig;
PFNQUERYCONFIG pfnQueryConfig;
DriverClass dc;
TCHAR szDescription[64];
DWORD IconIndex;
} SUBTYPE_INFO;
static SUBTYPE_INFO SubtypeInfo[] =
{
{ TEXT(""), IDS_MM_HEADER, IDI_MMICON, ConfigDriver, QueryConfigDriver, dcOTHER},
{ TEXT("waveaudio"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("wavemap"), IDS_WAVE_HEADER, IDI_WAVE, ConfigDriver, QueryConfigDriver, dcWAVE},
{ TEXT("wave"), IDS_WAVE_HEADER, IDI_WAVE, ConfigDriver, QueryConfigDriver, dcWAVE},
{ TEXT("vids"), IDS_ICM_HEADER, IDI_ICM, ConfigDriver, QueryConfigDriver, dcVCODEC},
{ TEXT("vidc"), IDS_ICM_HEADER, IDI_ICM, ConfigDriver, QueryConfigDriver, dcVCODEC},
{ TEXT("sequencer"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("msvideo"), IDS_VIDCAP_HEADER, IDI_VIDEO, ConfigDriver, QueryConfigDriver, dcVIDCAP},
{ TEXT("msacm"), IDS_ACM_HEADER, IDI_ACM, ConfigDriver, QueryConfigDriver, dcACODEC},
{ TEXT("mpegvideo"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("mixer"), IDS_MIXER_HEADER, IDI_MIXER, ConfigDriver, QueryConfigDriver, dcMIXER},
{ TEXT("midimapper"), IDS_MIDI_HEADER, IDI_MIDI, ConfigDriver, QueryConfigDriver, dcMIDI},
{ TEXT("midi"), IDS_MIDI_HEADER, IDI_MIDI, ConfigDriver, QueryConfigDriver, dcMIDI},
{ TEXT("mci"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("icm"), IDS_ICM_HEADER, IDI_ICM, ConfigDriver, QueryConfigDriver, dcVCODEC},
{ TEXT("cdaudio"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("avivideo"), IDS_MCI_HEADER, IDI_MCI, ConfigDriver, QueryConfigDriver, dcMCI},
{ TEXT("aux"), IDS_AUX_HEADER, IDI_AUX, ConfigDriver, QueryConfigDriver, dcAUX},
{ TEXT("acm"), IDS_ACM_HEADER, IDI_ACM, ConfigDriver, QueryConfigDriver, dcACODEC},
{ TEXT("joy"), IDS_JOYSTICK_HEADER, IDI_JOYSTICK, ConfigDriver, QueryConfigDriver, dcJOY}
};
#define SUBTYPE_INFO_SIZE (sizeof(SubtypeInfo)/sizeof(SUBTYPE_INFO))
BOOL LoadSubtypeInfo(HWND hwndTree)
{
UINT i;
UINT uFlags;
int cxMiniIcon;
int cyMiniIcon;
DWORD dwLayout;
HIMAGELIST hImagelist;
// Create the image list
cxMiniIcon = (int)GetSystemMetrics(SM_CXSMICON);
cyMiniIcon = (int)GetSystemMetrics(SM_CYSMICON);
uFlags = ILC_MASK | ILC_COLOR32;
if (GetProcessDefaultLayout(&dwLayout) &&
(dwLayout & LAYOUT_RTL))
{
uFlags |= ILC_MIRROR;
}
hImagelist = ImageList_Create(cxMiniIcon, cyMiniIcon, uFlags, SUBTYPE_INFO_SIZE, 4);
if (!hImagelist)
return FALSE;
for (i=0;i<SUBTYPE_INFO_SIZE;i++)
{
HICON hIcon;
// Load the description
LoadString(ghInstance, SubtypeInfo[i].DescId, SubtypeInfo[i].szDescription, 64);
// Load the image into the image list
hIcon = LoadImage (ghInstance,
MAKEINTRESOURCE( SubtypeInfo[i].IconId ),
IMAGE_ICON,
cxMiniIcon,
cyMiniIcon,
LR_DEFAULTCOLOR);
if (hIcon) // PREFIX 160723
{
SubtypeInfo[i].IconIndex = ImageList_AddIcon(hImagelist, hIcon);
DestroyIcon(hIcon);
}
else
{
SubtypeInfo[i].IconIndex = -1;
}
}
// Clean out and initialize tree control
TreeView_SetImageList(hwndTree, hImagelist, TVSIL_NORMAL);
return TRUE;
}
SUBTYPE_INFO *GetSubtypeInfo(TCHAR *pszClass)
{
UINT iClass;
if (pszClass)
{
for (iClass=0;iClass<SUBTYPE_INFO_SIZE;iClass++)
{
if (!lstrcmpi(pszClass,SubtypeInfo[iClass].szClass))
return &SubtypeInfo[iClass];
}
}
return &SubtypeInfo[0];
}
//******************************************************************************
/*
Routine Description: MediaPropPageProvider
Entry-point for adding additional device manager property
sheet pages. Registry specifies this routine under
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96C-E325-11CE-BFC1-08002BE10318}
EnumPropPage32="mmsys.cpl,thisproc"
This entry-point gets called only when the DeviceManager asks for additional property pages.
Arguments:
Info - points to PROPSHEETPAGE_REQUEST, see setupapi.h
AddFunc - function ptr to call to add sheet.
Lparam - add sheet functions private data handle.
Return Value:
BOOL: FALSE if pages could not be added, TRUE on success
*/
BOOL APIENTRY MediaPropPageProvider(LPVOID Info,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam
)
{
PSP_PROPSHEETPAGE_REQUEST pprPropPageRequest;
PROPSHEETPAGE psp;
HPROPSHEETPAGE hpsp;
POUR_PROP_PARAMS Params;
HDEVINFO DeviceInfoSet;
PSP_DEVINFO_DATA DeviceInfoData;
DWORD SpecialDriverType;
HKEY hkDrv;
HKEY hkDrivers;
DWORD cbLen;
pprPropPageRequest = (PSP_PROPSHEETPAGE_REQUEST) Info;
if (pprPropPageRequest->PageRequested != SPPSR_ENUM_ADV_DEVICE_PROPERTIES)
{
return TRUE;
}
DeviceInfoSet = pprPropPageRequest->DeviceInfoSet;
DeviceInfoData = pprPropPageRequest->DeviceInfoData;
// This API is called for both devices and the class as a whole
// (when someone right-clicks on the class and chooses properties).
// In the class case the DeviceInfoData field of the propPageRequest structure is NULL.
// We don't do anything in that case, so just return.
if (!DeviceInfoData)
{
return TRUE;
}
SpecialDriverType = IsSpecialDriver(DeviceInfoSet, DeviceInfoData);
if (SpecialDriverType)
{
SP_DEVINSTALL_PARAMS DeviceInstallParams;
DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams);
DeviceInstallParams.Flags |= DI_RESOURCEPAGE_ADDED | DI_DRIVERPAGE_ADDED; // | DI_GENERALPAGE_ADDED;
SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams);
AddSpecialPropertyPage(SpecialDriverType, AddFunc, Lparam);
return TRUE;
}
if (AddCDROMPropertyPage(DeviceInfoSet,DeviceInfoData, AddFunc, Lparam))
{
return TRUE;
}
// Open device reg key to see if this is a WDM driver
hkDrv = SetupDiOpenDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_ALL_ACCESS);
if (!hkDrv)
return FALSE;
// Allocate and zero out memory for the struct that will contain page specific data
Params = (POUR_PROP_PARAMS)LocalAlloc(LPTR, sizeof(OUR_PROP_PARAMS));
if (!Params)
{
RegCloseKey(hkDrv);
return FALSE;
}
// Initialize Params structure
Params->DeviceInfoSet = DeviceInfoSet;
Params->DeviceInfoData = DeviceInfoData;
Params->hkDrv = hkDrv;
// Override Resource page if this is not a WDM (PNP) driver
DmOverrideResourcesPage(Info, AddFunc, Lparam, Params);
// Try a couple of things to see if there are actually any drivers under this key
// and cache the results
// Try to open up the Drivers subkey
if (RegOpenKey(Params->hkDrv, TEXT("Drivers"), &hkDrivers))
{
RegCloseKey(hkDrv);
LocalFree(Params);
return TRUE;
}
// Try to read the SubClasses key to determine which subclasses to process
cbLen=sizeof(Params->szSubClasses);
if (RegQueryValueEx(hkDrivers, TEXT("Subclasses"), NULL, NULL, (LPBYTE)Params->szSubClasses, &cbLen))
{
RegCloseKey(hkDrv);
RegCloseKey(hkDrivers);
LocalFree(Params);
return TRUE;
}
Params->hkDrivers = hkDrivers;
// Initialize the property sheet page
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_USECALLBACK; // | PSP_HASHELP;
psp.hInstance = ghInstance;
psp.pszTemplate = MAKEINTRESOURCE(DLG_DM_ADVDLG);
psp.pfnDlgProc = DmAdvPropPageDlgProc; // dlg window proc
psp.lParam = (LPARAM) Params;
psp.pfnCallback = DmAdvPropPageDlgCallback; // control callback of the dlg window proc
// Create the page & get back a handle
hpsp = CreatePropertySheetPage(&psp);
if (!hpsp)
{
RegCloseKey(hkDrv);
LocalFree(Params);
return FALSE;
}
// Add the property page
if (!(*AddFunc)(hpsp, Lparam))
{
DestroyPropertySheetPage(hpsp);
return FALSE;
}
return TRUE;
} /* DmAdvPropPageProvider */
UINT CALLBACK DmAdvPropPageDlgCallback(HWND hwnd,
UINT uMsg,
LPPROPSHEETPAGE ppsp)
{
POUR_PROP_PARAMS Params;
switch (uMsg)
{
case PSPCB_CREATE: // This gets called when the page is created
return TRUE; // return TRUE to continue with creation of page
case PSPCB_RELEASE: // This gets called when the page is destroyed
Params = (POUR_PROP_PARAMS) ppsp->lParam;
RegCloseKey(Params->hkDrv);
RegCloseKey(Params->hkDrivers);
LocalFree(Params); // Free our local params
return 0; // return value ignored
default:
break;
}
return TRUE;
}
/*++
Routine Description: DmAdvPropPageDlgProc
The windows control function for the Port Settings properties window
Arguments:
hDlg, uMessage, wParam, lParam: standard windows DlgProc parameters
Return Value:
BOOL: FALSE if function fails, TRUE if function passes
--*/
INT_PTR APIENTRY DmAdvPropPageDlgProc(IN HWND hDlg,
IN UINT uMessage,
IN WPARAM wParam,
IN LPARAM lParam)
{
switch (uMessage)
{
case WM_COMMAND:
return DmAdvPropPage_OnCommand(hDlg, (int) LOWORD(wParam), (HWND)lParam, (UINT)HIWORD(wParam));
case WM_CONTEXTMENU:
return DmAdvPropPage_OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
case WM_HELP:
return DmAdvPropPage_OnHelp(hDlg, (LPHELPINFO) lParam);
case WM_INITDIALOG:
return DmAdvPropPage_OnInitDialog(hDlg, (HWND)wParam, lParam);
case WM_NOTIFY:
return DmAdvPropPage_OnNotify(hDlg, (NMHDR *)lParam);
case WM_DESTROY:
return DmAdvPropPage_OnDestroy(hDlg, (NMHDR *)lParam);
}
return FALSE;
} /* DmAdvPropPageDlgProc */
BOOL DmAdvPropPage_OnCommand(
HWND ParentHwnd,
int ControlId,
HWND ControlHwnd,
UINT NotifyCode
)
{
POUR_PROP_PARAMS params =
(POUR_PROP_PARAMS) GetWindowLongPtr(ParentHwnd, DWLP_USER);
switch (ControlId)
{
case ID_ADV_PROP:
DmAdvPropPage_OnPropertiesClicked(ParentHwnd, params);
break;
}
return FALSE;
}
BOOL DmAdvPropPage_OnContextMenu(
HWND HwndControl,
WORD Xpos,
WORD Ypos
)
{
WinHelp(HwndControl,
gszWindowsHlp,
HELP_CONTEXTMENU,
(ULONG_PTR) aDMPropHelpIds);
return FALSE;
}
BOOL DmAdvPropPage_OnHelp(
HWND ParentHwnd,
LPHELPINFO HelpInfo
)
{
if (HelpInfo->iContextType == HELPINFO_WINDOW)
{
WinHelp((HWND) HelpInfo->hItemHandle,
gszWindowsHlp,
HELP_WM_HELP,
(ULONG_PTR) aDMPropHelpIds);
}
return FALSE;
}
BOOL DmAdvPropPage_OnInitDialog(
HWND ParentHwnd,
HWND FocusHwnd,
LPARAM Lparam
)
{
HWND hwndTree;
POUR_PROP_PARAMS Params;
HCURSOR hCursor;
BOOL bSuccess;
// on WM_INITDIALOG call, lParam points to the property sheet page.
//
// The lParam field in the property sheet page struct is set by the
// caller. When I created the property sheet, I passed in a pointer
// to a struct containing information about the device. Save this in
// the user window long so I can access it on later messages.
Params = (POUR_PROP_PARAMS) ((LPPROPSHEETPAGE)Lparam)->lParam;
SetWindowLongPtr(ParentHwnd, DWLP_USER, (ULONG_PTR) Params);
// Put up the wait cursor
hCursor = SetCursor(LoadCursor(NULL,IDC_WAIT));
//create the device tree.
hwndTree = GetDlgItem(ParentHwnd, IDC_ADV_TREE);
// Initialize the tree
bSuccess = DmInitDeviceTree(hwndTree, Params);
// Enable the adv properties button
EnableWindow(GetDlgItem(ParentHwnd, ID_ADV_PROP), TRUE);
// Tear down the wait cursor
SetCursor(hCursor);
return bSuccess;
}
BOOL DmAdvPropPage_OnNotify(
HWND ParentHwnd,
LPNMHDR NmHdr
)
{
POUR_PROP_PARAMS Params = (POUR_PROP_PARAMS) GetWindowLongPtr(ParentHwnd, DWLP_USER);
switch (NmHdr->code)
{
case PSN_APPLY: // Sent when the user clicks on Apply OR OK !!
SetWindowLongPtr(ParentHwnd, DWLP_MSGRESULT, (LONG_PTR)PSNRET_NOERROR);
return TRUE;
case TVN_SELCHANGED:
//Don't bother if we are closing. This helps avoid irritating
//redraw problems as destroy causes several of these messages to be sent.
if (!Params->bClosing)
{
LPNM_TREEVIEW lpnmtv;
TV_ITEM tvi;
PDMTREE_NODE pTreeNode;
BOOL fEnablePropButton;
HWND hwndProp;
lpnmtv = (LPNM_TREEVIEW)NmHdr;
tvi = lpnmtv->itemNew;
pTreeNode = (PDMTREE_NODE)tvi.lParam;
fEnablePropButton = pTreeNode->pfnQueryConfig(ParentHwnd, pTreeNode);
//override the enabling for driver entries
if ((pTreeNode->NodeType == NodeTypeDriver) && (pTreeNode->dc != dcINVALID))
{
fEnablePropButton = TRUE;
}
// Enable or disable the Properties button depending upon
// whether this driver can be configured
hwndProp = GetDlgItem(ParentHwnd, ID_ADV_PROP);
EnableWindow(hwndProp, fEnablePropButton);
}
break;
case NM_DBLCLK:
//show properties on a double-click
if (NmHdr->idFrom == (DWORD)IDC_ADV_TREE)
{
HWND hwndTree;
TV_HITTESTINFO tvht;
hwndTree = GetDlgItem(ParentHwnd, IDC_ADV_TREE);
// Find out which tree item the cursor is on and call properties on it
GetCursorPos(&tvht.pt);
ScreenToClient(hwndTree, &tvht.pt);
TreeView_HitTest(hwndTree, &tvht);
if (tvht.flags & TVHT_ONITEM)
{
DoProperties(ParentHwnd, hwndTree, tvht.hItem);
}
}
break;
#if 0 // stolen from Win98, not integrated yet
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 TVN_ITEMEXPANDING:
{
TV_ITEM tvi;
HWND hwndTree = GetDlgItem(hDlg,IDC_ADV_TREE);
tvi = lpnmtv->itemNew;
tvi.mask = TVIF_PARAM;
if (!TreeView_GetItem(hwndTree, &tvi))
break;
if (!tvi.lParam || IsBadReadPtr((LPVOID)tvi.lParam, 2))
{
DPF("****TVN_ITEMEXPANDING: lParam = 0 || BadReadPtr***\r\n");
break;
}
if (*((short *)(tvi.lParam)) == 1)
{
//re-enum ACM codecs on expand because their states could have been programmatically changed.
PCLASSNODE pcn = (PCLASSNODE)(tvi.lParam);
if (lpnmtv->action == TVE_EXPAND && !lstrcmpi(pcn->szClass, ACM))
{
if (gfLoadedACM)
ACMNodeChange(hDlg);
}
}
else if (!tvi.lParam && lpnmtv->action == TVE_COLLAPSE)
{
//dont let the root collapse.
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LPARAM)(LRESULT)TRUE);
return TRUE;
}
break;
}
case TVN_BEGINLABELEDIT:
//we don't want to allow editing of label unless the user explicitly wants it by
//clicking context menu item
if (!gfEditLabel)
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LPARAM)(LRESULT)TRUE);
return TRUE;
case TVN_ENDLABELEDIT:
{
HWND hwndTree;
LPSTR pszFriendlyName = ((TV_DISPINFO *) lpnm)->item.pszText;
TV_ITEM item;
HTREEITEM hti;
PIRESOURCE pIResource;
char szWarn[128];
char ach[MAXSTR];
//user has chosen a new friendly name. COnfirm with the user and put it in the
//registry. ALso unhook KB hook which was used to track Esc and Return
if (gfnKBHook)
{
UnhookWindowsHookEx(gfnKBHook);
gfnKBHook = NULL;
}
if (!pszFriendlyName)
return FALSE;
hwndTree = GetDlgItem(hDlg, IDC_ADV_TREE);
hti = TreeView_GetSelection(hwndTree);
item.hItem = hti;
item.mask = TVIF_PARAM;
TreeView_GetItem(hwndTree, &item);
LoadString(ghInstance, IDS_FRIENDLYWARNING, szWarn, sizeof(szWarn));
wsprintf(ach, szWarn, pszFriendlyName);
LoadString(ghInstance, IDS_FRIENDLYNAME, szWarn, sizeof(szWarn));
if (mmse_MessageBox(hDlg, ach, szWarn, MMSE_YESNO) == MMSE_NO)
{
SetFocus(hwndTree);
return FALSE;
}
if (*((short *)(item.lParam)) == 2)
{
pIResource = (PIRESOURCE)item.lParam;
lstrcpy(pIResource->szFriendlyName, pszFriendlyName);
SaveDevFriendlyName(pIResource);
}
else
{
PINSTRUMENT pInstr = (PINSTRUMENT)item.lParam;
lstrcpy(pInstr->szFriendlyName, pszFriendlyName);
SaveInstrFriendlyName(pInstr);
}
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LPARAM)(LRESULT)TRUE);
return TRUE;
}
case NM_RCLICK:
//popup context menu.
TreeContextMenu(hDlg, GetDlgItem(hDlg, IDC_ADV_TREE));
return TRUE;
#endif
default:
return FALSE;
}
return FALSE;
}
void DoProperties(HWND ParentHwnd, HWND hWndI, HTREEITEM htiCur)
{
TV_ITEM tvi;
PDMTREE_NODE pTreeNode;
BOOL bRestart;
// Get item struct attached to selected node
tvi.mask = TVIF_PARAM;
tvi.hItem = htiCur;
if (TreeView_GetItem (hWndI, &tvi))
{
// Get my private data structure from item struct
pTreeNode = (PDMTREE_NODE)tvi.lParam;
if (pTreeNode->NodeType != NodeTypeDriver)
{
if (!pTreeNode->pfnQueryConfig(ParentHwnd, pTreeNode))
{
return;
}
}
// Call config and get back whether restart needed
pTreeNode->hti = htiCur; //this allows us to work with the legacy MIDI setup code
bRestart = pTreeNode->pfnConfig(ParentHwnd, pTreeNode);
if (bRestart)
{
PropSheet_Changed(GetParent(ParentHwnd), ParentHwnd);
}
}
return;
}
void DmAdvPropPage_OnPropertiesClicked(
HWND ParentHwnd,
POUR_PROP_PARAMS Params
)
{
HWND hWndI;
HTREEITEM htiCur;
// Get handle to treeview control
hWndI = GetDlgItem(ParentHwnd, IDC_ADV_TREE);
// Get handle to currently selected node
htiCur = TreeView_GetSelection (hWndI);
if (htiCur != NULL)
{
DoProperties(ParentHwnd, hWndI, htiCur);
}
return;
}
INT_PTR APIENTRY DmResourcesPageDlgProc(IN HWND hDlg,
IN UINT uMessage,
IN WPARAM wParam,
IN LPARAM lParam)
{
return FALSE;
} /* DmAdvPropPageDlgProc */
INT_PTR CALLBACK CDDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL AddCDROMPropertyPage( HDEVINFO hDeviceInfoSet,
PSP_DEVINFO_DATA pDeviceInfoData,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam
)
{
BOOL fHandled = FALSE;
if (IsEqualGUID(&pDeviceInfoData->ClassGuid,&GUID_DEVCLASS_CDROM))
{
PROPSHEETPAGE psp;
HPROPSHEETPAGE hpsp;
PALLDEVINFO padi;
padi = GlobalAllocPtr(GHND, sizeof(ALLDEVINFO));
if (!padi) return FALSE;
padi->hDevInfo = hDeviceInfoSet;
padi->pDevInfoData = pDeviceInfoData;
// Add our own page for DLG_DM_LEGACY_RESOURCES
// Initialize the property sheet page
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = 0;
psp.hInstance = ghInstance;
psp.pszTemplate = MAKEINTRESOURCE(DM_CDDLG);
psp.pfnDlgProc = CDDlg; // dlg window proc
psp.lParam = (LPARAM) padi;
psp.pfnCallback = 0; // control callback of the dlg window proc
// Create the page & get back a handle
hpsp = CreatePropertySheetPage(&psp);
if (!hpsp)
{
fHandled = TRUE;
}
else if (!(*AddFunc)(hpsp, Lparam))
{
GlobalFreePtr(padi);
DestroyPropertySheetPage(hpsp);
fHandled = FALSE;
}
}
return(fHandled);
}
INT_PTR CALLBACK AdvDlg(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL AddSpecialPropertyPage( DWORD SpecialDriverType,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam
)
{
PROPSHEETPAGE psp;
HPROPSHEETPAGE hpsp;
// Add our own page for DLG_DM_LEGACY_RESOURCES
// Initialize the property sheet page
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = 0;
psp.hInstance = ghInstance;
psp.pszTemplate = MAKEINTRESOURCE(DM_ADVDLG);
psp.pfnDlgProc = AdvDlg; // dlg window proc
psp.lParam = (LPARAM)SpecialDriverType;
psp.pfnCallback = 0; // control callback of the dlg window proc
// Create the page & get back a handle
hpsp = CreatePropertySheetPage(&psp);
if (!hpsp)
{
return FALSE;
}
// Add the property page
if (!(*AddFunc)(hpsp, Lparam))
{
DestroyPropertySheetPage(hpsp);
return FALSE;
}
return TRUE;
}
BOOL DmOverrideResourcesPage(LPVOID Info,
LPFNADDPROPSHEETPAGE AddFunc,
LPARAM Lparam,
POUR_PROP_PARAMS Params
)
{
HKEY hkDrv;
HDEVINFO DeviceInfoSet;
PSP_DEVINFO_DATA DeviceInfoData;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
PROPSHEETPAGE psp;
HPROPSHEETPAGE hpsp;
TCHAR szDriverType[16];
DWORD cbLen;
hkDrv = Params->hkDrv;
DeviceInfoSet = Params->DeviceInfoSet;
DeviceInfoData = Params->DeviceInfoData;
// Query value of DriverType field to decide if this is a WDM driver
cbLen = sizeof(szDriverType);
if (!RegQueryValueEx(hkDrv, TEXT("DriverType"), NULL, NULL, (LPBYTE)szDriverType, &cbLen))
{
if ( lstrcmpi(szDriverType,TEXT("Legacy")) || lstrcmpi(szDriverType,TEXT("PNPISA")) )
{
// This is a PNPISA or Legacy device. Override resource page.
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams);
DeviceInstallParams.Flags |= DI_RESOURCEPAGE_ADDED;
SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams);
// Add our own page for DLG_DM_LEGACY_RESOURCES
// Initialize the property sheet page
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = 0;
psp.hInstance = ghInstance;
psp.pszTemplate = MAKEINTRESOURCE(DLG_DM_LEGACY_RESOURCES);
psp.pfnDlgProc = DmResourcesPageDlgProc; // dlg window proc
psp.lParam = (LPARAM)0;
psp.pfnCallback = 0; // control callback of the dlg window proc
// Create the page & get back a handle
hpsp = CreatePropertySheetPage(&psp);
if (!hpsp)
{
return FALSE;
}
// Add the property page
if (!(*AddFunc)(hpsp, Lparam))
{
DestroyPropertySheetPage(hpsp);
return FALSE;
}
}
}
return TRUE;
}
/*
***************************************************************
* BOOL DmInitDeviceTree
*
* This function calls commctrl to create the image list and tree and
* the opens the registry, reads each class and loads all devices under
* the class by calling ReadNodes. For ACM however it uses ACM
* APIs (this enumeration code is in msacmcpl.c)
***************************************************************
*/
BOOL DmInitDeviceTree(HWND hwndTree, POUR_PROP_PARAMS Params)
{
TV_INSERTSTRUCT ti;
HDEVINFO DeviceInfoSet;
PSP_DEVINFO_DATA DeviceInfoData;
TCHAR *strtok_State; // strtok state
TCHAR *pszClass; // Information about e.g. classguid\0000\Drivers\wave
HKEY hkClass;
DWORD idxR3DriverName; // Information about e.g. classguid\0000\Drivers\wave\foo.drv
HKEY hkR3DriverName;
TCHAR szR3DriverName[64];
DWORD cbLen;
PDMTREE_NODE pTreeNode;
SUBTYPE_INFO *pSubtypeInfo;
HTREEITEM htiRoot;
HTREEITEM htiClass;
HTREEITEM htiDriver;
// Load up all the class descriptions and icons
LoadSubtypeInfo(hwndTree);
// Clear out the tree
SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0L);
// Allocate my private data structure for this class
pTreeNode = (PDMTREE_NODE)LocalAlloc(LPTR, sizeof(DMTREE_NODE));
if (!pTreeNode)
{
return FALSE;
}
pSubtypeInfo = GetSubtypeInfo(NULL);
pTreeNode->NodeType = NodeTypeRoot;
pTreeNode->pfnConfig = pSubtypeInfo->pfnConfig;
pTreeNode->pfnQueryConfig = pSubtypeInfo->pfnQueryConfig;
// Insert root entry
ti.hParent = TVI_ROOT;
ti.hInsertAfter = TVI_LAST;
ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
ti.item.iImage = ti.item.iSelectedImage = pSubtypeInfo->IconIndex;
ti.item.pszText = pSubtypeInfo->szDescription;
ti.item.lParam = (LPARAM)pTreeNode;
htiRoot = NULL; //TreeView_InsertItem(hwndTree, &ti);
// Enumerate all the subclasses
for (
pszClass = mystrtok(Params->szSubClasses,NULL,&strtok_State);
pszClass;
pszClass = mystrtok(NULL,NULL,&strtok_State)
)
{
// Get an ID for this class
pSubtypeInfo = GetSubtypeInfo(pszClass);
// Open up each subclass
if (RegOpenKey(Params->hkDrivers, pszClass, &hkClass))
{
continue;
}
// Allocate my private data structure for this class
pTreeNode = (PDMTREE_NODE)LocalAlloc(LPTR, sizeof(DMTREE_NODE));
if (!pTreeNode)
{
RegCloseKey(hkClass);
continue;
}
pTreeNode->NodeType = NodeTypeClass;
pTreeNode->pfnConfig = pSubtypeInfo->pfnConfig;
pTreeNode->pfnQueryConfig = pSubtypeInfo->pfnQueryConfig;
// Initialize tree insert struct
ti.hParent = htiRoot;
ti.hInsertAfter = TVI_LAST;
ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
ti.item.iImage = ti.item.iSelectedImage = pSubtypeInfo->IconIndex;
ti.item.pszText = pSubtypeInfo->szDescription;
ti.item.lParam = (LPARAM)pTreeNode;
// Insert Class entry into tree
htiClass = TreeView_InsertItem(hwndTree, &ti);
// Under each class is a set of driver name subkeys.
// For each driver (e.g. foo1.drv, foo2.drv, etc.)
for (idxR3DriverName = 0;
!RegEnumKey(hkClass, idxR3DriverName, szR3DriverName, sizeof(szR3DriverName)/sizeof(TCHAR));
idxR3DriverName++)
{
// Open the key to the driver name
if (RegOpenKey(hkClass, szR3DriverName, &hkR3DriverName))
{
continue;
}
// Create the branch for this subclass
pTreeNode = (PDMTREE_NODE)LocalAlloc(LPTR, sizeof(DMTREE_NODE));
pTreeNode->NodeType = NodeTypeDriver;
pTreeNode->pfnConfig = pSubtypeInfo->pfnConfig;
pTreeNode->pfnQueryConfig = pSubtypeInfo->pfnQueryConfig;
pTreeNode->dc = pSubtypeInfo->dc;
// Get driver name
cbLen = sizeof(pTreeNode->szDriver);
RegQueryValueEx(hkR3DriverName, TEXT("Driver"), NULL, NULL, (LPBYTE)pTreeNode->szDriver, &cbLen);
wcscpy(pTreeNode->wszDriver, pTreeNode->szDriver);
// Get driver description
cbLen = sizeof(pTreeNode->szDescription);
RegQueryValueEx(hkR3DriverName, TEXT("Description"), NULL, NULL, (LPBYTE)pTreeNode->szDescription, &cbLen);
// Get driver alias
cbLen = sizeof(pTreeNode->szAlias);
RegQueryValueEx(hkR3DriverName, TEXT("Alias"), NULL, NULL, (LPBYTE)pTreeNode->szAlias, &cbLen);
wcscpy(pTreeNode->wszAlias, pTreeNode->szAlias);
// Insert Class entry
ti.hParent = htiClass;
ti.hInsertAfter = TVI_LAST;
ti.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
ti.item.iImage = ti.item.iSelectedImage = pSubtypeInfo->IconIndex;
ti.item.pszText = pTreeNode->szDescription;
ti.item.lParam = (LPARAM)pTreeNode;
htiDriver = TreeView_InsertItem(hwndTree, &ti);
// Close the Driver Name key
RegCloseKey(hkR3DriverName);
}
// Close the class key
RegCloseKey(hkClass);
}
// Open up the tree and display
TreeView_Expand(hwndTree, htiRoot, TVE_EXPAND);
SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0L);
return TRUE;
}
// Free up the tree
void DmFreeAdvDlgTree (HWND hTree, HTREEITEM hti)
{
HTREEITEM htiChild;
TV_ITEM tvi;
// Delete all children by calling myself recursively
while ((htiChild = TreeView_GetChild(hTree, hti)) != NULL)
{
DmFreeAdvDlgTree(hTree, htiChild);
}
if (hti!=TVI_ROOT)
{
// Delete my attached data structures
tvi.mask = TVIF_PARAM;
tvi.hItem = hti;
tvi.lParam = 0;
TreeView_GetItem(hTree, &tvi);
if (tvi.lParam != 0)
LocalFree ((HANDLE)tvi.lParam);
// Delete myself
TreeView_DeleteItem (hTree, hti);
}
return;
}
BOOL DmAdvPropPage_OnDestroy(
HWND ParentHwnd,
LPNMHDR NmHdr
)
{
HWND hTree;
HIMAGELIST hImageList;
POUR_PROP_PARAMS Params = (POUR_PROP_PARAMS) GetWindowLongPtr(ParentHwnd, DWLP_USER);
if (Params)
{
Params->bClosing = TRUE; // Remember that we're now closing
}
// Get handle to treeview control
hTree = GetDlgItem(ParentHwnd, IDC_ADV_TREE);
// Free all the entries on the control
DmFreeAdvDlgTree(hTree,TVI_ROOT);
// Free up the image list attached to the control
hImageList = TreeView_GetImageList(hTree, TVSIL_NORMAL);
if (hImageList)
{
TreeView_SetImageList(hTree, NULL, TVSIL_NORMAL);
ImageList_Destroy (hImageList);
}
return FALSE;
}