|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation
//
// File: hotplug.cpp
//
//--------------------------------------------------------------------------
#include "HotPlug.h"
TCHAR szUnknown[64];
TCHAR szHotPlugFlags[]=TEXT("HotPlugFlags"); TCHAR HOTPLUG_NOTIFY_CLASS_NAME[] = TEXT("HotPlugNotifyClass");
typedef int (*PDEVICEPROPERTIES)( HWND hwndParent, LPTSTR MachineName, LPTSTR DeviceID, BOOL ShowDeviceTree );
//
// colors used to highlight removal relationships for selected device
//
COLORREF RemovalImageBkColor; COLORREF NormalImageBkColor; COLORREF RemovalTextColor;
HWND g_hwndNotify = NULL; HMODULE hDevMgr=NULL; PDEVICEPROPERTIES pDeviceProperties = NULL;
#define IDH_DISABLEHELP ((DWORD)(-1))
#define IDH_hwwizard_devices_list 15301 // (SysTreeView32)
#define idh_hwwizard_stop 15305 // "&Stop" (Button)
#define idh_hwwizard_display_components 15307 // "&Display device components" (Button)
#define idh_hwwizard_properties 15311 // "&Properties" (Button)
#define idh_hwwizard_close 15309 // "&Close" (Button)
DWORD UnplugtHelpIDs[] = { IDC_STOPDEVICE, idh_hwwizard_stop, // "&Stop" (Button)
IDC_PROPERTIES, idh_hwwizard_properties, // "&Properties" (Button)
IDC_VIEWOPTION, idh_hwwizard_display_components, // "&Display device components" (Button)
IDC_DEVICETREE, IDH_hwwizard_devices_list, // "" (SysTreeView32)
IDCLOSE, idh_hwwizard_close, IDC_HDWDEVICES, NO_HELP, IDC_NOHELP1, NO_HELP, IDC_NOHELP2, NO_HELP, IDC_NOHELP3, NO_HELP, IDC_DEVICEDESC, NO_HELP, 0,0 };
void OnRemoveDevice( HWND hDlg, PDEVICETREE DeviceTree ) { PDEVTREENODE DeviceTreeNode;
DeviceTreeNode = DeviceTree->ChildRemovalList;
if (!DeviceTreeNode) {
return; }
//
// Confirm with the user that they really want
// to remove this device and all of its attached devices.
// The dialog returns standard IDOK, IDCANCEL etc. for results.
// if anything besides IDOK don't do anything.
//
DialogBoxParam(hHotPlug, MAKEINTRESOURCE(DLG_CONFIRMREMOVE), hDlg, RemoveConfirmDlgProc, (LPARAM)DeviceTree );
return; }
void OnTvnSelChanged( PDEVICETREE DeviceTree, NM_TREEVIEW *nmTreeView ) { PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmTreeView->itemNew.lParam); PTCHAR DeviceName; ULONG DevNodeStatus, Problem; CONFIGRET ConfigRet; TCHAR Buffer[MAX_PATH*2];
if (DeviceTree->RedrawWait) {
return; }
//
// Clear Removal list for previously selected Node
//
ClearRemovalList(DeviceTree);
//
// Save the selected treenode.
//
DeviceTree->SelectedTreeNode = DeviceTreeNode;
//
// No device is selected
//
if (!DeviceTreeNode) {
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE); EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), FALSE); SetDlgItemText(DeviceTree->hDlg, IDC_DEVICEDESC, TEXT("")); return; }
//
// reset the text for the selected item
//
DeviceName = FetchDeviceName(DeviceTreeNode);
if (!DeviceName) {
DeviceName = szUnknown; }
StringCchPrintf(Buffer, SIZECHARS(Buffer), TEXT("%s %s"), DeviceName, DeviceTreeNode->Location ? DeviceTreeNode->Location : TEXT("") );
SetDlgItemText(DeviceTree->hDlg, IDC_DEVICEDESC, Buffer);
//
// Turn on the stop\eject button, and set text accordingly.
//
ConfigRet = CM_Get_DevNode_Status_Ex(&DevNodeStatus, &Problem, DeviceTreeNode->DevInst, 0, NULL ); if (ConfigRet != CR_SUCCESS) {
DevNodeStatus = 0; Problem = 0; }
//
// Any removable (but not surprise removable) device is OK, except
// if the user already removed it.
//
if (Problem != CM_PROB_HELD_FOR_EJECT) {
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), TRUE);
} else {
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE); }
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), TRUE);
//
// reset the overlay icons if device state has changed
//
if (DeviceTreeNode->Problem != Problem || DeviceTreeNode->DevNodeStatus != DevNodeStatus) {
TV_ITEM tv;
tv.mask = TVIF_STATE; tv.stateMask = TVIS_OVERLAYMASK; tv.hItem = DeviceTreeNode->hTreeItem;
if (DeviceTreeNode->Problem == CM_PROB_DISABLED) {
tv.state = INDEXTOOVERLAYMASK(IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
} else if (DeviceTreeNode->Problem) {
tv.state = INDEXTOOVERLAYMASK(IDI_PROBLEM_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
} else {
tv.state = INDEXTOOVERLAYMASK(0); }
TreeView_SetItem(DeviceTree->hwndTree, &tv); }
//
// Starting from the TopLevel removal node, build up the removal lists
//
DeviceTreeNode = TopLevelRemovalNode(DeviceTree, DeviceTreeNode);
//
// Add devices to ChildRemoval list
//
DeviceTree->ChildRemovalList = DeviceTreeNode; DeviceTreeNode->NextChildRemoval = DeviceTreeNode; InvalidateTreeItemRect(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem); AddChildRemoval(DeviceTree, &DeviceTreeNode->ChildSiblingList);
//
// Add eject amd removal relations
//
AddEjectToRemoval(DeviceTree); }
int OnCustomDraw( HWND hDlg, PDEVICETREE DeviceTree, LPNMTVCUSTOMDRAW nmtvCustomDraw ) { PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmtvCustomDraw->nmcd.lItemlParam);
UNREFERENCED_PARAMETER(hDlg);
if (nmtvCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT) { return CDRF_NOTIFYITEMDRAW; }
//
// If this node is in the Removal list, then do special
// highlighting.
//
if (DeviceTreeNode->NextChildRemoval) {
//
// set text color if its not the selected item
//
if (DeviceTree->SelectedTreeNode != DeviceTreeNode) { nmtvCustomDraw->clrText = RemovalTextColor; }
//
// Highlight the image-icon background
//
ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList, RemovalImageBkColor ); } else {
//
// Normal image-icon background
//
ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList, NormalImageBkColor ); }
return CDRF_DODEFAULT; }
void OnSysColorChange( HWND hDlg, PDEVICETREE DeviceTree ) { COLORREF ColorWindow, ColorHighlight; BYTE Red, Green, Blue;
UNREFERENCED_PARAMETER(hDlg);
//
// Fetch the colors used for removal highlighting
//
ColorWindow = GetSysColor(COLOR_WINDOW); ColorHighlight = GetSysColor(COLOR_HIGHLIGHT);
Red = (BYTE)(((WORD)GetRValue(ColorWindow) + (WORD)GetRValue(ColorHighlight)) >> 1); Green = (BYTE)(((WORD)GetGValue(ColorWindow) + (WORD)GetGValue(ColorHighlight)) >> 1); Blue = (BYTE)(((WORD)GetBValue(ColorWindow) + (WORD)GetBValue(ColorHighlight)) >> 1);
RemovalImageBkColor = RGB(Red, Green, Blue); RemovalTextColor = ColorHighlight; NormalImageBkColor = ColorWindow;
//
// Update the ImageList Background color
//
if (DeviceTree->ClassImageList.cbSize) { ImageList_SetBkColor(DeviceTree->ClassImageList.ImageList, ColorWindow ); } }
void OnTvnItemExpanding( HWND hDlg, NM_TREEVIEW *nmTreeView ) { PDEVTREENODE DeviceTreeNode = (PDEVTREENODE)(nmTreeView->itemNew.lParam);
//
// don't allow collapse of root items with children
//
if (!DeviceTreeNode->ParentNode && (nmTreeView->action == TVE_COLLAPSE || nmTreeView->action == TVE_COLLAPSERESET || (nmTreeView->action == TVE_TOGGLE && (nmTreeView->itemNew.state & TVIS_EXPANDED))) ) { SetDlgMsgResult(hDlg, WM_NOTIFY, TRUE); } else { SetDlgMsgResult(hDlg, WM_NOTIFY, FALSE); } }
void OnContextMenu( HWND hDlg, PDEVICETREE DeviceTree ) { int IdCmd; POINT ptPopup; RECT rect; HMENU hMenu; PDEVTREENODE DeviceTreeNode; TCHAR Buffer[MAX_PATH];
DeviceTreeNode = DeviceTree->SelectedTreeNode; if (!DeviceTreeNode) { return; }
TreeView_GetItemRect(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem, &rect, TRUE );
ptPopup.x = (rect.left+rect.right)/2; ptPopup.y = (rect.top+rect.bottom)/2; ClientToScreen(DeviceTree->hwndTree, &ptPopup);
hMenu = CreatePopupMenu(); if (!hMenu) { return; }
//
// if device is running add stop item
//
if (DeviceTreeNode->DevNodeStatus & DN_STARTED) {
LoadString(hHotPlug, IDS_STOP, Buffer, SIZECHARS(Buffer) );
AppendMenu(hMenu, MF_STRING, IDC_STOPDEVICE, Buffer); }
//
// add Properties item (link to device mgr).
//
LoadString(hHotPlug, IDS_PROPERTIES, Buffer, SIZECHARS(Buffer) );
AppendMenu(hMenu, MF_STRING, IDC_PROPERTIES, Buffer);
IdCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_NONOTIFY, ptPopup.x, ptPopup.y, 0, hDlg, NULL );
DestroyMenu(hMenu);
if (!IdCmd) {
return; }
switch (IdCmd) { case IDC_STOPDEVICE: OnRemoveDevice(hDlg, DeviceTree); break;
case IDC_PROPERTIES: { if (pDeviceProperties) { (*pDeviceProperties)( hDlg, NULL, DeviceTreeNode->InstanceId, FALSE ); } } break; }
return; }
void OnRightClick( HWND hDlg, PDEVICETREE DeviceTree, NMHDR * nmhdr ) { DWORD dwPos; TV_ITEM tvi; TV_HITTESTINFO tvht; PDEVTREENODE DeviceTreeNode;
UNREFERENCED_PARAMETER(hDlg);
if (nmhdr->hwndFrom != DeviceTree->hwndTree) { return; }
dwPos = GetMessagePos();
tvht.pt.x = LOWORD(dwPos); tvht.pt.y = HIWORD(dwPos);
ScreenToClient(DeviceTree->hwndTree, &tvht.pt); tvi.hItem = TreeView_HitTest(DeviceTree->hwndTree, &tvht); if (!tvi.hItem) { return; }
tvi.mask = TVIF_PARAM; if (!TreeView_GetItem(DeviceTree->hwndTree, &tvi)) { return; }
DeviceTreeNode = (PDEVTREENODE)tvi.lParam; if (!DeviceTreeNode) { return; }
//
// Make the current right click item, the selected item
//
if (DeviceTreeNode != DeviceTree->SelectedTreeNode) { TreeView_SelectItem(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem); } }
void OnViewOptionClicked( HWND hDlg, PDEVICETREE DeviceTree ) { BOOL bChecked; DWORD HotPlugFlags, NewFlags; HKEY hKey = NULL;
//
// checked means "show complex view"
//
bChecked = IsDlgButtonChecked(hDlg, IDC_VIEWOPTION);
//
// Update HotPlugs registry if needed.
//
NewFlags = HotPlugFlags = GetHotPlugFlags(&hKey);
if (hKey) {
if (bChecked) {
NewFlags |= HOTPLUG_REGFLAG_VIEWALL; } else {
NewFlags &= ~HOTPLUG_REGFLAG_VIEWALL; }
if (NewFlags != HotPlugFlags) {
RegSetValueEx(hKey, szHotPlugFlags, 0, REG_DWORD, (LPBYTE)&NewFlags, sizeof(NewFlags) ); }
if (hKey) {
RegCloseKey(hKey); } }
if (!DeviceTree->ComplexView && bChecked) {
DeviceTree->ComplexView = TRUE; } else if (DeviceTree->ComplexView && !bChecked) {
DeviceTree->ComplexView = FALSE; } else {
// we are in the correct state, nothing to do.
return; }
//
// redraw the entire tree.
//
RefreshTree(DeviceTree);
return; }
LRESULT hotplugNotifyWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { HWND hMainWnd; hMainWnd = (HWND)GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch (uMsg) { case WM_CREATE: hMainWnd = (HWND)((CREATESTRUCT*)lParam)->lpCreateParams; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)hMainWnd); break;
case WM_DEVICECHANGE: if (DBT_DEVNODES_CHANGED == wParam) { //
// While we are in WM_DEVICECHANGE context,
// no CM apis can be called because it would
// deadlock. Here, we schedule a timer so that
// we can handle the message later on.
//
SetTimer(hMainWnd, TIMERID_DEVICECHANGE, 1000, NULL); } break;
default: break; }
return DefWindowProc(hWnd, uMsg, wParam, lParam); }
BOOL CreateNotifyWindow( HWND hWnd ) { WNDCLASS wndClass;
if (!GetClassInfo(hHotPlug, HOTPLUG_NOTIFY_CLASS_NAME, &wndClass)) {
ZeroMemory(&wndClass, sizeof(wndClass)); wndClass.lpfnWndProc = hotplugNotifyWndProc; wndClass.hInstance = hHotPlug; wndClass.lpszClassName = HOTPLUG_NOTIFY_CLASS_NAME;
if (!RegisterClass(&wndClass)) {
return FALSE; } }
g_hwndNotify = CreateWindowEx(WS_EX_TOOLWINDOW, HOTPLUG_NOTIFY_CLASS_NAME, TEXT(""), WS_DLGFRAME | WS_BORDER | WS_DISABLED, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, hHotPlug, (void *)hWnd );
return(NULL != g_hwndNotify); }
BOOL InitDevTreeDlgProc( HWND hDlg, PDEVICETREE DeviceTree ) { CONFIGRET ConfigRet; HWND hwndTree; DWORD HotPlugFlags; HICON hIcon; HWND hwndParent;
DeviceTree->AllowRefresh = TRUE;
CreateNotifyWindow(hDlg);
hDevMgr = LoadLibrary(TEXT("devmgr.dll"));
if (hDevMgr) {
pDeviceProperties = (PDEVICEPROPERTIES)GetProcAddress(hDevMgr, "DevicePropertiesW"); }
hIcon = LoadIcon(hHotPlug,MAKEINTRESOURCE(IDI_HOTPLUGICON)); if (hIcon) {
SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); }
hwndParent = GetParent(hDlg);
if (hwndParent) {
SendMessage(hwndParent, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); SendMessage(hwndParent, WM_SETICON, ICON_BIG, (LPARAM)hIcon); }
DeviceTree->hDlg = hDlg; DeviceTree->hwndTree = hwndTree = GetDlgItem(hDlg, IDC_DEVICETREE);
LoadString(hHotPlug, IDS_UNKNOWN, (PTCHAR)szUnknown, SIZECHARS(szUnknown) );
//
// Disable the Stop button, until an item is selected.
//
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_STOPDEVICE), FALSE);
EnableWindow(GetDlgItem(DeviceTree->hDlg, IDC_PROPERTIES), FALSE);
//
// Get the Class Icon Image Lists
//
DeviceTree->ClassImageList.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); if (SetupDiGetClassImageList(&DeviceTree->ClassImageList)) {
TreeView_SetImageList(hwndTree, DeviceTree->ClassImageList.ImageList, TVSIL_NORMAL);
} else {
DeviceTree->ClassImageList.cbSize = 0; }
OnSysColorChange(hDlg, DeviceTree);
HotPlugFlags = GetHotPlugFlags(NULL); if (HotPlugFlags & HOTPLUG_REGFLAG_VIEWALL) {
DeviceTree->ComplexView = TRUE; CheckDlgButton(hDlg, IDC_VIEWOPTION, BST_CHECKED);
} else {
DeviceTree->ComplexView = FALSE; CheckDlgButton(hDlg, IDC_VIEWOPTION, BST_UNCHECKED); }
//
// Get the root devnode.
//
ConfigRet = CM_Locate_DevNode_Ex(&DeviceTree->DevInst, NULL, CM_LOCATE_DEVNODE_NORMAL, NULL );
if (ConfigRet != CR_SUCCESS) {
return FALSE; }
RefreshTree(DeviceTree);
if (DeviceTree->EjectDeviceInstanceId) {
DEVINST EjectDevInst; PDEVTREENODE DeviceTreeNode;
//
// we are removing a specific device, find it
// and post a message to trigger device removal.
//
ConfigRet = CM_Locate_DevNode_Ex(&EjectDevInst, DeviceTree->EjectDeviceInstanceId, CM_LOCATE_DEVNODE_NORMAL, NULL );
if (ConfigRet != CR_SUCCESS) {
return FALSE; }
DeviceTreeNode = DevTreeNodeByDevInst(EjectDevInst, &DeviceTree->ChildSiblingList );
if (!DeviceTreeNode) {
return FALSE; }
TreeView_SelectItem(hwndTree, DeviceTreeNode->hTreeItem); PostMessage(hDlg, WUM_EJECTDEVINST, 0, 0);
} else {
ShowWindow(hDlg, SW_SHOW); }
return TRUE; }
void OnContextHelp( LPHELPINFO HelpInfo, PDWORD ContextHelpIDs ) { //
// Define an array of dword pairs,
// where the first of each pair is the control ID,
// and the second is the context ID for a help topic,
// which is used in the help file.
//
if (HelpInfo->iContextType == HELPINFO_WINDOW) { // must be for a control
WinHelp((HWND)HelpInfo->hItemHandle, TEXT("hardware.hlp"), HELP_WM_HELP, (DWORD_PTR)(void *)ContextHelpIDs ); }
}
INT_PTR CALLBACK DevTreeDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { PDEVICETREE DeviceTree = NULL;
if (message == WM_INITDIALOG) { DeviceTree = (PDEVICETREE)lParam;
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)DeviceTree);
if (DeviceTree) { InitDevTreeDlgProc(hDlg, DeviceTree); } return TRUE; }
DeviceTree = (PDEVICETREE)GetWindowLongPtr(hDlg, DWLP_USER);
switch (message) { case WM_DESTROY: //
// Destroy the Notification Window
//
if (g_hwndNotify && IsWindow(g_hwndNotify)) { DestroyWindow(g_hwndNotify); g_hwndNotify = NULL; }
//
// Clear the DeviceTree
//
TreeView_DeleteAllItems(DeviceTree->hwndTree);
//
// Clean up the class image list.
//
if (DeviceTree->ClassImageList.cbSize) { SetupDiDestroyClassImageList(&DeviceTree->ClassImageList); DeviceTree->ClassImageList.cbSize = 0; }
//
// Clean up the device tree
//
ClearRemovalList(DeviceTree); RemoveChildSiblings(DeviceTree, &DeviceTree->ChildSiblingList);
if (hDevMgr) { FreeLibrary(hDevMgr); hDevMgr = NULL; pDeviceProperties = NULL; } break;
case WM_CLOSE: SendMessage(hDlg, WM_COMMAND, IDCANCEL, 0L); break;
case WM_COMMAND: { UINT Control = GET_WM_COMMAND_ID(wParam, lParam); UINT Cmd = GET_WM_COMMAND_CMD(wParam, lParam);
switch (Control) { case IDC_VIEWOPTION: if (Cmd == BN_CLICKED) { OnViewOptionClicked(hDlg, DeviceTree); } break;
case IDC_STOPDEVICE: OnRemoveDevice(hDlg, DeviceTree); break;
case IDOK: // enter -> default to expand\collapse the selected tree node
if (DeviceTree->SelectedTreeNode) { TreeView_Expand(DeviceTree->hwndTree, DeviceTree->SelectedTreeNode->hTreeItem, TVE_TOGGLE); }
break;
case IDC_PROPERTIES: if (DeviceTree->SelectedTreeNode && pDeviceProperties) { (*pDeviceProperties)(hDlg, NULL, DeviceTree->SelectedTreeNode->InstanceId, FALSE); } break;
case IDCLOSE: case IDCANCEL: EndDialog(hDlg, IDCANCEL); break; }
} break;
// Listen for Tree notifications
case WM_NOTIFY: switch (((NMHDR *)lParam)->code) { case TVN_SELCHANGED: OnTvnSelChanged(DeviceTree, (NM_TREEVIEW *)lParam); break;
case TVN_ITEMEXPANDING: OnTvnItemExpanding(hDlg, (NM_TREEVIEW *)lParam); break;
case TVN_KEYDOWN: { TV_KEYDOWN *tvKeyDown = (TV_KEYDOWN *)lParam;
if (tvKeyDown->wVKey == VK_DELETE) { OnRemoveDevice(hDlg, DeviceTree); } } break;
case NM_CUSTOMDRAW: if (IDC_DEVICETREE == ((NMHDR *)lParam)->idFrom) { SetDlgMsgResult(hDlg, WM_NOTIFY, OnCustomDraw(hDlg, DeviceTree, (NMTVCUSTOMDRAW *)lParam)); } break;
case NM_RETURN: // we don't get this in a dialog, see IDOK
break;
case NM_DBLCLK: OnRemoveDevice(hDlg, DeviceTree); SetDlgMsgResult(hDlg, WM_NOTIFY, TRUE); break;
case NM_RCLICK: OnRightClick(hDlg, DeviceTree, (NMHDR *)lParam); break;
default: return FALSE; } break;
case WUM_EJECTDEVINST: OnRemoveDevice(hDlg, DeviceTree); EndDialog(hDlg, IDCANCEL); break;
case WM_SYSCOLORCHANGE: HotPlugPropagateMessage(hDlg, message, wParam, lParam); OnSysColorChange(hDlg,DeviceTree); break;
case WM_TIMER: if (TIMERID_DEVICECHANGE == wParam) { KillTimer(hDlg, TIMERID_DEVICECHANGE); DeviceTree->RefreshEvent = TRUE;
if (DeviceTree->AllowRefresh) { OnTimerDeviceChange(DeviceTree); } } break;
case WM_SETCURSOR: if (DeviceTree->RedrawWait || DeviceTree->RefreshEvent) { SetCursor(LoadCursor(NULL, IDC_WAIT)); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1); break; } return FALSE;
case WM_CONTEXTMENU: //
// handle kbd- shift-F10, mouse rclick is invoked from NM_RCLICK
//
if ((HWND)wParam == DeviceTree->hwndTree) { OnContextMenu(hDlg, DeviceTree); break; } else { WinHelp((HWND)wParam, TEXT("hardware.hlp"), HELP_CONTEXTMENU, (DWORD_PTR)(void *)(PDWORD)UnplugtHelpIDs); } return FALSE;
case WM_HELP: OnContextHelp((LPHELPINFO)lParam, (PDWORD)UnplugtHelpIDs); break;
default: return FALSE; }
return TRUE; }
|