//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation // // File: init.cpp // //-------------------------------------------------------------------------- #include "hotplug.h" #define HOTPLUG_CLASS_NAME TEXT("HotPlugClass") VOID HotPlugDeviceTree( HWND hwndParent, BOOLEAN HotPlugTree ) { CONFIGRET ConfigRet; DEVICETREE DeviceTree; ZeroMemory(&DeviceTree, sizeof(DeviceTree)); DeviceTree.HotPlugTree = HotPlugTree; InitializeListHead(&DeviceTree.ChildSiblingList); DialogBoxParam(hHotPlug, MAKEINTRESOURCE(DLG_DEVTREE), hwndParent, DevTreeDlgProc, (LPARAM)&DeviceTree ); return; } DWORD WINAPI HotPlugRemovalVetoedW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); return HandleVetoedOperation(szCmd, VETOED_REMOVAL); } DWORD WINAPI HotPlugEjectVetoedW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); return HandleVetoedOperation(szCmd, VETOED_EJECT); } DWORD WINAPI HotPlugStandbyVetoedW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); return HandleVetoedOperation(szCmd, VETOED_STANDBY); } DWORD WINAPI HotPlugHibernateVetoedW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); return HandleVetoedOperation(szCmd, VETOED_HIBERNATE); } DWORD WINAPI HotPlugWarmEjectVetoedW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(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; UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); // // 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)) { ZeroMemory(&wndClass, 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 hDriverBlockIconEvent = NULL; HANDLE hDriverBlockEvent = NULL; TCHAR szEventName[MAX_PATH]; UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); // // Open the specified name pipe and event. // if (OpenPipeAndEventHandles(szCmd, &hPipeRead, &hEvent) == FALSE) { 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); // // Since the balloons can hang around for numerous seconds, or longer if // there is no user input to the system, we need to make sure that we only // have one driver block event queued for any given type of driver block. // This way if there is an attempt to load a driver many times in a row, we // won't queue up numerous driver blocked ballons for the same driver. // We will do this by creating a local event that includes the GUID of the // blocked driver or no GUID if we are showing the generic balloon. // if (SUCCEEDED(StringCchPrintf(szEventName, SIZECHARS(szEventName), TEXT("Local\\DRIVERBLOCK-%s"), (blockedDriverCollection.NumDevices == 1) ? DeviceCollectionGetDeviceInstancePath(&blockedDriverCollection, 0) : TEXT("ALL") ))) { hDriverBlockEvent = CreateEvent(NULL, FALSE, TRUE, szEventName ); if (hDriverBlockEvent) { if (WaitForSingleObject(hDriverBlockEvent, 0) != WAIT_OBJECT_0) { // // This means that this driver block balloon is either being // displayed, or in the queue to be displayed, so we can just // exit this process. // goto clean0; } } } // // 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 driver blocked 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. // hDriverBlockIconEvent = CreateEvent(NULL, FALSE, TRUE, TEXT("Local\\HotPlug_DriverBlockedIcon_Event") ); if (hDriverBlockIconEvent) { for (;;) { DWORD waitStatus; waitStatus = MsgWaitForMultipleObjects(1, &hDriverBlockIconEvent, FALSE, INFINITE, QS_ALLINPUT); if (waitStatus == WAIT_OBJECT_0) { // // The current driver block icon went away so it is our turn. // break; } else if (waitStatus == (WAIT_OBJECT_0 + 1)) { // // Message in the queue. // MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_CLOSE) { goto clean0; } TranslateMessage(&msg); DispatchMessage(&msg); } } else { // // This shouldn't happen. // goto clean0; } } } // // Show the balloon. // DisplayDriverBlockBalloon(&blockedDriverCollection); // // Since the balloon has now gone away, set the event so if we get another // block on the same driver it will display another balloon. // if (hDriverBlockEvent) { SetEvent(hDriverBlockEvent); } clean0: // // Set the Event so the next blocked driver process can go to work and then // close the event handle. // if (hDriverBlockIconEvent) { SetEvent(hDriverBlockIconEvent); CloseHandle(hDriverBlockIconEvent); } if (hDriverBlockEvent) { CloseHandle(hDriverBlockEvent); } // // Destroy the collection. // DeviceCollectionDestroy(&blockedDriverCollection); return 1; } DWORD WINAPI HotPlugChildWithInvalidIdW( HWND hwnd, HINSTANCE hInst, LPWSTR szCmd, int nShow ) { HANDLE hPipeRead, hEvent; DEVICE_COLLECTION childWithInvalidIdCollection; HANDLE hChildWithInvalidIdIconEvent = NULL; HANDLE hChildWithInvalidIdEvent = NULL; TCHAR szEventName[MAX_PATH]; UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(nShow); // // 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 instance Id of the parent that has a child device // with an invalid Id. Note, if we are passed multiple device instance // Ids, we will only display UI for the first one in the list. // DeviceCollectionBuildFromPipe( hPipeRead, CT_CHILD_WITH_INVALID_ID_NOTIFICATION, &childWithInvalidIdCollection ); // // 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); // // Since the balloons can hang around for numerous seconds, or longer if // there is no user input to the system, we need to make sure that we only // have one invalid child event queued for any given parent with an invalid // child. // StringCchPrintf(szEventName, SIZECHARS(szEventName), TEXT("Local\\CHILDWITHINVALIDID-%s"), DeviceCollectionGetDeviceInstancePath(&childWithInvalidIdCollection, 0) ); hChildWithInvalidIdEvent = CreateEvent(NULL, FALSE, TRUE, szEventName ); if (hChildWithInvalidIdEvent) { if (WaitForSingleObject(hChildWithInvalidIdEvent, 0) != WAIT_OBJECT_0) { // // This means that this invalid child balloon is either being // displayed, or in the queue to be displayed, so we can just // exit this process. // goto clean0; } } // // In order to prevent multipe invalid child 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 invalid child 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 invalid child 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. // hChildWithInvalidIdIconEvent = CreateEvent(NULL, FALSE, TRUE, TEXT("Local\\HotPlug_ChildWithInvalidId_Event") ); if (hChildWithInvalidIdIconEvent) { for (;;) { DWORD waitStatus; waitStatus = MsgWaitForMultipleObjects(1, &hChildWithInvalidIdIconEvent, FALSE, INFINITE, QS_ALLINPUT); if (waitStatus == WAIT_OBJECT_0) { // // The current invalid child icon went away so it is our turn. // break; } else if (waitStatus == (WAIT_OBJECT_0 + 1)) { // // Message in the queue. // MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_CLOSE) { goto clean0; } TranslateMessage(&msg); DispatchMessage(&msg); } } else { // // This shouldn't happen. // goto clean0; } } } // // Show the balloon. // DisplayChildWithInvalidIdBalloon(&childWithInvalidIdCollection); // // Since the balloon has now gone away, set the event so if we get another // invalid child device it will display another balloon. // if (hChildWithInvalidIdEvent) { SetEvent(hChildWithInvalidIdEvent); } clean0: // // Set the Event so the next invalid child process can go to work and then // close the event handle. // if (hChildWithInvalidIdIconEvent) { SetEvent(hChildWithInvalidIdIconEvent); CloseHandle(hChildWithInvalidIdIconEvent); } if (hChildWithInvalidIdEvent) { CloseHandle(hChildWithInvalidIdEvent); } // // Destroy the collection. // DeviceCollectionDestroy(&childWithInvalidIdCollection); return 1; } LONG CPlApplet( HWND hWnd, WORD uMsg, DWORD_PTR lParam1, LRESULT lParam2 ) { LPNEWCPLINFO lpCPlInfo; LPCPLINFO lpOldCPlInfo; UNREFERENCED_PARAMETER(lParam1); 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, SIZECHARS(lpCPlInfo->szName)); LoadString(hHotPlug, IDS_HOTPLUGINFO, lpCPlInfo->szInfo, SIZECHARS(lpCPlInfo->szInfo)); lpCPlInfo->dwHelpContext = IDH_HOTPLUGAPPLET; lpCPlInfo->dwSize = sizeof(NEWCPLINFO); lpCPlInfo->lData = 0; lpCPlInfo->szHelpFile[0] = '\0'; return TRUE; case CPL_DBLCLK: HotPlugDeviceTree(hWnd, TRUE); break; default: break; } return 0L; }