问题描述
我知道在$ C $的CProject两篇文章(一个使用WMI和其他没有WMI但在C ++)。我试过WMI方式,不仅是缓慢的,但也是不可靠的。所以,这就是为什么我决定放弃这种方式。我想通过的PInvoke做到这一点在C#。我尝试过,但陷入了DeviceIoControl的API。任何人都可以给我一个提示?这里是我的code:
使用系统;
使用System.ComponentModel;
使用System.Collections.Generic;
使用System.Linq的;
使用System.Text;
使用System.Runtime.InteropServices;
使用Microsoft.Win32.SafeHandles;命名空间Chemulator.Common
{
公共类HDSerialNumber
{
[StructLayout(LayoutKind.Sequential)]
私人结构IDEREGS
{
公共字节bFeaturesReg;
公共字节bSectorCountReg;
公共字节bSectorNumberReg;
公共字节bCylLowReg;
公共字节bCylHighReg;
公共字节bDriveHeadReg;
公共字节bCommandReg;
公共字节bReserved;
} [StructLayout(LayoutKind.Sequential)]
私人结构SENDCMDINPARAMS
{
公众的Int32 cBufferSize;
公共IDEREGS irDriveRegs;
公共字节bDriveNumber;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 3)]
公众的byte [] bReserved;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)]
公众的Int32 [] dwReserved;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 1)]
公众的byte [] bBuffer;
}
[StructLayout(LayoutKind.Sequential)]
私人结构DRIVERSTATUS
{
公共字节bDriverError;
公共字节bIDEError;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 2)]
公众的byte [] bReserved;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 2)]
公众的Int32 [] dwReserved;
} [StructLayout(LayoutKind.Sequential)]
私人结构SENDCMDOUTPARAMS
{
公众的Int32 cBufferSize;
公共DRIVERSTATUS DriverStatus;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = IDENTIFY_BUFFER_SIZE)
公众的byte [] bBuffer;
} [StructLayout(LayoutKind.Sequential)]
私人结构GETVERSIONOUTPARAMS
{
公共字节bVersion;
公共字节bRevision;
公共字节bReserved;
公共字节bIDEDeviceMap;
公众的Int32 fCapabilities;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)]
公众的Int32 dwReserved;
} [StructLayout(LayoutKind.Sequential)]
私人结构STORAGE_PROPERTY_QUERY
{
公众的Int32属性ID;
公众的Int32查询类型;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 1)]
公众的byte [] AdditionalParameters;
} [StructLayout(LayoutKind.Sequential)]
私人结构STORAGE_DEVICE_DESCRIPTOR
{
公众的Int32版本;
公众的Int32大小;
公共字节的devicetype;
公共字节DeviceTypeModifier;
公共字节RemovableMedia;
公共字节CommandQueueing;
公众的Int32 VendorIdOffset;
公众的Int32 ProductIdOffset;
公众的Int32 ProductRevisionOffset;
公众的Int32 SerialNumberOffset;
公共字节BusType;
公众的Int32 RawPropertiesLength;
[的MarshalAs(UnmanagedType.ByValArray,SizeConst = 10240)
公众的byte [] RawDeviceProperties;
} 函数[DllImport(KERNEL32.DLL,SetLastError = TRUE)]
私人静态外部SafeFileHandle的CreateFile(字符串lpFileName的对象,的Int32 dwDesiredAccess,的Int32 dwShareMode,IntPtr的lpSecurityAttributes,的Int32 CREATE_NEW标志,的Int32 dwFlagsAndAttributes,IntPtr的hTemplateFile); 函数[DllImport(KERNEL32)]
私人静态的extern BOOL DeviceIoControl的(SafeFileHandle hDevice,UINT dwIoControl code,IntPtr的lpInBuffer,UINT nInBufferSize,IntPtr的lpOutBuffer,UINT nOutBufferSize,裁判UINT lpBytesReturned,IntPtr的lpOverlapped的);
私人常量的Int32 OPEN_EXISTING = 3;
私人常量的Int32 GENERIC_READ =选中((INT)为0x80000000);
私人常量的Int32 GENERIC_WRITE = 0x40000000之后;
私人常量的Int32 FILE_SHARE_READ =为0x1;
私人常量的Int32 FILE_SHARE_WRITE = 0X2;
私人常量的Int32 FILE_SHARE_DELETE =为0x4;
私人常量的Int32 SMART_GET_VERSION = 0x74080;
私人常量的Int32 SMART_RCV_DRIVE_DATA = 0x7C088;
私人常量的Int32 ID_CMD = 0xEC;
私人常量的Int32 IDENTIFY_BUFFER_SIZE = 512;
私人常量的Int32 CAP_SMART_CMD =为0x4;
私人常量的Int32 IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
私人常量的Int32 PropertyStandardQuery = 0;
私人常量的Int32 StorageDeviceProperty会= 0; 公共静态字符串GetSerialNumber(INT diskNumber)
{
字符串str = GetSerialNumberUsingStorageQuery(diskNumber);
如果(string.IsNullOrEmpty(STR))
海峡= GetSerialNumberUsingSmart(diskNumber);
返回海峡;
} 公共静态字符串GetSerialNumberUsingStorageQuery(INT diskNumber)
{
使用(SafeFileHandle HDISK = OpenDisk(diskNumber))
{
UINT iBytesReturned = 0;
VAR SPQ =新STORAGE_PROPERTY_QUERY();
VAR SDD =新STORAGE_DEVICE_DESCRIPTOR();
spq.PropertyId = StorageDeviceProperty会;
spq.QueryType = PropertyStandardQuery;
如果(DeviceIoControl的(HDISK,IOCTL_STORAGE_QUERY_PROPERTY,SPQ,(UINT)Marshal.SizeOf(SPQ),SDD(UINT)Marshal.SizeOf(SDD),REF iBytesReturned,IntPtr.Zero))
扔CreateWin32Exception(Marshal.GetLastWin32Error(),的DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)); VAR的结果=新的StringBuilder();
如果(sdd.SerialNumberOffset大于0)
{
变种rawDevicePropertiesOffset = Marshal.SizeOf(SDD) - sdd.RawDeviceProperties.Length;
INT POS = sdd.SerialNumberOffset - rawDevicePropertiesOffset;
而(POS< iBytesReturned&放大器;&安培;!sdd.RawDeviceProperties [POS] = 0)
{
result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties,POS,1));
POS + = 1;
}
}
返回result.ToString();
}
} 公共静态字符串GetSerialNumberUsingSmart(INT diskNumber)
{
使用(SafeFileHandle HDISK = OpenDisk(diskNumber))
{
如果(IsSmartSupported(HDISK))
{
INT32 iBytesReturned = 0;
VAR SCI =新SENDCMDINPARAMS();
VAR SCO =新SENDCMDOUTPARAMS();
sci.irDriveRegs.bCommandReg = ID_CMD;
sci.bDriveNumber =(字节)diskNumber;
sci.cBufferSize = IDENTIFY_BUFFER_SIZE;
如果(DeviceIoControl的(HDISK,SMART_RCV_DRIVE_DATA,SCI(UINT)Marshal.SizeOf(SCI),SCO(UINT)Marshal.SizeOf(SCO),REF iBytesReturned,IntPtr.Zero))
扔CreateWin32Exception(Marshal.GetLastWin32Error(),的DeviceIoControl(SMART_RCV_DRIVE_DATA)); VAR的结果=新的StringBuilder();
对于(INT指数= 20;指数< 39;指数+ = 2)
{
result.Append(Encoding.ASCII.GetString(sco.bBuffer,索引+ 1,1));
result.Append(Encoding.ASCII.GetString(sco.bBuffer,指数,1));
}
返回result.ToString();
}
返回的String.Empty;
}
} 私有静态Win32Exception CreateWin32Exception(的Int32错误code,字符串上下文中)
{
VAR win32Exception =新Win32Exception(错误code);
win32Exception.Data [上下文] =背景;
返回win32Exception;
} 私有静态SafeFileHandle OpenDisk(INT diskNumber)
{
SafeFileHandle hDevice =的CreateFile(的String.Format(@\\\\。\\ PhysicalDrive {0},diskNumber),GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,IntPtr.Zero,OPEN_EXISTING,0,IntPtr.Zero);
如果(!hDevice.IsInvalid)
返回hDevice;
其他
扔CreateWin32Exception(Marshal.GetLastWin32Error(),的CreateFile);
} 私人静态布尔IsSmartSupported(SafeFileHandle HDISK)
{
UINT iBytesReturned = 0;
VAR GVO =新GETVERSIONOUTPARAMS();
IntPtr的pGVO = Marshal.AllocHGlobal(512);
如果(DeviceIoControl的(HDISK,SMART_GET_VERSION,IntPtr.Zero,0,pGVO,512,参考iBytesReturned,IntPtr.Zero))
返回false;
回报(gvo.fCapabilities&安培; CAP_SMART_CMD)GT; 0;
}
}
}
退房有关的。
向下滚动页面,在这里你可以看到的 VB .NET 3.0完整的例子(感谢bogdandaniel)的编辑pPumkiN 。这是访问不同影响IO设备的完整的例子。我beleieve有DRIVE_INFO了。
另外我没有任何这方面的经验。自己尝试一下。
I know there are two articles in CodeProject (one uses WMI and the other no WMI but in C++). I tried the WMI way, not only it's slow, but it is also unreliable. So, that's why I decided not to pursue that way. I want to do it in C# through pInvoke. I tried it but got stuck in DeviceIoControl API. Can anybody give me a hint? Here is my code:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Chemulator.Common
{
public class HDSerialNumber
{
[StructLayout(LayoutKind.Sequential)]
private struct IDEREGS
{
public byte bFeaturesReg;
public byte bSectorCountReg;
public byte bSectorNumberReg;
public byte bCylLowReg;
public byte bCylHighReg;
public byte bDriveHeadReg;
public byte bCommandReg;
public byte bReserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct SENDCMDINPARAMS
{
public Int32 cBufferSize;
public IDEREGS irDriveRegs;
public byte bDriveNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public Int32[] dwReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] bBuffer;
}
[StructLayout(LayoutKind.Sequential)]
private struct DRIVERSTATUS
{
public byte bDriverError;
public byte bIDEError;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] bReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public Int32[] dwReserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct SENDCMDOUTPARAMS
{
public Int32 cBufferSize;
public DRIVERSTATUS DriverStatus;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IDENTIFY_BUFFER_SIZE)]
public byte[] bBuffer;
}
[StructLayout(LayoutKind.Sequential)]
private struct GETVERSIONOUTPARAMS
{
public byte bVersion;
public byte bRevision;
public byte bReserved;
public byte bIDEDeviceMap;
public Int32 fCapabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public Int32 dwReserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct STORAGE_PROPERTY_QUERY
{
public Int32 PropertyId;
public Int32 QueryType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] AdditionalParameters;
}
[StructLayout(LayoutKind.Sequential)]
private struct STORAGE_DEVICE_DESCRIPTOR
{
public Int32 Version;
public Int32 Size;
public byte DeviceType;
public byte DeviceTypeModifier;
public byte RemovableMedia;
public byte CommandQueueing;
public Int32 VendorIdOffset;
public Int32 ProductIdOffset;
public Int32 ProductRevisionOffset;
public Int32 SerialNumberOffset;
public byte BusType;
public Int32 RawPropertiesLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]
public byte[] RawDeviceProperties;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, Int32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32")]
private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped);
private const Int32 OPEN_EXISTING = 3;
private const Int32 GENERIC_READ = unchecked((int)0x80000000);
private const Int32 GENERIC_WRITE = 0x40000000;
private const Int32 FILE_SHARE_READ = 0x1;
private const Int32 FILE_SHARE_WRITE = 0x2;
private const Int32 FILE_SHARE_DELETE = 0x4;
private const Int32 SMART_GET_VERSION = 0x74080;
private const Int32 SMART_RCV_DRIVE_DATA = 0x7C088;
private const Int32 ID_CMD = 0xEC;
private const Int32 IDENTIFY_BUFFER_SIZE = 512;
private const Int32 CAP_SMART_CMD = 0x4;
private const Int32 IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
private const Int32 PropertyStandardQuery = 0;
private const Int32 StorageDeviceProperty = 0;
public static string GetSerialNumber(int diskNumber)
{
string str = GetSerialNumberUsingStorageQuery(diskNumber);
if (string.IsNullOrEmpty(str))
str = GetSerialNumberUsingSmart(diskNumber);
return str;
}
public static string GetSerialNumberUsingStorageQuery(int diskNumber)
{
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
{
uint iBytesReturned = 0;
var spq = new STORAGE_PROPERTY_QUERY();
var sdd = new STORAGE_DEVICE_DESCRIPTOR();
spq.PropertyId = StorageDeviceProperty;
spq.QueryType = PropertyStandardQuery;
if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, spq, (uint)Marshal.SizeOf(spq), sdd, (uint)Marshal.SizeOf(sdd), ref iBytesReturned, IntPtr.Zero))
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY)");
var result = new StringBuilder();
if (sdd.SerialNumberOffset > 0)
{
var rawDevicePropertiesOffset = Marshal.SizeOf(sdd) - sdd.RawDeviceProperties.Length;
int pos = sdd.SerialNumberOffset - rawDevicePropertiesOffset;
while (pos < iBytesReturned && sdd.RawDeviceProperties[pos] != 0)
{
result.Append(Encoding.ASCII.GetString(sdd.RawDeviceProperties, pos, 1));
pos += 1;
}
}
return result.ToString();
}
}
public static string GetSerialNumberUsingSmart(int diskNumber)
{
using (SafeFileHandle hDisk = OpenDisk(diskNumber))
{
if (IsSmartSupported(hDisk))
{
Int32 iBytesReturned = 0;
var sci = new SENDCMDINPARAMS();
var sco = new SENDCMDOUTPARAMS();
sci.irDriveRegs.bCommandReg = ID_CMD;
sci.bDriveNumber = (byte)diskNumber;
sci.cBufferSize = IDENTIFY_BUFFER_SIZE;
if (DeviceIoControl(hDisk, SMART_RCV_DRIVE_DATA, sci, (uint)Marshal.SizeOf(sci), sco, (uint)Marshal.SizeOf(sco), ref iBytesReturned, IntPtr.Zero))
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "DeviceIoControl(SMART_RCV_DRIVE_DATA)");
var result = new StringBuilder();
for (int index = 20; index < 39; index += 2)
{
result.Append(Encoding.ASCII.GetString(sco.bBuffer, index + 1, 1));
result.Append(Encoding.ASCII.GetString(sco.bBuffer, index, 1));
}
return result.ToString();
}
return string.Empty;
}
}
private static Win32Exception CreateWin32Exception(Int32 errorCode, string context)
{
var win32Exception = new Win32Exception(errorCode);
win32Exception.Data["Context"] = context;
return win32Exception;
}
private static SafeFileHandle OpenDisk(int diskNumber)
{
SafeFileHandle hDevice = CreateFile(string.Format(@"\\.\PhysicalDrive{0}", diskNumber), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (!hDevice.IsInvalid)
return hDevice;
else
throw CreateWin32Exception(Marshal.GetLastWin32Error(), "CreateFile");
}
private static bool IsSmartSupported(SafeFileHandle hDisk)
{
uint iBytesReturned = 0;
var gvo = new GETVERSIONOUTPARAMS();
IntPtr pGVO = Marshal.AllocHGlobal(512);
if (DeviceIoControl(hDisk, SMART_GET_VERSION, IntPtr.Zero, 0, pGVO, 512, ref iBytesReturned, IntPtr.Zero))
return false;
return (gvo.fCapabilities & CAP_SMART_CMD) > 0;
}
}
}
Check out the pinvoke.net tutorial about DeviceIOcontrol.
Scroll down the page where you can see VB .NET 3.0 Full Example (Thanks to "bogdandaniel") Edited by pPumkiN. It is a complete example of accessing diffferent IO devices. I beleieve there is DRIVE_INFO too.
Also i dont have any experience with this. Try it yourself
这篇关于如何获得硬盘的SerialNumber在C#中(不WMI)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!