今天在编程检测 Storage(SD/USB) 时,使用了一个消息:WM_DEVICECHANGE 来检测,测试了一下插入/拔出 Storage 设备时确实可以接收到消息。
但后继的处理出现问题:在 Storage 设备拔出时,立刻调用 FindFirstStore/FindNextStore 和 FindFirstPartition/FindNextPartition 时正常;
但在 Storage 设备插入时,立刻调用 FindFirstStore/FindNextStore 和 FindFirstPartition/FindNextPartition 未检测到刚插入的 Storage 设备的目录(盘符)。
单步测试发现,只要在首次调用 FindFirstStore 时停一会,就正常。说明在接收到 WM_DEVICECHANGE 消息时不能立刻扫描,需要等待一会。
看了一下公司其它项目对 WM_DEVICECHANGE 消息的处理,都是采用延时如 200ms 来处理。
延时处理,个人认为不是好的办法。因为:
(1) 延时的时间不好确认
(2)在 Storage 设备拔出后,如果在延时的时间内、且设备已经拔出时出现文件操作,较难处理。
除了处理设备的插入/拔出的硬件检测消息外,是否有其它办法来检测 Storage 设备的插入/拔出?
答案是肯定的。可以采用文件系统的 MOUNT/UNMOUNT 消息来处理。
请看以下示例代码:
// 检测 Storage 插入/拔出
#include "pnp.h"
#include "storemgr.h"
#define DEVICE_QUEUE_MAX_SIZE 3
static BOOL gbExitDetectThread = FALSE;
union BufferPlusDevDetail
{
DEVDETAIL DevDetail;
BYTE byBuffer[sizeof(DEVDETAIL) + MAX_DEVCLASS_NAMELEN * sizeof(TCHAR)];
};
DWORD DevicePlugDetectThread(LPVOID lpPara)
{
DWORD dwObjectRet = 0;
MSGQUEUEOPTIONS msgQInfo =
{
sizeof(MSGQUEUEOPTIONS),
MSGQUEUE_ALLOW_BROKEN,
DEVICE_QUEUE_MAX_SIZE,
sizeof(BufferPlusDevDetail),
TRUE
};
DWORD dwMsgSize = 0;
DWORD dwAlert = 0;
HANDLE hStorageMsgQueue = CreateMsgQueue(NULL, &msgQInfo);
if (NULL == hStorageMsgQueue)
{
return FALSE;
}
// request notification
HANDLE hStorageNotify = RequestDeviceNotifications(&FATFS_MOUNT_GUID, hStorageMsgQueue, FALSE);
if(NULL == hStorageNotify)
{
return FALSE;
}
while(1)
{
if(gbExitDetectThread)
{
break;
}
dwObjectRet = WaitForSingleObject(hStorageMsgQueue,1000);
if(WAIT_OBJECT_0 == dwObjectRet)
{
BufferPlusDevDetail DevBuff = {0};
if (ReadMsgQueue(hStorageMsgQueue, &DevBuff, sizeof(DevBuff), &dwMsgSize, 0, &dwAlert))
{
BOOL bAttached = DevBuff.DevDetail.fAttached;
CString csStorageName = DevBuff.DevDetail.szName;
RETAILMSG(1,(L"Storage:: received %s message: %s!\r\n",0 == bAttached ? L"unmount" : L"mount",csStorageName));
COPYDATASTRUCT cs;
TCHAR tcStorage[MAX_PATH];
ZeroMemory(tcStorage,sizeof(TCHAR) * MAX_PATH);
wsprintf(tcStorage,L"%s",csStorageName);
cs.dwData = 0x5050;
cs.cbData = sizeof(TCHAR) * wcslen(tcStorage);
cs.lpData = tcStorage;
SendMyMsgToAgent(WM_COPYDATA,bAttached,(LPARAM)&cs);
}
}
else if(WAIT_TIMEOUT == dwObjectRet)
{
}
}
StopDeviceNotifications(hStorageNotify);
CloseHandle(hStorageNotify);
return TRUE;
}
// 调用示例,直接开启线程。代码如下:
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DevicePlugDetectThread, NULL, 0, NULL);