1. 程式人生 > >一個可以監控U盤接入、自動拷貝檔案到U盤、自動移除U盤的小程式

一個可以監控U盤接入、自動拷貝檔案到U盤、自動移除U盤的小程式

一個可以監控U盤接入、自動拷貝檔案到U盤、自動移除U盤的小程式


1,支援自動拖拽檔案,並獲取檔案路徑。
支援檔案拖拽:
DragAcceptFiles(hWnd, TRUE);
呼叫該API後,向視窗拖拽檔案就會收到 WM_DROPFILES訊息。 在響應該訊息的時候,可以使用DragQueryFile來獲取檔案數量以及檔案的具體路徑。 用法如下:
BOOL OnDragFiles(WPARAM wp, LPARAM lp)
{
	HDROP hDrop = reinterpret_cast<HDROP>(wp);
	int nCount = DragQueryFile(hDrop, -1, NULL, NULL);
	for (int i = 0; i < nCount; i++)
	{
		TCHAR szSrcPath[MAX_PATH] = { 0 };
		DragQueryFile(hDrop, i, szSrcPath, MAX_PATH - 1);
		g_VectSrcFilePath.push_back(szSrcPath);
	}
	return TRUE;
}
備註:如果拖拽的是一個資料夾,只會獲取到該資料夾的全路徑,資料夾的內容需要自己去遍歷。
2,監控U盤接入。 監控U盤接入一種簡單的方法就是 監聽WM_DEVICECHANGE訊息。 為了準確監聽該訊息需要註冊事件:
BOOL RegisterDeviceEvent(HWND hWnd)
{
	DEV_BROADCAST_DEVICEINTERFACE DevInt;
	memset(&DevInt, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
	DevInt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
	DevInt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	DevInt.dbcc_classguid = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
	return (RegisterDeviceNotification(hWnd, &DevInt, DEVICE_NOTIFY_WINDOW_HANDLE) != NULL);

}
然後響應該訊息,可以獲取接入U盤的碟符:
TCHAR FirstDriveFromMask(ULONG unitmask)
{
	int i;
	for (i = 0; i < 26; ++i)
	{
		if (unitmask & 0x1)
			break;
		unitmask = unitmask >> 1;
	}
	return (i + _T('A'));
}
BOOL OnDeviceChange(WPARAM wp, LPARAM lp)
{
	DWORD dwdata = static_cast<DWORD>(wp);
	PDEV_BROADCAST_HDR lpdb = reinterpret_cast<PDEV_BROADCAST_HDR>(lp);
	switch (dwdata)
	{
	case DBT_DEVICEREMOVECOMPLETE:
		break;
	case DBT_DEVICEARRIVAL:
		if (lpdb != NULL)
		{
			if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
			{
				PDEV_BROADCAST_VOLUME lpdv = reinterpret_cast<PDEV_BROADCAST_VOLUME>(lp);
				TCHAR cVolume = FirstDriveFromMask(lpdv->dbcv_unitmask);
				g_cVolume = cVolume;

知道碟符我們就可以方便的往裡面拷貝檔案了。
3,移除U盤:
有很多種方案,使用 CM_Request_Device_Eject API的好的一點就是 呼叫之後就看不到這個碟符了
可參考程式碼:
void RemoveTheUSBDisk(TCHAR DriveLetter)
{
	if (DriveLetter < _T('A') || DriveLetter > _T('Z'))
	{
		return ;
	}

	TCHAR szRootPath[] = _T("X:\\");   // "X:\"  -> for GetDriveType
	szRootPath[0] = DriveLetter;

	TCHAR szDevicePath[] = _T("X:");   // "X:"   -> for QueryDosDevice
	szDevicePath[0] = DriveLetter;

	TCHAR szVolumeAccessPath[] = _T("\\\\.\\X:");   // "\\.\X:"  -> to open the volume
	szVolumeAccessPath[4] = DriveLetter;

	HANDLE hDevice = CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
		OPEN_EXISTING, NULL, NULL);
	if (hDevice == INVALID_HANDLE_VALUE) 
	{
		return ;
	}
	// get the volume's device number
	STORAGE_DEVICE_NUMBER sdn;
	DWORD dwBytesReturned = 0;
	long DeviceNumber = -1;
	long res = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn,
		sizeof(sdn), &dwBytesReturned, NULL);
	if (res) 
	{
		DeviceNumber = sdn.DeviceNumber;
	}
	CloseHandle(hDevice);

	if (DeviceNumber == -1) 
	{
		return ;
	}

	// get the drive type which is required to match the device numbers correctely
	UINT DriveType = GetDriveType(szRootPath);

	// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
	TCHAR szDosDeviceName[MAX_PATH] = { 0 };
	res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
	if (!res) 
	{
		return;
	}

	DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);

	if (DevInst == 0) 
	{
		return;
	}

	PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
	TCHAR VetoName[MAX_PATH];
	VetoName[0] = 0;
	bool bSuccess = false;

	DEVINST DevInstParent = 0;
	res = CM_Get_Parent(&DevInstParent, DevInst, 0);

	for (long tries = 1; tries <= 3; tries++) 
	{ // sometimes we need some tries...

		VetoName[0] = 0;

		// CM_Query_And_Remove_SubTree doesn't work for restricted users
		//res = CM_Query_And_Remove_SubTreeW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
		//res = CM_Query_And_Remove_SubTreeW(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART);  // with messagebox (W2K, Vista) or balloon (XP)

		res = CM_Request_Device_Eject(DevInstParent, &VetoType, VetoName, MAX_PATH, 0);
		//res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 0); // with messagebox (W2K, Vista) or balloon (XP)

		bSuccess = (res == CR_SUCCESS && VetoType == PNP_VetoTypeUnknown);
		if (bSuccess)  
		{
			break;
		}

		Sleep(500); // required to give the next tries a chance!
	}

	if (bSuccess) 
	{
		return;
	}

	return;


}

Thanks~