概述: PnP 相关代码汇总

1. 弹出U盘

1.1. EjectVolume 弹出U盘

STDAPI_(BOOL) EjectVolume(DWORD dwVolLetter)
{
	MYTRACE(L"Enter EjectVolume\n");
 
	DWORD_PTR lrRet = 0;
	HWND hWnd = FindWindow(WNDCLASS_CACHEWND, NULL);
	LRESULT lr = 0;
	if (NULL != hWnd)
	{
		lr = SendMessageTimeout(hWnd, WM_DEREGISTER_UDISK, dwVolLetter, 0, SMTO_BLOCK, 3000, &lrRet);
	}
 
	WCHAR wszVolShort[20] = {};
	WCHAR szParentInstanceId[MAX_DEVICE_ID_LEN] = {};
	VOLUMECOLLECTION VolCll = {};
	BOOL bRet = FALSE;
	BOOL bUsed = FALSE;
	HANDLE hVolume = INVALID_HANDLE_VALUE;
	CONFIGRET conRet = 0;
	DEVINST dnDevInst = NULL;
 
	EnableXXXPrivilege(SE_LOAD_DRIVER_NAME);
	EnableXXXPrivilege(SE_UNDOCK_NAME);
 
	bRet = GetVolumeCollection((BYTE)dwVolLetter, &VolCll, szParentInstanceId);
	if (!bRet)
	{
		MYTRACE(L"Get volume instance id error!\n");
		return FALSE;
	}
 
	StringCchPrintf(wszVolShort, ARRAYSIZE(wszVolShort), L"%c:\\", dwVolLetter);
	if (!PathFileExists(wszVolShort))
	{
		MYTRACE(L"Not Find Volume:%c, Delete letter\n", dwVolLetter);
		return TRUE;
	}
 
	// 不要直接尝试弹出
	// 先判断是否占用
	DWORD volumeFlags = GENERIC_READ | GENERIC_WRITE;
	hVolume = CreateVolumeHandleFromDriveLetter((WCHAR)dwVolLetter,volumeFlags);
	if (INVALID_HANDLE_VALUE == hVolume)
	{
		MYTRACE(L"ERROR! Can not open volume! Maybe it's locked\n");
		return FALSE;
	}
	else
	{
		CloseHandle(hVolume);
		hVolume = INVALID_HANDLE_VALUE;
	}
 
	PNP_VETO_TYPE VetoType;
	bRet = CMRequestRemoveDev(dwVolLetter, szParentInstanceId, VetoType);
 
	if (!bRet)
	{
		switch (VetoType)
		{
		case PNP_VetoTypeUnknown:
			MYTRACE(L"[Eject] for unspecified reason");
			break;
 
		case PNP_VetoLegacyDevice:
			MYTRACE(L"[Eject] due to legacy device");
			break;
 
		case PNP_VetoPendingClose:
			MYTRACE(L"[Eject] due to pending close");
			break;
 
		case PNP_VetoWindowsApp:
			MYTRACE(L"[Eject] due to windows application");
			break;
 
		case PNP_VetoWindowsService:
			MYTRACE(L"[Eject] due to service");
			break;
 
		case PNP_VetoOutstandingOpen:
			MYTRACE(L"[Eject] due to outstanding handles on device");
			break;
 
		case PNP_VetoDevice:
			MYTRACE(L"[Eject] by device");
			break;
 
		case PNP_VetoDriver:
			MYTRACE(L"[Eject] by driver");
			break;
 
		case PNP_VetoIllegalDeviceRequest:
		{
			MYTRACE(L"[Eject] as the request was invalid for the device");
 
			if (EjectVolumeForce(dwVolLetter))
			{
				CMRequestRemoveDev(dwVolLetter, szParentInstanceId, VetoType);
				bRet = TRUE;
			}
		}
		break;
 
		case PNP_VetoInsufficientPower:
			MYTRACE(L"[Eject] because there would be insufficient system power to continue");
			break;
 
		case PNP_VetoNonDisableable:
			MYTRACE(L"[Eject] due to non-disableable device");
			break;
 
		case PNP_VetoLegacyDriver:
			MYTRACE(L"[Eject] due to legacy driver");
			break;
 
		case PNP_VetoInsufficientRights:
			MYTRACE(L"[Eject] insufficient permissions");
			break;
 
		default:
			MYTRACE(L"[Eject] due to uncoded reason");
			break;
		}
	}
	return bRet;
}

1.2. GetVolumeCollection 获取设备ID

STDAPI_(BOOL) GetVolumeCollection(BYTE byVolLetter, PVOLUMECOLLECTION pVolCll, LPWSTR pParentInstId)
{
	WCHAR szParentInstanceId[MAX_DEVICE_ID_LEN] = {}; 
	WCHAR szTmpInstanceId[MAX_DEVICE_ID_LEN] ={};
	WCHAR szDriveLetters[26*2*4] ={};
	BOOL bRet;
	DWORD cbBytesReturned; 
	LPWSTR pszDriveRoot = NULL;
	size_t uStrlen;
 
	pVolCll->dwCount = 0;
 
	bRet = GetParentInstanceId(byVolLetter, szParentInstanceId, NULL, 0);
	if (!bRet)
		return FALSE;                                            pszLogicalDrives); 
 
	cbBytesReturned = GetLogicalDriveStrings (ARRAYSIZE(szDriveLetters)*sizeof(WCHAR), 
                                                  szDriveLetters); 
 
	for (pszDriveRoot = szDriveLetters; *pszDriveRoot != TEXT('\0'); 
             pszDriveRoot += uStrlen + 1)
	{
		StringCchLength(pszDriveRoot, 26*2*4, &uStrlen);
		if (((BYTE)(*pszDriveRoot) == 'A') || ((BYTE)(*pszDriveRoot) == 'a') 
			|| ((BYTE)(*pszDriveRoot) == 'B') || ((BYTE)(*pszDriveRoot) == 'b'))   //skip floppy and the target volume
			continue;
 
		bRet = GetParentInstanceId((BYTE)(*pszDriveRoot), szTmpInstanceId, NULL, 0);
		if (bRet && (StrCmpI(szParentInstanceId,szTmpInstanceId) == 0))
		{
			pVolCll->byVolume[pVolCll->dwCount] = (BYTE)(*pszDriveRoot);
			pVolCll->dwCount++;
		}
 
	}
	
	if (pParentInstId)
		StringCchCopy(pParentInstId, MAX_DEVICE_ID_LEN, szParentInstanceId);
 
	return TRUE;
}
 
BOOL GetParentInstanceId(BYTE byVolLetter,LPWSTR pParentInstId, LPWSTR pParentDevName,DWORD dwBufSize)
{
	HANDLE hDevice = INVALID_HANDLE_VALUE;  
	WCHAR szDeviceName[8]; 
	STORAGE_PROPERTY_QUERY spq; 
	STORAGE_DEVICE_NUMBER sdn; 
	STORAGE_DEVICE_NUMBER sdnTmp; 
	BOOL bSuccess;
	DWORD cbBytesReturned; 
	BYTE byBuffer[4096]; 
	BOOL bRet = FALSE;
	GUID *pGuidInferface = NULL;
//	GUID *pGuidClass = NULL; 
	HDEVINFO hIntDevInfo = INVALID_HANDLE_VALUE; 
	DWORD dwIndex;
	SP_DEVICE_INTERFACE_DATA interfaceData; 
	SP_DEVINFO_DATA deviceInfoData;
	CONFIGRET configret; 
	DEVINST dnDevInstParent;
	WCHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN]={};
    HANDLE hDev = INVALID_HANDLE_VALUE; 
    PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL; 
 
	StringCchPrintf(szDeviceName, ARRAYSIZE(szDeviceName), L"\\\\.\\%c:", byVolLetter);
	hDevice = CreateFile (szDeviceName,             
							0, 
							FILE_SHARE_READ | FILE_SHARE_WRITE,  
							NULL,
							OPEN_EXISTING, 
							0, 
							NULL); 
	if (hDevice == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
 
	spq.PropertyId = StorageDeviceProperty; 
	spq.QueryType = PropertyStandardQuery; 
	spq.AdditionalParameters[0] = 0; 
	bSuccess = DeviceIoControl (hDevice,  
								IOCTL_STORAGE_QUERY_PROPERTY,     
								&spq, sizeof(spq),              
								&byBuffer, sizeof(byBuffer),     
								&cbBytesReturned,                
								(LPOVERLAPPED) NULL);   
	 STORAGE_DEVICE_DESCRIPTOR *psdp = (STORAGE_DEVICE_DESCRIPTOR *)byBuffer; 
	if (!bSuccess || psdp->BusType != BusTypeUsb)     //Not USB device, just return
	{
		CloseHandle (hDevice); 
		return FALSE;
	}
 
	bSuccess = DeviceIoControl (hDevice,                         
								IOCTL_STORAGE_GET_DEVICE_NUMBER,  
								NULL, 0,                         
								(LPVOID)&sdn, sizeof(sdn),      
								&cbBytesReturned,                
								(LPOVERLAPPED) NULL);  
	// GetLastError of ERROR_MORE_DATA indicates to the caller that the buffer was not large enough to accommodate the data requested 
	if (!bSuccess)
	{
		CloseHandle (hDevice); 
		return FALSE;
	}
 
	if (sdn.DeviceType == FILE_DEVICE_CD_ROM || sdn.DeviceType == FILE_DEVICE_DVD) 
	{ 
		pGuidInferface = (GUID*)&GUID_DEVINTERFACE_CDROM; 
	//	pGuidClass = (GUID*)&GUID_DEVCLASS_CDROM; 
	} 
	else //if (sdn.DeviceType == FILE_DEVICE_DISK) 
	{ 
		pGuidInferface = (GUID*)&GUID_DEVINTERFACE_DISK; 
//		pGuidClass = (GUID*)&GUID_DEVCLASS_DISKDRIVE; 
	}
 
	CloseHandle (hDevice); 
    hDevice = INVALID_HANDLE_VALUE; 
 
	hIntDevInfo = SetupDiGetClassDevs (pGuidInferface, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 
	if (INVALID_HANDLE_VALUE == hIntDevInfo)
		return FALSE;
 
	for (dwIndex = 0; ;dwIndex ++) 
	{ 
		ZeroMemory(&interfaceData, sizeof(interfaceData)); 
        interfaceData.cbSize = sizeof(interfaceData); 
        bSuccess = SetupDiEnumDeviceInterfaces (hIntDevInfo, NULL, pGuidInferface, dwIndex, &interfaceData); 
        if (!bSuccess) { 
            DWORD dwErrorCode = GetLastError(); 
            if (dwErrorCode == ERROR_NO_MORE_ITEMS) 
                break; 
            //else  
            //    break;  // ERROR!!! 
        } 
 
		cbBytesReturned = 0; 
		bSuccess = SetupDiGetDeviceInterfaceDetail (hIntDevInfo, &interfaceData, NULL, 0, &cbBytesReturned, NULL); 
		if ((!bSuccess && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
			 || cbBytesReturned == 0) 
			continue;  // ERROR!!! 
 
		if (pInterfaceDetailData) 
			pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalFree (pInterfaceDetailData); 
 
		pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc (LPTR, cbBytesReturned); 
		pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 
		ZeroMemory (&deviceInfoData, sizeof(deviceInfoData)); 
		deviceInfoData.cbSize = sizeof(deviceInfoData); 
		bSuccess = SetupDiGetDeviceInterfaceDetail (hIntDevInfo, &interfaceData, 
													pInterfaceDetailData, cbBytesReturned, &cbBytesReturned, &deviceInfoData); 
		if (!bSuccess) 
			continue; 
 
		hDev = CreateFile (pInterfaceDetailData->DevicePath,  
							0,                                   
							FILE_SHARE_READ | FILE_SHARE_WRITE,  
							NULL,                                
							OPEN_EXISTING,                       
							0,                                  
							NULL);   
		if (hDev == INVALID_HANDLE_VALUE)
			continue;  //ERROR
 
        bSuccess = DeviceIoControl (hDev,                           
                                    IOCTL_STORAGE_GET_DEVICE_NUMBER,  
                                    NULL, 0,                        
                                    (LPVOID)&sdnTmp, sizeof(sdnTmp),     
                                    &cbBytesReturned,               
                                    (LPOVERLAPPED) NULL);    
		CloseHandle(hDev);
		if (!bSuccess)
			continue;
 
		if ((sdn.DeviceType != sdnTmp.DeviceType) || (sdn.DeviceNumber != sdnTmp.DeviceNumber))
		{
			continue;
		}
 
		//found the device
			
//		MYTRACE(L"pInterfaceDetailData->DevicePath: %s", pInterfaceDetailData->DevicePath);
 
		configret = CM_Get_Parent (&dnDevInstParent, deviceInfoData.DevInst, 0); 
		if (CR_SUCCESS != configret)
			break;
		configret = CM_Get_Device_ID (dnDevInstParent, szDeviceInstanceID, ARRAYSIZE(szDeviceInstanceID), 0); 
		if (CR_SUCCESS != configret)
			break;
 
		if (pParentInstId)
			StringCchCopy(pParentInstId, MAX_DEVICE_ID_LEN, szDeviceInstanceID);
		if (pParentDevName && dwBufSize > 8)
		{
			LPWSTR pDevName = pInterfaceDetailData->DevicePath;
			LPWSTR pfind = StrStrI(pDevName, L"ven_");
			if (pfind)
			{	
				pDevName = pfind + 4;
				pfind = StrChr(pDevName, '&');
				StringCbCopyN(pParentDevName, dwBufSize, pDevName, (pfind-pDevName)*sizeof(WCHAR));	
				StringCbCat(pParentDevName, dwBufSize, L" ");
				pDevName =  pInterfaceDetailData->DevicePath;
			}
			pfind = StrStrI(pDevName, L"prod_");
			if (pfind)
			{	
				pDevName = pfind + 5;
				pfind = StrChr(pDevName, '&');
				StringCbCatN(pParentDevName, dwBufSize, pDevName, (pfind-pDevName)*sizeof(WCHAR));
			}
			//replace '_' with ' '
			pDevName = pParentDevName;
			do
			{
				pfind = StrChr(pDevName, '_');
				if (!pfind)
					break;
				*pfind = ' ';
				pDevName = pfind +1;			
			}while(1);
 
			wcsupr(pParentDevName);
			//_wcsupr_s(pParentDevName, wcslen());
		}
 
		bRet = TRUE;  //Success
		break;   
	}
 
	if (pInterfaceDetailData)
		LocalFree( pInterfaceDetailData);
	if (hIntDevInfo )
		SetupDiDestroyDeviceInfoList(hIntDevInfo);
 
	return bRet;
}

1.3. CMRequestRemoveDev 移除设备

BOOL CMRequestRemoveDev(DWORD dwVolLetter, WCHAR* szParentInstanceId, PNP_VETO_TYPE& VetoType)
{
	DEVINST dnDevInst = NULL;
	DEVINST parentDevInst = NULL;
	VOLUMECOLLECTION VolCll = {};
	CONFIGRET conRet = 0;
 
	conRet = CM_Locate_DevNode(&dnDevInst, szParentInstanceId, NULL);
 
	if (conRet == CR_SUCCESS)
	{
		WCHAR szVetoName[MAX_PATH] = { 0 };
 
		MYTRACE(L"CM_Request_Device_Eject_Ex");
		conRet = CM_Request_Device_Eject_Ex(dnDevInst, &VetoType, szVetoName, MAX_PATH, 0, NULL);
 
		MYTRACE(L"CM_Request_Device_Eject_Ex ! rt:%d, VetoType:%d, info:%s", conRet, VetoType, szVetoName);
		if (conRet == CR_SUCCESS)
		{
			MYTRACE(L"CM_Request_Device_Eject_Ex  SUCCESS!\n");
			return TRUE;
		}
		else
		{
			if (conRet == CR_REMOVE_VETOED && VetoType == PNP_VetoIllegalDeviceRequest)
			{
				conRet = CM_Query_And_Remove_SubTree(dnDevInst,
					&VetoType,
					szVetoName,
					MAX_PATH,
 
					// We have to add the `CM_REMOVE_NO_RESTART` flag because
					// otherwise the just-removed device may be immediately
					// redetected, which might happen on XP and Vista.
					// See https://www.codeproject.com/articles/13839/how-to-prepare-a-usb-drive-for-safe-removal
 
					CM_REMOVE_NO_RESTART);
				if (conRet == CR_SUCCESS)
				{
					return TRUE;
				}
			}
 
			MYTRACE(L"CM_Request_Device_Eject_Ex FAILED! rt:%d, VetoType:%d, info:%s", conRet, VetoType, szVetoName);
		}
	}
 
	MYTRACE(L"CM_Locate_DevNode eject %d volume(s) error!\n", VolCll.dwCount);
 
	return FALSE;
}

2. 强制弹出U盘

BOOL LockVolume(HANDLE volume) 
{
	DWORD bytesReturned;
 
	for (size_t tries = 0; tries < 20; tries++) 
	{
		if (DeviceIoControl(volume,
			FSCTL_LOCK_VOLUME,
			NULL, 0,
			NULL, 0,
			&bytesReturned,
			NULL)) {
			return TRUE;
		}
 
		Sleep(500);
	}
 
	return FALSE;
}
 
BOOL UnlockVolume(HANDLE volume)
{
	DWORD bytesReturned;
 
	return DeviceIoControl(volume,
		FSCTL_UNLOCK_VOLUME,
		NULL, 0,
		NULL, 0,
		&bytesReturned,
		NULL);
}
 
STDAPI_(BOOL) EjectVolumeForce(DWORD dwVolLetter)
{
	BOOL bRet = FALSE;
	WCHAR  wszVolShort[20] = {};
	DWORD dwReturn = 0;
	HANDLE hVolume = INVALID_HANDLE_VALUE;
 
	StringCchPrintf(wszVolShort, ARRAYSIZE(wszVolShort), L"%c:\\", dwVolLetter);
 
	DWORD volumeFlags = GENERIC_READ | GENERIC_WRITE;
	hVolume = CreateVolumeHandleFromDriveLetter(dwVolLetter, volumeFlags);
	if (hVolume == INVALID_HANDLE_VALUE)
	{
		MYTRACE(L"ERROR! Can not open volume!\n");
		return FALSE;
	}
 
	do
	{
		if (DRIVE_FIXED == GetDriveType(wszVolShort))
		{
			MYTRACE(L"DRIVE_FIXED == GetDriveType");
 
			ULONG deviceNumber = GetDeviceNumberFromVolumeHandle(hVolume);
			if (!deviceNumber) 
			{
				MYTRACE(L"Couldn't get device number from volume handle");
				bRet = FALSE;
				break;
			}
 
			if (!CloseHandle(hVolume)) 
			{
				MYTRACE(L"Couldn't close volume handle");
				break;
			}
 
			MYTRACE(L"Ejecting fixed drive");
			bRet = EjectFixedDriveByDeviceNumber(deviceNumber);
 
			break;
		}
		
		if (!LockVolume(hVolume))
		{
			MYTRACE(L"LockVolume Failed");
			break;
		}
 
		// 开始暴力弹出
		if (DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwReturn, NULL))
		{
			DWORD dwTmp = 0;
			MYTRACE(L"IOCTL_STORAGE_MEDIA_REMOVAL");
			bRet = DeviceIoControl(hVolume, IOCTL_STORAGE_MEDIA_REMOVAL, &dwTmp, sizeof(dwTmp), NULL, 0, &dwReturn, NULL);
			MYTRACE(L"IOCTL_STORAGE_MEDIA_REMOVAL, bRet==%d", bRet);
			bRet = DeviceIoControl(hVolume, IOCTL_DISK_EJECT_MEDIA, NULL, 0, NULL, 0, &dwReturn, NULL);
			MYTRACE(L"IOCTL_DISK_EJECT_MEDIA bRet==%d", bRet);
			bRet = DeviceIoControl(hVolume, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwReturn, NULL);
			MYTRACE(L"IOCTL_STORAGE_EJECT_MEDIA bRet==%d", bRet);
 
		}
 
		if (!UnlockVolume(hVolume))
		{
			MYTRACE(L"UnlockVolume Failed");
			break;
		}
 
		// 弹出是否成功
		if (!PathFileExists(wszVolShort))
		{
			DeleteJunctionPoint(dwVolLetter);
			bRet = TRUE;
			break;
		}
		else
		{
			bRet = FALSE;
			break;
		}
	} while (0);
	
	if (INVALID_HANDLE_VALUE != hVolume)
	{
		CloseHandle(hVolume);
		hVolume = INVALID_HANDLE_VALUE;
	}
 
	// 避免不生效,多调用几个
	// RefreshFolderViews(CSIDL_DESKTOP, SHCNE_UPDATEDIR);
	NotifyDeviceRemoval(dwVolLetter);
	SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATH, wszVolShort, NULL);
	SHChangeNotify(SHCNE_DRIVEADD, SHCNF_PATH, wszVolShort, NULL);
 
	return bRet;
}

3. 弹出硬盘

// Adapted from https://www.codeproject.com/articles/13839/how-to-prepare-a-usb-drive-for-safe-removal
// which is licensed under "The Code Project Open License (CPOL) 1.02"
// https://www.codeproject.com/info/cpol10.aspx
DEVINST GetDeviceInstanceFromDeviceNumber(ULONG deviceNumber) 
{
	const GUID* guid = reinterpret_cast<const GUID*>(&GUID_DEVINTERFACE_DISK);
 
	// Get device interface info set handle for all devices attached to system
	DWORD deviceInformationFlags = DIGCF_PRESENT | DIGCF_DEVICEINTERFACE;
	HDEVINFO deviceInformation = SetupDiGetClassDevs(guid,
		NULL, NULL,
		deviceInformationFlags);
 
	if (deviceInformation == INVALID_HANDLE_VALUE) 
	{
		return 0;
	}
 
	DWORD memberIndex = 0;
	BYTE buffer[1024];
 
	PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData =
		(PSP_DEVICE_INTERFACE_DETAIL_DATA)buffer;
 
	SP_DEVINFO_DATA deviceInformationData;
	DWORD requiredSize;
 
	SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
	deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
 
	while (true)
	{
		if (!SetupDiEnumDeviceInterfaces(deviceInformation,
			NULL,
			guid,
			memberIndex,
			&deviceInterfaceData)) 
		{
			break;
		}
 
		requiredSize = 0;
		SetupDiGetDeviceInterfaceDetail(deviceInformation,
			&deviceInterfaceData,
			NULL, 0,
			&requiredSize, NULL);
 
		if (requiredSize == 0 || requiredSize > sizeof(buffer)) 
		{
			memberIndex++;
			continue;
		}
 
		deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
 
		ZeroMemory((PVOID)&deviceInformationData, sizeof(SP_DEVINFO_DATA));
		deviceInformationData.cbSize = sizeof(SP_DEVINFO_DATA);
 
		BOOL result = SetupDiGetDeviceInterfaceDetail(deviceInformation,
			&deviceInterfaceData,
			deviceInterfaceDetailData,
			sizeof(buffer),
			&requiredSize,
			&deviceInformationData);
 
		if (!result) {
			memberIndex++;
			continue;
		}
 
		LPCTSTR devicePath = deviceInterfaceDetailData->DevicePath;
		HANDLE driveHandle = CreateVolumeHandleFromDevicePath(devicePath, 0);
 
		if (driveHandle == INVALID_HANDLE_VALUE) {
			memberIndex++;
			continue;
		}
 
		ULONG currentDriveDeviceNumber =
			GetDeviceNumberFromVolumeHandle(driveHandle);
 
		CloseHandle(driveHandle);
 
		if (!currentDriveDeviceNumber) {
			memberIndex++;
			continue;
		}
 
		if (deviceNumber == currentDriveDeviceNumber) {
			SetupDiDestroyDeviceInfoList(deviceInformation);
			return deviceInformationData.DevInst;
		}
 
		memberIndex++;
	}
 
	SetupDiDestroyDeviceInfoList(deviceInformation);
 
	return 0;
}
 
BOOL EjectFixedDriveByDeviceNumber(ULONG deviceNumber) 
{
	DEVINST deviceInstance = GetDeviceInstanceFromDeviceNumber(deviceNumber);
	if (!deviceInstance) 
	{
		MYTRACE(L"Couldn't get instance from device number");
		return FALSE;
	}
 
	CONFIGRET status;
	PNP_VETO_TYPE vetoType = PNP_VetoTypeUnknown;
	WCHAR szVetoName[MAX_PATH];
 
	// It's often seen that the removal fails on the first
	// attempt but works on the second attempt.
	// See https://www.codeproject.com/articles/13839/how-to-prepare-a-usb-drive-for-safe-removal
	for (size_t tries = 0; tries < 3; tries++) 
	{
		if (tries != 0) {
			MYTRACE(L"Retrying");
			Sleep(500);
		}
		MYTRACE(L"Ejecting device instance");
		status = CM_Request_Device_Eject(deviceInstance,
			&vetoType,
			szVetoName,
			MAX_PATH,
			0);
 
		if (status == CR_SUCCESS) 
		{
			MYTRACE(L"Ejected device instance successfully");
			return TRUE;
		}
 
		MYTRACE(L"Ejecting was vetoed");
 
		// We use this as an indicator that the device driver
		// is not setting the `SurpriseRemovalOK` capability.
		// See https://msdn.microsoft.com/en-us/library/windows/hardware/ff539722(v=vs.85).aspx
		if (status == CR_REMOVE_VETOED &&
			vetoType == PNP_VetoIllegalDeviceRequest) 
		{
			MYTRACE(L"Removing subtree");
			status = CM_Query_And_Remove_SubTree(deviceInstance,
				&vetoType,
				szVetoName,
				MAX_PATH,
				CM_REMOVE_NO_RESTART);
 
			if (status == CR_ACCESS_DENIED) {
				return FALSE;
			}
 
			if (status == CR_SUCCESS) {
				return TRUE;
			}
 
			MYTRACE(L"Couldn't eject device instance");
			return FALSE;
		}
	}
 
	return FALSE;
}

4. 获取设备数

bool GetDeviceNumber(HANDLE deviceHandle, STORAGE_DEVICE_NUMBER& deviceNumber) {
	DWORD bytesReturned = 0;
	BOOL result = DeviceIoControl(
		deviceHandle,
		IOCTL_STORAGE_GET_DEVICE_NUMBER,
		NULL,
		0,
		&deviceNumber,
		sizeof(STORAGE_DEVICE_NUMBER),
		&bytesReturned,
		NULL
	);
 
	return result != 0;
}

5. 获取设备ID

 
bool GetDeviceInstanceId(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& deviceInfoData, std::wstring& deviceInstanceId) {
	WCHAR buffer[1024];
	DWORD requiredSize = 0;
 
	BOOL result = SetupDiGetDeviceInstanceIdW(
		deviceInfoSet,
		&deviceInfoData,
		buffer,
		sizeof(buffer) / sizeof(buffer[0]),
		&requiredSize
	);
 
	if (result) {
		deviceInstanceId = buffer;
	}
 
	return result != 0;
}