当USB设备插入或者弹出时,Windows会产生一条全局消息:WM_DEVICECHANGE
我们需要做的是,获得这条消息的wParam参数,如果为DBT_DEVICEARRIVAL则表示有设备插入并可用,
如果是DBT_DEVICEREMOVECOMPLETE则表示有设备已经移除。再查看lParam参数为DBT_DEVTYP_VOLUME时,
就可以取出DEV_BROADCAST_VOLUME结构的卷号dbcv_unitmask,就知道是哪个卷被插入或者弹出。
代码片段如下:
using System;
using System.Runtime.InteropServices;
///
/// 监听设备的插入和拔出
///
public class DriveDetector
{
///
/// 设备插入事件
///
public event EventHandler DeviceArrived = null;
///
/// 设备拔出事件
///
public event EventHandler DeviceRemoved = null;
///
/// 消息处理(HwndSourceHook委托的签名)
///
///
///
///
///
///
///
public IntPtr WndProc(
IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam,
ref bool handled)
{
if (msg == NativeConstants.WM_DEVICECHANGE)
{
#warning USB设备检测目前只支持32位系统)
switch (wParam.ToInt32())
{
case NativeConstants.DBT_DEVICEARRIVAL:
{
var devType = Marshal.ReadInt32(lParam, 4);
if (devType == NativeConstants.DBT_DEVTYP_VOLUME)
{
var drive = GetDrive(lParam);
if (DeviceArrived != null)
{
var args = new DriveDectctorEventArgs(drive);
DeviceArrived(this, args); //触发设备插入事件
}
}
}
break;
case NativeConstants.DBT_DEVICEREMOVECOMPLETE:
{
var devType = Marshal.ReadInt32(lParam, 4);
if (devType == NativeConstants.DBT_DEVTYP_VOLUME)
{
var drive = GetDrive(lParam);
if (DeviceRemoved != null)
{
var args = new DriveDectctorEventArgs(drive);
DeviceRemoved(this, args);
}
}
}
break;
}
}
return IntPtr.Zero;
}
private static string GetDrive(IntPtr lParam)
{
var volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_VOLUME));
var letter = GetLetter(volume.dbcv_unitmask);
return string.Format("{0}:\\", letter);
}
///
/// 获得盘符
///
///
/// 1 = A
/// 2 = B
/// 4 = C...
///
/// 结果是A~Z的任意一个字符或者为'?'
private static char GetLetter(uint dbcvUnitmask)
{
const char nona = '?';
const string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (dbcvUnitmask == 0) return nona;
var i = 0;
var pom = dbcvUnitmask >> 1;
while (pom != 0)
{
pom = pom >> 1;
i++;
}
if (i < drives.Length)
return drives[i];
return nona;
}
/*
private static void GetLetterTest()
{
for (int i = 0; i < 67108864; i++)
{
Console.WriteLine("{0} - {1}", i, GetLetter((uint)i));
i = i << 1;
}
//0 - ?
//1 - A
//3 - B
//7 - C
//15 - D
//31 - E
//63 - F
//127 - G
//255 - H
//511 - I
//1023 - J
//2047 - K
//4095 - L
//8191 - M
//16383 - N
//32767 - O
//65535 - P
//131071 - Q
//262143 - R
//524287 - S
//1048575 - T
//2097151 - U
//4194303 - V
//8388607 - W
//16777215 - X
//33554431 - Y
//67108863 - Z
}*/
///
/// 设备插入或拔出事件
///
public class DriveDectctorEventArgs : EventArgs
{
///
/// 获得设备卷标
///
public string Drive { get; private set; }
public DriveDectctorEventArgs(string drive)
{
Drive = drive ?? string.Empty;
}
}
#region Win32 API
public partial class NativeConstants
{
/// WM_DEVICECHANGE -> 0x0219
public const int WM_DEVICECHANGE = 537;
/// BROADCAST_QUERY_DENY -> 0x424D5144
//public const int BROADCAST_QUERY_DENY = 1112363332;
//public const int DBT_DEVTYP_DEVICEINTERFACE = 5;
//public const int DBT_DEVTYP_HANDLE = 6;
public const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
//public const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal)
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
public const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct DEV_BROADCAST_VOLUME
{
/// DWORD->unsigned int
public uint dbcv_size;
/// DWORD->unsigned int
public uint dbcv_devicetype;
/// DWORD->unsigned int
public uint dbcv_reserved;
/// DWORD->unsigned int
public uint dbcv_unitmask;
/// WORD->unsigned short
public ushort dbcv_flags;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct OVERLAPPED
{
/// ULONG_PTR->unsigned int
public uint Internal;
/// ULONG_PTR->unsigned int
public uint InternalHigh;
/// Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196
public Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196 Union1;
/// HANDLE->void*
public System.IntPtr hEvent;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)]
public struct Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196
{
/// Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6 Struct1;
/// PVOID->void*
[System.Runtime.InteropServices.FieldOffsetAttribute(0)]
public System.IntPtr Pointer;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6
{
/// DWORD->unsigned int
public uint Offset;
/// DWORD->unsigned int
public uint OffsetHigh;
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
/// DWORD->unsigned int
public uint nLength;
/// LPVOID->void*
public System.IntPtr lpSecurityDescriptor;
/// BOOL->int
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public bool bInheritHandle;
}
#endregion
}
现在,你可以在你的UI线程上创建一个DriveDetector对象,监听DeviceArrived和DeviceRemoved事件。
然后通过该对象WndProc方法,传递UI线程上的消息。WPF程序可以直接用HwndSource对象AddHook,此方法的签名
与HwndSourceHook委托相同。WinForm程序也没关系,override窗口的void WndProc(ref Message m)方法,按
DriveDetector对象的WndProc签名格式,将m数据传入,或者干脆自己写个WinForm版本的。
我的演示代码效果图如下:(注,上述代码未提供显示代码,请自己编写)
另外,关于磁盘容量的监视可以使用FileSystemWatcher对象。
请参考:http://msdn.microsoft.com/zh-cn/library/cc437966.aspx
摘取代码如下:
using System;
using System.IO;
using System.Security.Permissions;
public class Watcher
{
public static void Main()
{
Run();
}
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
public static void Run()
{
string[] args = System.Environment.GetCommandLineArgs();
// If a directory is not specified, exit program.
if(args.Length != 2)
{
// Display the proper way to call the program.
Console.WriteLine("Usage: Watcher.exe (directory)");
return;
}
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[1];
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch text files.
//watcher.Filter = "*.txt";
watcher.IncludeSubdirectories=true;
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while(Console.Read()!='q');
}
// Define the event handlers.
private static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}
}