精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源心得,转载请注明:“锐英源www.wisestudy.cn,孙老师作品,电话13803810136。”需要全文内容也请联系孙老师。
;
This article describes how to use the .NET System.Management WMI (Windows Management Instrumentation) wrappers to enumerate and describe USB disk drives. It also includes a non-Interop solution for detecting drive state changes as they come online or go offline.本文描述了如何使用.net System.Management WMI(Windows管理规范)封装器列举和描述USB磁盘驱动器。 它还包括一个非互操作模式解决方案,它能检测驱动在线或离线状态改变
While writing iTuner, I needed to be able to enumerate USB disk drives (MP3 players) and allow the user to select one with which iTuner could synchronize iTunes playlists. In order to select the correct device, the user needs some basic identifying information beyond just drive letter such as volume name, manufacturer's model name, and available disk space. As an added feature, I wanted the UI to update automatically as USB drives come online or go offline. Finally, I wanted to accomplish this through a tidy, simple interface that did not add tremendous complexity to the main application.
在开发 itunes设备时 ,我需要能够列举USB磁盘驱动器(MP3),允许用户选择一个对iTunes同步iTunes播放列表。 为了选择正确的设备,用户需要一些基本的识别信息,不仅仅是驱动器卷名、制造商的模式名称和可用的磁盘空间。 作为一个额外的功能,我希望UI在USB驱动器在线或离线时自动更新。 最后,我想完成这个整洁,简单的界面,没有添加巨大复杂性到主应用程序。
Caveat: The UsbManager.GetAvailableDisks method can be run in any application type. However, the StateChanged event will not be fired unless running in the context of a WPF or Windows Forms application. This is because the event handler relies on Windows message processing to intercept these state changes as explained below.
警告: 的 UsbManager.GetAvailableDisks 方法可以运行在任何应用程序类型。 然而, statechange 事件不会被,除非在WPF的上下文中运行或Windows窗体应用程序中运行。 这是因为事件处理程序依赖于Windows消息处理来捕获这些状态更改,解释如下。
As an application developer and consumer of this API, you first need an instance of the UsbManager class. This class exposes a very simple interface:
作为应用程序开发人员和消费者的API,您首先需要的一个实例 UsbManager 类。 这类公开了一个非常简单的接口:
class UsbManager : IDisposable { public UsbManager (); public event UsbStateChangedEventHandler StateChanged; public UsbDiskCollection GetAvailableDisks (); } class UsbDiskCollection : ObservableCollection<UsbDisk> { public bool Contains (string name); public bool Remove (string name); } class UsbDisk { public ulong FreeSpace { get; } public string Model { get; } public string Name { get; } public ulong Size { get; } public string Volume { get; } public string ToString (); }
Instantiate a new UsbManager using its parameterless constructor 实例化一个新 UsbManager 使用它的无参数构造函数。
Wire up an event handler to the StateChanged event. 一个事件处理程序来连接 statechange 事件。
If desired, call GetAvailableDisks to retrieve a list of current USB disks. 如果需要的话,叫 GetAvailableDisks 检索当前USB磁盘列表。
The UsbDisk class abstracts the information pertaining to a particular USB disk. It includes only the most recognizable fields that a typical end user might use to differentiate drives such as the drive letter (Name), volume name, and manufacturer's model name. It also specifies the available free space and total disk size, both specified in bytes. While other information such as serial numbers, partition or sector data might be of interest to developers, they're quite esoteric to end users.
的 UsbDisk 类抽象的信息属于一个特定的USB磁盘。 最知名的字段,它只包含一个典型的最终用户可能使用区分硬盘驱动器等(名字),卷名,和制造商的模型的名字。 它还指定了可用的自由空间和总磁盘大小,字节中指定。 而其他信息,如编号、分区或部门数据可能感兴趣的开发人员,他们很深奥的最终用户。
That's it! Happy coding! OK, keep reading if you want to understand how it's all put together.就是这样! 编码快乐! 好吧,继续阅读,如果你想了解都是放在一起。
Developing this API was first and foremost an exercise in discovering the WMI classes and their relationships. Unfortunately, WMI does not have a single WMI_USBDiskDrive class with all the properties we want. But the information is there. I started by using the WMI Explorer utility from KS-Soft. It's free and available for download on their Web site.
这个API开发首先是发现WMI类以及它们之间的关系。 不幸的是,WMI没有一个 WMI_USBDiskDrive 类的所有属性。 但是信息可查。 我开始使用KS-Soft公司的WMI Explorer 。它是免费的,在他们的网站上可下载。
The first WMI class that draws your attention is Win32_DiskDrive. Drives are listed with creative and obvious names like "\\PHYSICALDRIVE0". We can filter these by looking at only those with an InterfaceType property value of "USB". Win32_DiskDrive also specifies the Model and Size of the drive. There are lots of other properties, but none are very interesting in this case. Here's the WMI query that retrieves the DeviceID and Model name for USB drives:
第一个WMI类吸引你的注意力 Win32_DiskDrive 。 列出驱动,带有创造性和明显的名字”\ \ PHYSICALDRIVE0 ”。 我们可以过滤这些名称,通过观察 InterfaceType 属性值,看它包含“USB ”不。 Win32_DiskDrive 还指定了驱动器的模型和尺寸。 还有很多其他的属性,但非常有趣。 这是WMI查询USB驱动器检索出来的DeviceID和模型名称
select DeviceID, Model from Win32_DiskDrive where InterfaceType='USB'
My next stop was the Win32_LogicalDisk class. This gets a bit more interesting right off the bat because instances are listed as drive letters like "C:", "D:", and "S:". We can also fetch the FreeSpace and VolumeName properties. Here's the WMI query:
我的下一站是 Win32_LogicalDisk 类。 这变得更加有趣马上因为实例列出驱动器字母像“ C: ”、“ D: ”、“ 史: ”。 我们也可以获取 空闲空间 和 VolumeName 属性。 这是WMI查询:
select FreeSpace, Size, VolumeName from Win32_LogicalDisk where Name='S:'
We now need a way to associate Win32_DiskDrive and Win32_LogicalDisk so we can marry these bits of information together. You might think there would be some shared field that allows you join the two classes. No such luck. And that's exactly where the Web came to the rescue and I discovered a bit of code tucked away on MSDN that demonstrates how to associate these classes. We can use the associators operator to discover associations between various classes. Given a Win32_DiskDrive instance, we can use its DeviceID property to determine the Win32_DiskPartition instance associated via Win32_DiskDriveToDiskPartition:
现在我们需要一种方法来关联 Win32_DiskDrive 和 Win32_LogicalDisk 这些信息我们可以结婚在一起。 你可能认为会有一些共享字段,允许你加入两类。 没有这样的运气。 这正是Web来救援,我发现一些隐藏代码 在MSDN上 演示了如何将这些类。 我们可以使用 关联方 操作员发现各种类之间的关联。 给定一个 Win32_DiskDrive 实例中,我们可以使用它 的DeviceID 属性来确定 Win32_DiskPartition 通过相关实例 Win32_DiskDriveToDiskPartition :
associators of {Win32_DiskDrive.DeviceID='\\PHYSICALDRIVE1'}
where AssocClass = Win32_DiskDriveToDiskPartition
Then using Win32_DiskPartition.DeviceID, we can determine the Win32_LogicalDisk instance associated via Win32_LogicalDiskToPartition:
然后使用 Win32_DiskPartition.DeviceID ,我们可以确定 Win32_LogicalDisk 通过相关实例 Win32_LogicalDiskToPartition
associators of {Win32_DiskPartition.DeviceID='Disk #0, Partition #1'}
where AssocClass = Win32_LogicalDiskToPartition
To execute a WMI query, we can use the System.Management.ManagementObjectSearcher class. This class always has the same pattern: search, get, enumerate as shown here: 执行一个WMI查询,我们可以使用 System.Management.ManagementObjectSearcher 类。 这门课总是有相同的模式:搜索、获取、列举如下所示:
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("select * from Win32_DiskDrive"); ManagementObjectCollection items = searcher.Get(); foreach (ManagementObject item in items { }
Given the cascading calls needed to query the four WMI classes, we would end up with a fairly ugly nesting of foreach loops. In order to clean this up and make the logic more obvious, I created a simple extension method for the ManagementObjectSearcher class. This extension adds a First() method to the ManagementObjectSearcher class that invokes its Get method, enumerates the resultant collection and immediately returns the first item in that collection:
鉴于层叠调用需要查询四个WMI类,我们会得到一个相当丑陋的嵌套 foreach 循环。 为了搞定这一切,使逻辑更明显,我创建了一个简单的 扩展方法 为 ManagementObjectSearcher 类。 这个扩展添加一个 第() 方法 ManagementObjectSearcher 类调用它 得到 方法,列举了结果集合并立即返回集合的第一项:
public static ManagementObject First (this ManagementObjectSearcher searcher)
{
ManagementObject result = null;
foreach (ManagementObject item in searcher.Get())
{
result = item;
break;
}
return result;
}
Combine this helper extension with the WMI queries above and we end up with a straightforward code in UsbManager.GetAvailableDisks(). Yes, we still have a nested structure, but testing for null is much more clear than the alternative!把这个助手扩展与上面的WMI查询,我们最终得到一个简单的代码 UsbManager.GetAvailableDisks() 。 是的,我们仍然有一个嵌套结构,但测试 零 是比另一种更清晰!
public UsbDiskCollection GetAvailableDisks () { UsbDiskCollection disks = new UsbDiskCollection(); // browse all USB WMI physical disks foreach (ManagementObject drive in new ManagementObjectSearcher( "select DeviceID, Model from Win32_DiskDrive " + "where InterfaceType='USB'").Get()) { // associate physical disks with partitions ManagementObject partition = new ManagementObjectSearcher(String.Format( "associators of {{Win32_DiskDrive.DeviceID='{0}'}} " + "where AssocClass = Win32_DiskDriveToDiskPartition", drive["DeviceID"])).First(); if (partition != null) { // associate partitions with logical disks (drive letter volumes) ManagementObject logical = new ManagementObjectSearcher(String.Format( "associators of {{Win32_DiskPartition.DeviceID='{0}'}} " + "where AssocClass= Win32_LogicalDiskToPartition", partition["DeviceID"])).First();p> if (logical != null) { // finally find the logical disk entry ManagementObject volume = new ManagementObjectSearcher(String.Format( "select FreeSpace, Size, VolumeName from Win32_LogicalDisk " +> "where Name='{0}'", logical["Name"])).First(); UsbDisk disk = new UsbDisk(logical["Name"].ToString()); disk.Model = drive["Model"].ToString(); disk.Volume = volume["VolumeName"].ToString(); disk.FreeSpace = (ulong)volume["FreeSpace"]; disk.Size = (ulong)volume["Size"]; disks.Add(disk); } } } return disks; }
Now that we can enumerate the currently available USB disk drives, it would be nice to know when one of these goes offline or a new drive comes online. This is the purpose of the UsbManager.DriverWindow class.
现在,我们可以列举当前可用的USB磁盘驱动器,这将是很高兴知道其中一个脱机或一个新的驱动上线。 这是UsbManager.DriverWindow 类的目的 。
The DriverWindow class extends System.Windows.Forms.NativeWindow and is a private class encapsulated by UsbManager. The WndProc method of NativeWindow provides a convenient location to intercept and process Windows messages. The Windows message we need is WM_DEVICECHANGE and its LParam value must be DBT_DEVTYP_VOLUME. The WParam value is also important and we look for two DBT values (and an optional third).
DriverWindow 类继承了 System.Windows.Forms.NativeWindow ,这是一个 私人 类封装了 UsbManager 。 NativeWindow的WndProc方法 提供了一个方便的位置拦截和处理窗口消息。 我们需要的是Windows消息 WM_DEVICECHANGE 和它的 LParam 值必须是 DBT_DEVTYP_VOLUME 。 的 按钮 价值也很重要,我们找两个印度生物技术部值(和一个可选的第三)。
DBT_DEVICEARRIVAL - broadcast when a device or piece of media has been inserted and becomes available DBT_DEVICEARRIVAL ——播放当一块设备或媒体已经插入和可用
DBT_DEVICEREMOVECOMPLETE - broadcast when a device or piece of media has been physically removed DBT_DEVICEREMOVECOMPLETE ——广播当一块设备或媒体的物理上删除
DBT_DEVICEQUERYREMOVE - broadcast to request permission to remove a device or piece of media; we do not process this message but it provides an opportunity to deny removal of a device DBT_DEVICEQUERYREMOVE ——广播申请删除一个设备或部分媒体;我们不处理这些消息,但它提供了一个机会拒绝删除设备
DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE both deliver a DEV_BROADCAST_VOLUME struct. This is actually a DEV_BROADCAST_HDR whose dbcv_devicetype is set to DBT_DEVTYP_VOLUME, so we know we can cast the packet to a DEV_BROADCAST_VOLUME.
DBT_DEVICEARRIVAL 和 DBT_DEVICEREMOVECOMPLETE 两个交付 DEV_BROADCAST_VOLUME 结构体。 这实际上是一个 DEV_BROADCAST_HDR 谁的 dbcv_devicetype 被设置为 DBT_DEVTYP_VOLUME ,所以我们知道我们可以把数据包 DEV_BROADCAST_VOLUME 。
[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_VOLUME
{
public int dbcv_size; // size of the struct
public int dbcv_devicetype; // DBT_DEVTYP_VOLUME
public int dbcv_reserved; // reserved; do not use
public int dbcv_unitmask; // Bit 0=A, bit 1=B, and so on (bitmask)
public short dbcv_flags; // DBTF_MEDIA=0x01, DBTF_NET=0x02 (bitmask)
}
The dbcv_unitmask field is a bitmask where each of the first 26 low-order bits correspond to a Windows drive letter. Apparently, it is possible to see a device associated with more than one drive letter but we only care about the first available for our use. dbcv_unitmask 字段是一个前26名低阶位的位掩码,每个对应一个Windows驱动器。 显然,可以看到一个与多个驱动器相关设备,但我们只关心可供我们使用的首个驱动。
DriverWindow fires its own StateChanged event to signal UsbManager. UsbManager then decides if it needs to retrieve information - which it does for new arrivals - and then fires its own StateChanged event to signal consumers. DriverWindow 火灾的 statechange 事件信号 UsbManager 。 UsbManager 然后决定是否需要检索信息——它对新来者——然后自己火灾 statechange 事件消费者的信号。
The demo app attached to this article shows all the power of UsbManager in just a few lines of code. It first enumerates all existing USB disk drives and displays them in a TextBox. It then wires up a handler to the UsbManager.StateChanged event. This event is defined as follows:
本文演示应用程序显示了强大能力, UsbManager 在短短几行代码内能实现很多功能。 它首先列举了现有的USB磁盘驱动器并显示它们到文本框里 。 然后连接了一个处理程序 UsbManager.StateChanged 事件。 这个事件被定义如下:
public event UsbStateChangedEventHandler StateChanged
Take a look at the StateChanged implementation and you'll notice that the add and remove statements have been extended. This allows us to instantiate a DriverWindow instance only when consumers are listening and then dispose it off when all consumers have stopped listening. 看一看 statechange 实现和你会注意到 添加 和 删除 报表已经扩展。 这允许我们实例化 DriverWindow 实例只有当消费者听然后处理掉当所有消费者已经不再听。
Your handler must be declared as a UsbStateChangedEventHandler as follows: 必须声明为一个处理程序 UsbStateChangedEventHandler 如下:
public delegate void UsbStateChangedEventHandler (UsbStateChangedEventArgs e);
And the UsbStateChangedEventArgs is declared as: 和 UsbStateChangedEventArgs 被声明为:
public class UsbStateChangedEventArgs : EventArgs
{
public UsbDisk Disk;
public UsbStateChange State;
}
The State property is an enum specifying one of Added, Removing, or Removed. State属性是一个 枚举, 表示了: 添加 , 删除中,或已删除 。
The Disk property is a UsbDisk instance. If State is Added, then all properties of Disk should be populated. However, if State is Removing or Removed, then only the Name property is populated since we can't detect attributes of a device that no longer exist. Disk 属性是一个 UsbDisk 实例。 如果 State 是 添加 ,那么Disk的所有属性会被填充。 然而,如果 State 是 删除中 或 已删除 则只有Name 属性填充,因为我们无法检测到不再存在设备的属性。