Windows下两种方法弹出U盘

1. 通过设置IOCTL_STORAGE_EJECT_MEDIA

  • 通过CreateFile打开指定盘符的U盘获取设备句柄hDevice
  • 通过DeviceIoControl(hDevice,FSCTL_DISMOUNT_VOLUME…)强制关闭其他进程对设备的占用
  • 通过DeviceIoControl(hDevice,FSCTL_LOCK_VOLUME…)对设备进行加锁,防止其他进程干扰
  • 通过DeviceIoControl(hDevice,IOCTL_STORAGE_MEDIA_REMOVAL…)来禁用阻止删除多媒体设备功能
  • 通过DeviceIoControl(hDevice,IOCTL_STORAGE_EJECT_MEDIA…)来弹出设备。
    注意,此方法只是从文件浏览页面删除盘符,并不是真的弹出了设备
static int EjectLogDisk(const char *discId)
{
	DWORD accessMode = GENERIC_WRITE | GENERIC_READ;
	DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
	HANDLE hDevice;
	long bResult = 0;
	DWORD retu = 0;
	DWORD dwError;
	DWORD dwBytesReturned;
	DWORD dwSleepAmount;
	int nTryCount;
	char szDriv[10];

	if(discId == NULL){
		return 0;
	}
	dwSleepAmount = LOCK_TIMEOUT/LOCK_RETRIES;
	sprintf(szDriv,"\\\\.\\%s:",discId);
	hDevice = CreateFile(szDriv,accessMode,shareMode,NULL,OPEN_EXISTING,0,NULL);
	if(hDevice == INVALID_HANDLE_VALUE){
		printf("uninstallusb createfile failed error:%d\n",GetLastError());
		return -1;
	}
#if 1
	//卸载U盘卷,不论是否在使用
	dwBytesReturned = 0;
	if(!DeviceIoControl(hDevice,FSCTL_DISMOUNT_VOLUME,NULL,0,NULL,0,&dwBytesReturned,NULL)){
		printf("deviceIoConrol FSCTL_DISMOUNT_VOLUME failed\n");
	}
	
	//此循环是用于锁定要弹出的U盘设备,如果U盘在使用,则循环等待
	// Do this in a loop until a timeout period has expired
	for(nTryCount = 0;nTryCount < LOCK_RETRIES;nTryCount++){
		if(DeviceIoControl(hDevice,FSCTL_LOCK_VOLUME,NULL,0,NULL,0,&dwBytesReturned,NULL)){
			break;
		}
	}
#endif

	dwBytesReturned = 0;
	PREVENT_MEDIA_REMOVAL PMRBuffer;
	PMRBuffer.PreventMediaRemoval = FALSE;
	if(!DeviceIoControl(hDevice,IOCTL_STORAGE_MEDIA_REMOVAL,&PMRBuffer,sizeof(PREVENT_MEDIA_REMOVAL),NULL,0,&dwBytesReturned,NULL)){
		printf("DeviceIoControl IOCTL_STORAGE_MEDIA_REMOVAL failed:%d\n",GetLastError());
	}
	bResult = DeviceIoControl(hDevice,IOCTL_STORAGE_EJECT_MEDIA,NULL,0,NULL,0,&retu,NULL);
	if(!bResult){
		CloseHandle(hDevice);
		printf("uninstallusb DeviceIoControl failed error:%d\n",GetLastError());
		return -1;
	}
	CloseHandle(hDevice);

	return 0;
}

2. 通过设置CM_Request_Device_Eject

此方法的作用与从右下角托盘中安全删除并弹出媒体的功能一致,设置之后,设备的错误码为47。并且设备只能通过断上电重新接入电脑才可用。

#include 
#include "setupapi.h" 
#include "cfgmgr32.h" 
#pragma comment(lib,"setupapi.lib")  

DEVINST GetDrivesDevInstByDiskNumber(long DiskNumber)
{
	GUID* guid = (GUID*)(void*)&GUID_DEVINTERFACE_DISK;

	// Get device interface info set handle for all devices attached to system
	HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL,
		DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

	if (hDevInfo == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	// Retrieve a context structure for a device interface of a device
	// information set.
	DWORD dwIndex = 0;
	SP_DEVICE_INTERFACE_DATA devInterfaceData = {0};
	devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
	BOOL bRet = FALSE;


	PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd;
	SP_DEVICE_INTERFACE_DATA spdid;
	SP_DEVINFO_DATA spdd;
	DWORD dwSize;

	spdid.cbSize = sizeof(spdid);

	while ( true )
	{
		bRet = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex,
			&devInterfaceData);
		if (!bRet)
		{
			break;
		}

		SetupDiEnumInterfaceDevice(hDevInfo, NULL, guid, dwIndex, &spdid);

		dwSize = 0;
		SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize,NULL);

		if ( dwSize )
		{
			pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwSize);
			if ( pspdidd == NULL ) 
			{
				continue; // autsch
			}
			pspdidd->cbSize = sizeof(*pspdidd);
			ZeroMemory((PVOID)&spdd, sizeof(spdd));
			spdd.cbSize = sizeof(spdd);


			long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid,pspdidd, dwSize, &dwSize, &spdd);
			if ( res )
			{
				HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0,
					FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
				if ( hDrive != INVALID_HANDLE_VALUE )
				{
					STORAGE_DEVICE_NUMBER sdn;
					DWORD dwBytesReturned = 0;
					res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER,NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
					if ( res ) 
					{
						if ( DiskNumber == (long)sdn.DeviceNumber )
						{
							CloseHandle(hDrive);
							SetupDiDestroyDeviceInfoList(hDevInfo);
							return spdd.DevInst;
						}
					}
					CloseHandle(hDrive);
				}
			}
			HeapFree(GetProcessHeap(), 0, pspdidd);
		}
		dwIndex++;
	}

	SetupDiDestroyDeviceInfoList(hDevInfo);

	return 0;
}

static int EjectUSBDisk(char *discId)
{
	DWORD accessMode = GENERIC_WRITE | GENERIC_READ;
	DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
	HANDLE hDevice;
	long bResult = 0;
	DWORD retu = 0;
	DWORD dwError;
	DWORD dwBytesReturned;
	int nTryCount;
	char szDriv[10];

	if(discId == NULL)
	{
		return 0;
	}

	sprintf(szDriv,"\\\\.\\%s:",discId);
	hDevice = CreateFile(szDriv,accessMode,shareMode,NULL,OPEN_EXISTING,0,NULL);
	if(hDevice == INVALID_HANDLE_VALUE)
	{
		printf("uninstallusb createfile failed error:%d\n",GetLastError());
		return -1;
	}

	//使用CM_Request_Device_Eject弹出USB设备
	STORAGE_DEVICE_NUMBER sdn;
	long DiskNumber = -1;
	long res = DeviceIoControl(hDevice,IOCTL_STORAGE_GET_DEVICE_NUMBER,NULL,0,&sdn,sizeof(sdn),&dwBytesReturned,NULL);
	if(!res)
	{
		printf("DeviceIoControl IOCTL_STORAGE_GET_DEVICE_NUMBER failed:%d\n",GetLastError());
		CloseHandle(hDevice);
		return -1;
	}

	CloseHandle(hDevice);
	DiskNumber = sdn.DeviceNumber;
	if(DiskNumber == -1)
	{
		printf("DiskNumber == -1\n");
		return -1;
	}

	DEVINST DevInst = GetDrivesDevInstByDiskNumber(DiskNumber);
	if(DevInst == 0)
	{
		printf("GetDrivesDevInstDiskNumber failed\n");
		return -1;
	}

	ULONG Status = 0;
	ULONG ProblemNumber = 0;
	PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
	char VetoName[MAX_PATH];
	bool bSuccess = false;

	res = CM_Get_Parent(&DevInst,DevInst,0); //disk's parent, e.g. the USB bridge, the SATA controller....
	res = CM_Get_DevNode_Status(&Status,&ProblemNumber,DevInst,0);
	bool IsRemovable = ((Status & DN_REMOVABLE) != 0);

	printf("isremovable:%d\n",IsRemovable);
	long i;
	// try 3 times
	for(i = 0;i < 3;i++)
	{
		VetoName[0] = '\0';
		if(IsRemovable)
		{
			res = CM_Request_Device_Eject(DevInst,&VetoType,VetoName,MAX_PATH,0);
		}
		else
		{
			res = CM_Query_And_Remove_SubTree(DevInst,&VetoType,VetoName,MAX_PATH,0);
		}
		bSuccess = (res == CR_SUCCESS && VetoName[0] == '\0');
		if(bSuccess)
		{
			break;
		}
		else
		{
			Sleep(200);
		}
	}
	if(bSuccess){
		printf("Success\n\n");
	}else{
		printf("failed\n");

	}
	return 0;
}

你可能感兴趣的:(存储)