windows-nt/Source/XPSP1/NT/base/pnp/hotplug/init.cpp
2020-09-26 16:20:57 +08:00

880 lines
24 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 2000
//
// File: init.c
//
//--------------------------------------------------------------------------
#include "hotplug.h"
#define HOTPLUG_CLASS_NAME TEXT("HotPlugClass")
#if BUBBLES
#define SURPRISE_UNDOCK_TIMER TEXT("Local\\HotPlug_SurpriseUndockTimer_{25126bc2-1ab0-4494-8b6d-e4034cb9c24a}")
#define SURPRISE_UNDOCK_EVENT TEXT("Local\\HotPlug_SurpriseUndockEvent_{25126bc2-1ab0-4494-8b6d-e4034cb9c24a}")
#endif
void
HotPlugDeviceTree(
HWND hwndParent,
PTCHAR MachineName,
BOOLEAN HotPlugTree
)
{
CONFIGRET ConfigRet;
DEVICETREE DeviceTree;
memset(&DeviceTree, 0, sizeof(DeviceTree));
if (MachineName) {
lstrcpy(DeviceTree.MachineName, MachineName);
ConfigRet = CM_Connect_Machine(MachineName, &DeviceTree.hMachine);
if (ConfigRet != CR_SUCCESS) {
return;
}
}
DeviceTree.HotPlugTree = HotPlugTree;
InitializeListHead(&DeviceTree.ChildSiblingList);
DeviceTree.HideUI = FALSE;
DialogBoxParam(hHotPlug,
MAKEINTRESOURCE(DLG_DEVTREE),
hwndParent,
(DLGPROC)DevTreeDlgProc,
(LPARAM)&DeviceTree
);
if (DeviceTree.hMachine) {
CM_Disconnect_Machine(DeviceTree.hMachine);
}
return;
}
BOOL
HotPlugEjectDevice(
HWND hwndParent,
PTCHAR DeviceInstanceId
)
/*++
Routine Description:
Exported Entry point from hotplug.dll to eject a specific Device Instance.
Arguments:
hwndParent - Window handle of the top-level window to use for any UI related
to installing the device.
DeviceInstanceId - Supplies the ID of the device instance. This is the registry
path (relative to the Enum branch) of the device instance key.
Return Value:
BOOL TRUE for success (does not mean device was ejected or not),
FALSE unexpected error. GetLastError returns the winerror code.
--*/
{
DEVNODE DevNode;
CONFIGRET ConfigRet;
if ((ConfigRet = CM_Locate_DevNode(&DevNode,
DeviceInstanceId,
0)) == CR_SUCCESS) {
ConfigRet = CM_Request_Device_Eject_Ex(DevNode,
NULL,
NULL,
0,
0,
NULL);
}
SetLastError(ConfigRet);
return (ConfigRet == CR_SUCCESS);
}
#if UNDOCK_WARNING
DWORD
WINAPI
HotPlugSurpriseWarnW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
HANDLE hPipeRead;
HANDLE hEvent;
SURPRISE_WARN_COLLECTION surpriseWarnCollection;
MSG Msg;
WNDCLASS wndClass;
HWND hSurpriseWarnWnd;
HANDLE hHotplugIconEvent;
HANDLE hSurpriseUndockEventTimer = NULL;
HANDLE hSurpriseUndockEvent = NULL;
LARGE_INTEGER liDelayTime;
DWORD result, handleCount;
HANDLE handleArray[2];
//
// Open the specified name pipe and event.
//
if (!OpenPipeAndEventHandles(szCmd,
&hPipeRead,
&hEvent)) {
return 1;
}
ASSERT((hEvent != NULL) && (hEvent != INVALID_HANDLE_VALUE));
ASSERT((hPipeRead != NULL) && (hPipeRead != INVALID_HANDLE_VALUE));
DeviceCollectionBuildFromPipe(
hPipeRead,
CT_SURPRISE_REMOVAL_WARNING,
(PDEVICE_COLLECTION) &surpriseWarnCollection
);
surpriseWarnCollection.SuppressSurprise = FALSE;
#if BUBBLES
//
// This is how long the bubble should watch for a surprise undock (in secs)
//
surpriseWarnCollection.MaxWaitForDock = BUBBLE_SUPPRESSION_TIME;
#endif
//
// We are finished reading from the pipe, so close the handle and tell
// umpnpmgr that it can continue.
//
CloseHandle(hPipeRead);
SetEvent(hEvent);
CloseHandle(hEvent);
//
// If we have any devices then bring up the surprise removal dialog
//
if (surpriseWarnCollection.NumDevices) {
#if BUBBLES
//
// A surprise removal event occured. Make sure our waitable timer is
// up.
//
OpenGetSurpriseUndockObjects(
&hSurpriseUndockEventTimer,
&hSurpriseUndockEvent
);
#endif // BUBBLES
if (surpriseWarnCollection.DockInList) {
#if BUBBLES
//
// Tell anyone out there waiting to put up their bubble to bail
//
if (hSurpriseUndockEvent) {
PulseEvent(hSurpriseUndockEvent);
}
if (hSurpriseUndockEventTimer) {
//
// We suppress bubbles for some period of time after a surprise
// undock event.
//
liDelayTime.QuadPart = -10000000 * BUBBLE_SUPPRESSION_TIME;
SetWaitableTimer(
hSurpriseUndockEventTimer,
&liDelayTime,
0,
NULL,
NULL,
FALSE
);
}
#endif // BUBBLES
DialogBoxParam(hHotPlug,
MAKEINTRESOURCE(DLG_SURPRISEUNDOCK),
NULL,
SurpriseWarnDlgProc,
(LPARAM)&surpriseWarnCollection
);
} else {
#if BUBBLES
if (!GetClassInfo(hHotPlug, HOTPLUG_CLASS_NAME, &wndClass)) {
memset(&wndClass, 0, sizeof(wndClass));
wndClass.lpfnWndProc = SurpriseWarnBalloonProc;
wndClass.hInstance = hHotPlug;
wndClass.lpszClassName = HOTPLUG_CLASS_NAME;
if (!RegisterClass(&wndClass)) {
goto clean0;
}
}
//
// In order to prevent multiple hotplug icons on the tray and multiple surprise
// removals stepping on each other, we will create a named event that will be
// used to serialize the surprise removal UI.
//
// Note that if we can't create the event for some reason then we will just
// display the UI. This might cause multiple hotplug icons, but it is
// better than not displaying any UI at all.
//
hHotplugIconEvent = CreateEvent(NULL,
FALSE,
TRUE,
TEXT("Local\\HotPlug_TaskBarIcon_Event")
);
if (hHotplugIconEvent) {
handleArray[0] = hHotplugIconEvent;
handleArray[1] = hSurpriseUndockEvent;
handleCount = hSurpriseUndockEvent ? 2 : 1;
//
// Wait for our chance to put up a hotplug icon. Note that we
// will throw our message away if an undock happened recently.
// hSurpriseUndockEvent tells us we should throw away this
// UI attempt.
//
result = WaitForMultipleObjects(
handleCount,
handleArray,
FALSE,
INFINITE
);
if (result == (WAIT_OBJECT_0 + 1)) {
//
// A surprise undock occured, throw it back.
//
goto clean0;
} else if (hSurpriseUndockEventTimer) {
result = WaitForSingleObject(hSurpriseUndockEventTimer, 0);
if (result == WAIT_TIMEOUT) {
//
// We missed the kill event but an undock occured
// recently. Throw this one back.
//
SetEvent(hHotplugIconEvent);
CloseHandle(hHotplugIconEvent);
goto clean0;
}
}
}
//
// First disable the hotplug service so that the icon will go away from
// the taskbar. We do this just in case there are any other hotplug devices
// in the machine.
//
SysTray_EnableService(STSERVICE_HOTPLUG, FALSE);
hSurpriseWarnWnd = CreateWindowEx(WS_EX_TOOLWINDOW,
HOTPLUG_CLASS_NAME,
TEXT(""),
WS_DLGFRAME | WS_BORDER | WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
NULL,
NULL,
hHotPlug,
(LPVOID)&surpriseWarnCollection
);
if (hSurpriseWarnWnd != NULL) {
while (IsWindow(hSurpriseWarnWnd)) {
if (GetMessage(&Msg, NULL, 0, 0)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
}
//
// Set the Event so the next surprise removal process can go to work
// and then close the event handle.
//
if (hHotplugIconEvent) {
SetEvent(hHotplugIconEvent);
CloseHandle(hHotplugIconEvent);
}
//
// Re-enable the hotplug service so that the icon can show back up in
// the taskbar if we have any hotplug devices.
//
SysTray_EnableService(STSERVICE_HOTPLUG, TRUE);
#endif // BUBBLES
}
}
#if BUBBLES
clean0:
if (hSurpriseUndockEventTimer) {
CloseHandle(hSurpriseUndockEventTimer);
}
if (hSurpriseUndockEvent) {
CloseHandle(hSurpriseUndockEvent);
}
if (surpriseWarnCollection.SuppressSurprise) {
DeviceCollectionSuppressSurprise(
(PDEVICE_COLLECTION) &surpriseWarnCollection
);
}
#endif
DeviceCollectionDestroy(
(PDEVICE_COLLECTION) &surpriseWarnCollection
);
return 1;
}
#endif // UNDOCK_WARNING
DWORD
WINAPI
HotPlugRemovalVetoedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
return HandleVetoedOperation(szCmd, VETOED_REMOVAL);
}
DWORD
WINAPI
HotPlugEjectVetoedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
return HandleVetoedOperation(szCmd, VETOED_EJECT);
}
DWORD
WINAPI
HotPlugStandbyVetoedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
return HandleVetoedOperation(szCmd, VETOED_STANDBY);
}
DWORD
WINAPI
HotPlugHibernateVetoedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
return HandleVetoedOperation(szCmd, VETOED_HIBERNATE);
}
DWORD
WINAPI
HotPlugWarmEjectVetoedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
return HandleVetoedOperation(szCmd, VETOED_WARM_EJECT);
}
DWORD
WINAPI
HandleVetoedOperation(
LPWSTR szCmd,
VETOED_OPERATION VetoedOperation
)
{
HANDLE hPipeRead;
HANDLE hEvent;
PNP_VETO_TYPE vetoType;
DWORD bytesRead;
VETO_DEVICE_COLLECTION removalVetoCollection;
//
// Open the specified name pipe and event.
//
if (!OpenPipeAndEventHandles(szCmd,
&hPipeRead,
&hEvent)) {
return 1;
}
ASSERT((hEvent != NULL) && (hEvent != INVALID_HANDLE_VALUE));
ASSERT((hPipeRead != NULL) && (hPipeRead != INVALID_HANDLE_VALUE));
//
// The first DWORD is the VetoType
//
if (!ReadFile(hPipeRead,
(LPVOID)&vetoType,
sizeof(PNP_VETO_TYPE),
&bytesRead,
NULL)) {
CloseHandle(hPipeRead);
SetEvent(hEvent);
CloseHandle(hEvent);
return 1;
}
//
// Now drain all the removal strings. Note that some of them will be
// device instance paths (definitely the first)
//
DeviceCollectionBuildFromPipe(
hPipeRead,
CT_VETOED_REMOVAL_NOTIFICATION,
(PDEVICE_COLLECTION) &removalVetoCollection
);
//
// We are finished reading from the pipe, so close the handle and tell
// umpnpmgr that it can continue.
//
CloseHandle(hPipeRead);
SetEvent(hEvent);
CloseHandle(hEvent);
//
// There should always be one device as that is the device who's removal
// was vetoed.
//
ASSERT(removalVetoCollection.dc.NumDevices);
//
// Invent the VetoedOperation "VETOED_UNDOCK" from an eject containing
// another dock.
//
if (removalVetoCollection.dc.DockInList) {
if (VetoedOperation == VETOED_EJECT) {
VetoedOperation = VETOED_UNDOCK;
} else if (VetoedOperation == VETOED_WARM_EJECT) {
VetoedOperation = VETOED_WARM_UNDOCK;
}
}
removalVetoCollection.VetoType = vetoType;
removalVetoCollection.VetoedOperation = VetoedOperation;
VetoedRemovalUI(&removalVetoCollection);
DeviceCollectionDestroy(
(PDEVICE_COLLECTION) &removalVetoCollection
);
return 1;
}
DWORD
WINAPI
HotPlugSafeRemovalNotificationW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
HANDLE hPipeRead, hEvent;
DEVICE_COLLECTION safeRemovalCollection;
MSG Msg;
WNDCLASS wndClass;
HWND hSafeRemovalWnd;
HANDLE hHotplugIconEvent;
//
// Open the specified name pipe and event.
//
if (!OpenPipeAndEventHandles(szCmd,
&hPipeRead,
&hEvent)) {
return 1;
}
ASSERT((hEvent != NULL) && (hEvent != INVALID_HANDLE_VALUE));
ASSERT((hPipeRead != NULL) && (hPipeRead != INVALID_HANDLE_VALUE));
//
// Read out the device ID list from the Pipe
//
DeviceCollectionBuildFromPipe(
hPipeRead,
CT_SAFE_REMOVAL_NOTIFICATION,
&safeRemovalCollection
);
//
// On success or error, we are finished reading from the pipe, so close the
// handle and tell umpnpmgr it can continue.
//
CloseHandle(hPipeRead);
SetEvent(hEvent);
CloseHandle(hEvent);
//
// If we have any devices then bring up the safe removal dialog
//
if (safeRemovalCollection.NumDevices) {
if (!GetClassInfo(hHotPlug, HOTPLUG_CLASS_NAME, &wndClass)) {
memset(&wndClass, 0, sizeof(wndClass));
wndClass.lpfnWndProc = (safeRemovalCollection.DockInList)
? DockSafeRemovalBalloonProc
: SafeRemovalBalloonProc;
wndClass.hInstance = hHotPlug;
wndClass.lpszClassName = HOTPLUG_CLASS_NAME;
if (!RegisterClass(&wndClass)) {
goto clean0;
}
}
//
// In order to prevent multiple similar icons on the tray, we will
// create a named event that will be used to serialize the UI.
//
// Note that if we can't create the event for some reason then we will just
// display the UI. This might cause multiple icons, but it is better
// than not displaying any UI at all.
//
hHotplugIconEvent = CreateEvent(NULL,
FALSE,
TRUE,
safeRemovalCollection.DockInList
? TEXT("Local\\Dock_TaskBarIcon_Event")
: TEXT("Local\\HotPlug_TaskBarIcon_Event")
);
if (hHotplugIconEvent) {
WaitForSingleObject(hHotplugIconEvent, INFINITE);
}
if (!safeRemovalCollection.DockInList) {
//
// First disable the hotplug service so that the icon will go away from
// the taskbar. We do this just in case there are any other hotplug devices
// in the machine since we don't want multiple hotplug icons
// showing up in the taskbar.
//
// NOTE: We don't need to do this for the safe to undock case since
// the docking icon is different.
//
SysTray_EnableService(STSERVICE_HOTPLUG, FALSE);
}
hSafeRemovalWnd = CreateWindowEx(WS_EX_TOOLWINDOW,
HOTPLUG_CLASS_NAME,
TEXT(""),
WS_DLGFRAME | WS_BORDER | WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
NULL,
NULL,
hHotPlug,
(LPVOID)&safeRemovalCollection
);
if (hSafeRemovalWnd != NULL) {
while (IsWindow(hSafeRemovalWnd)) {
if (GetMessage(&Msg, NULL, 0, 0)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
}
//
// Set the Event so the next surprise removal process can go to work
// and then close the event handle.
//
if (hHotplugIconEvent) {
SetEvent(hHotplugIconEvent);
CloseHandle(hHotplugIconEvent);
}
if (!safeRemovalCollection.DockInList) {
//
// Re-enable the hotplug service so that the icon can show back up in
// the taskbar if we have any hotplug devices.
//
SysTray_EnableService(STSERVICE_HOTPLUG, TRUE);
}
}
clean0:
DeviceCollectionDestroy(&safeRemovalCollection);
return 1;
}
DWORD
WINAPI
HotPlugDriverBlockedW(
HWND hwnd,
HINSTANCE hInst,
LPWSTR szCmd,
int nShow
)
{
HANDLE hPipeRead, hEvent;
DEVICE_COLLECTION blockedDriverCollection;
HANDLE hHotplugIconEvent = NULL;
//
// Open the specified name pipe and event.
//
if (!OpenPipeAndEventHandles(szCmd,
&hPipeRead,
&hEvent)) {
return 1;
}
ASSERT((hEvent != NULL) && (hEvent != INVALID_HANDLE_VALUE));
ASSERT((hPipeRead != NULL) && (hPipeRead != INVALID_HANDLE_VALUE));
//
// Read out the list of blocked driver GUIDs from the Pipe. Note that for
// the CT_BLOCKED_DRIVER_NOTIFICATION collection type, we use only the
// DeviceInstanceId field of each collection entry (which is OK because
// MAX_GUID_STRING_LEN << MAX_DEVICE_ID_LEN). All other fields are skipped.
//
DeviceCollectionBuildFromPipe(
hPipeRead,
CT_BLOCKED_DRIVER_NOTIFICATION,
&blockedDriverCollection
);
//
// On success or error, we are finished reading from the pipe, so close the
// handle and tell umpnpmgr it can continue.
//
CloseHandle(hPipeRead);
SetEvent(hEvent);
CloseHandle(hEvent);
//
// In order to prevent multipe driver blocked icons and ballons showing up
// on the taskbar together and stepping on each other, we will create a
// named event that will be used to serialize the hotplug icons and balloon
// UI.
//
// Note that if we can't create the event for some reason then we will just
// display the UI. This might cause multiple driver blocked icons, but it
// is better than not displaying any UI at all.
//
// Also note that we can coexist with normal hotplug icon. As such we have
// a different event name and a different icon.
//
hHotplugIconEvent = CreateEvent(NULL,
FALSE,
TRUE,
TEXT("Local\\HotPlug_DriverBlockedIcon_Event")
);
if (hHotplugIconEvent) {
WaitForSingleObject(hHotplugIconEvent, INFINITE);
}
//
// Show the balloon.
//
DisplayDriverBlockBalloon(&blockedDriverCollection);
//
// Set the Event so the next blocked driver process can go to work and then
// close the event handle.
//
if (hHotplugIconEvent) {
SetEvent(hHotplugIconEvent);
CloseHandle(hHotplugIconEvent);
}
//
// Destroy the collection.
//
DeviceCollectionDestroy(&blockedDriverCollection);
return 1;
}
LONG
CPlApplet(
HWND hWnd,
WORD uMsg,
DWORD_PTR lParam1,
LRESULT lParam2
)
{
LPNEWCPLINFO lpCPlInfo;
LPCPLINFO lpOldCPlInfo;
switch (uMsg) {
case CPL_INIT:
return TRUE;
case CPL_GETCOUNT:
return 1;
case CPL_INQUIRE:
lpOldCPlInfo = (LPCPLINFO)(LPARAM)lParam2;
lpOldCPlInfo->lData = 0L;
lpOldCPlInfo->idIcon = IDI_HOTPLUGICON;
lpOldCPlInfo->idName = IDS_HOTPLUGNAME;
lpOldCPlInfo->idInfo = IDS_HOTPLUGINFO;
return TRUE;
case CPL_NEWINQUIRE:
lpCPlInfo = (LPNEWCPLINFO)(LPARAM)lParam2;
lpCPlInfo->hIcon = LoadIcon(hHotPlug, MAKEINTRESOURCE(IDI_HOTPLUGICON));
LoadString(hHotPlug, IDS_HOTPLUGNAME, lpCPlInfo->szName, sizeof(lpCPlInfo->szName));
LoadString(hHotPlug, IDS_HOTPLUGINFO, lpCPlInfo->szInfo, sizeof(lpCPlInfo->szInfo));
lpCPlInfo->dwHelpContext = IDH_HOTPLUGAPPLET;
lpCPlInfo->dwSize = sizeof(NEWCPLINFO);
lpCPlInfo->lData = 0;
lpCPlInfo->szHelpFile[0] = '\0';
return TRUE;
case CPL_DBLCLK:
HotPlugDeviceTree(hWnd, NULL, TRUE);
break;
case CPL_STARTWPARMS:
//
// what does this mean ?
//
break;
case CPL_EXIT:
// Free up any allocations of resources made.
break;
default:
break;
}
return 0L;
}
#if BUBBLES
VOID
OpenGetSurpriseUndockObjects(
OUT HANDLE *SurpriseUndockTimer,
OUT HANDLE *SurpriseUndockEvent
)
{
LARGE_INTEGER liDelayTime;
HANDLE hSurpriseUndockEventTimer;
HANDLE hSurpriseUndockEvent;
hSurpriseUndockEventTimer = CreateWaitableTimer(
NULL,
TRUE,
SURPRISE_UNDOCK_TIMER
);
if ((hSurpriseUndockEventTimer != NULL) &&
(GetLastError() == ERROR_SUCCESS)) {
//
// We created it (if not the status would be ERROR_ALREADY_EXISTS).
// Ensure it starts life signalled.
//
liDelayTime.QuadPart = 0;
SetWaitableTimer(
hSurpriseUndockEventTimer,
&liDelayTime,
0,
NULL,
NULL,
FALSE
);
}
hSurpriseUndockEvent = CreateEvent(
NULL,
TRUE,
FALSE,
SURPRISE_UNDOCK_EVENT
);
*SurpriseUndockTimer = hSurpriseUndockEventTimer;
*SurpriseUndockEvent = hSurpriseUndockEvent;
}
#endif // BUBBLES