C# 如何知道两个分区是否位于一个物理硬盘中而没有WMI?
我有这些分区(在Windows中),例如:C# 如何知道两个分区是否位于一个物理硬盘中而没有WMI?,c#,windows,wmi,disk-partitioning,C#,Windows,Wmi,Disk Partitioning,我有这些分区(在Windows中),例如: Hard Disk 1 - Partition C, Partition D Hard Disk 2 - Partition E 在程序语言中,是否有任何方法可以知道例如分区C和分区D是否在一个物理硬盘中,而没有WMI 我不想使用WMI,因为它很慢-对于这个例子,我花了0.5秒。我需要它快点 谢谢。我不知道有任何其他托管方式可以获取磁盘分区信息。 您可以从C#使用P/Invoke来使用Win32 API。然而,除非绝对必要,否则你不应该这样做 您需要
Hard Disk 1 - Partition C, Partition D
Hard Disk 2 - Partition E
在程序语言中,是否有任何方法可以知道例如分区C和分区D是否在一个物理硬盘中,而没有WMI
我不想使用WMI,因为它很慢-对于这个例子,我花了0.5秒。我需要它快点
谢谢。我不知道有任何其他托管方式可以获取磁盘分区信息。 您可以从C#使用P/Invoke来使用Win32 API。然而,除非绝对必要,否则你不应该这样做 您需要的Win32函数称为DeviceIoControl()。API文档可在上找到。使用控制代码IOCTL\u STORAGE\u GET\u DEVICE\u NUMBER调用DeviceIoControl(),您将获得给定分区设备句柄的物理磁盘驱动器。可以使用CreateFile()API检索分区的设备句柄 但是,使用DeviceIoControl()很麻烦,您很可能需要为32位和64位版本的Windows制作不同的版本 要检索所有分区,可以使用托管代码System.IO.DriveInfo,如下所示:
var x = from di in DriveInfo.GetDrives()
where (di.DriveType == DriveType.Fixed)
select di;
foreach (DriveInfo di in x)
{
// Call DeviceIoControl() using the partition name from di.Name and the IOCTL_STORAGE_GET_DEVICE_NUMBER control code to retrieve the physical disk
}
pinvoke.net似乎有一些这段Delphi代码应该可以使用p/Invoke调用轻松地转换为C#,并完全按照您的要求执行。(还有一点)importand调用是对DeviceIOControl的
type
STORAGE_QUERY_TYPE = DWORD;
const
PropertyStandardQuery = 0; // Retrieves the descriptor
PropertyExistsQuery = 1; // Used to test whether the descriptor is supported
PropertyMaskQuery = 2; // Used to retrieve a mask of writeable fields in the descriptor
type
STORAGE_PROPERTY_ID = DWORD;
const
StorageDeviceProperty = 0;
// Query structure - additional parameters for specific queries can follow the header
type
STORAGE_PROPERTY_QUERY = packed record
PropertyId: STORAGE_PROPERTY_ID;
QueryType: STORAGE_QUERY_TYPE;
AdditionalParameters: Longword;
end;
const
FILE_DEVICE_MASS_STORAGE = $0000002d;
IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
FILE_ANY_ACCESS = 0;
METHOD_BUFFERED = 0;
IOCTL_STORAGE_QUERY_PROPERTY = ( IOCTL_STORAGE_BASE shl 16 ) or ( $500 shl 2 ) or METHOD_BUFFERED or ( FILE_ANY_ACCESS shl 14 );
type
STORAGE_BUS_TYPE = DWORD;
const
BusTypeUnknown = $00;
BusTypeScsi = $01;
BusTypeAtapi = $02;
BusTypeAta = $03;
BusType1394 = $04;
BusTypeSsa = $05;
BusTypeFibre = $06;
BusTypeUsb = $07;
BusTypeRAID = $08;
BusTypeiScsi = $09;
BusTypeSas = $0A;
BusTypeSata = $0B;
BusTypeSd = $0C;
BusTypeMmc = $0D;
BusTypeVirtual = $0E;
BusTypeFileBackedVirtual = $0F;
BusTypeMax = $10;
BusTypeMaxReserved = $7F;
type
STORAGE_DEVICE_DESCRIPTOR = packed record
// sizeof( STORAGE_DEVICE_DESCRIPTOR )
Version: DWORD;
// Total size of the descriptor, including the space for additional data and id strings
Size: DWORD;
// The SCSI-2 device type
DeviceType: BYTE;
// The SCSI-2 device type modifier (if any) - this may be zero
DeviceTypeModifier: BYTE;
// Flag indicating whether the device's media (if any) is removable. This field should be ignored for media-less devices
RemovableMedia: BOOLEAN;
// Flag indicating whether the device can support multiple outstanding commands.
// The actual synchronization in this case is the responsibility of the port driver.
CommandQueueing: BOOLEAN;
// Byte offset to the zero-terminated ascii string containing the device's vendor id string.
// For devices with no such ID this will be zero
VendorIdOffset: DWORD;
// Byte offset to the zero-terminated ascii string containing the device's product id string.
// For devices with no such ID this will be zero
ProductIdOffset: DWORD;
// Byte offset to the zero-terminated ascii string containing the device's product revision string.
// For devices with no such string this will be zero
ProductRevisionOffset: DWORD;
// Byte offset to the zero-terminated ascii string containing the device's serial number.
// For devices with no serial number this will be zero
SerialNumberOffset: DWORD;
// Contains the bus type (as defined above) of the device. It should be used to interpret the raw device
// properties at the end of this structure (if any)
BusType: STORAGE_BUS_TYPE;
// The number of bytes of bus-specific data which have been appended to this descriptor
RawPropertiesLength: DWORD;
// Place holder for the first byte of the bus specific property data
RawDeviceProperties: DWORD;
end;
PSTORAGE_DEVICE_DESCRIPTOR = ^STORAGE_DEVICE_DESCRIPTOR;
STORAGE_DEVICE_NUMBER = packed record
DeviceType: LONGWORD; // DEVICE_TYPE
DeviceNumber: ULONG;
PartitionNumber: ULONG;
end;
PSTORAGE_DEVICE_NUMBER = ^STORAGE_DEVICE_NUMBER;
const
IOCTL_STORAGE_GET_DEVICE_NUMBER = ( IOCTL_STORAGE_BASE shl 16 ) or ( $420 shl 2 ) or METHOD_BUFFERED or ( FILE_ANY_ACCESS shl 14 );
type
TDriveBusType = (
dbtUnknown,
dbtScsi,
dbtAtapi,
dbtAta,
dbt1394,
dbtSsa,
dbtFibre,
dbtUsb,
dbtRAID,
dbtiScsi,
dbtSas,
dbtSata,
dbtSd,
dbtMmc,
dbtVirtual,
dbtFileBackedVirtual );
TDeviceType = (
dtUnknown ); // todo: implement
TDriveInfoResult = record
//
Drive: string;
VendorID: string;
ProductID: string;
Revision: string;
Serial: string;
BusType: TDriveBusType;
Removable: Boolean;
//
DeviceType: TDeviceType;
DeviceNumber: Integer;
Partition: Integer;
end;
const
BusTypes: array [ TDriveBusType ] of AnsiString = (
'Unknown',
'Scsi',
'Atapi',
'Ata',
'1394',
'Ssa',
'Fibre',
'Usb',
'RAID',
'iScsi',
'Sas',
'Sata',
'Sd',
'Mmc',
'Virtual',
'FileBackedVirtual' );
function DriveInfo( const Drive: string ): TDriveInfoResult;
var
H: THandle;
N: Longword;
Query: STORAGE_PROPERTY_QUERY;
Buffer: array [ 0..1023 ] of Byte;
Desc: PSTORAGE_DEVICE_DESCRIPTOR;
S, X: AnsiString;
i: Integer;
Info: PSTORAGE_DEVICE_NUMBER;
begin
// Clear out old data
Result.Drive := Drive;
Result.VendorID := '';
Result.ProductID := '';
Result.Revision := '';
Result.Serial := '';
// Open drive for querying
H := CreateFile( PChar( '\\.\' + Drive ), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
if H = INVALID_HANDLE_VALUE then Exit;
try
// Query device.
FillChar( Query, sizeof( Query ), 0 );
Query.PropertyId := StorageDeviceProperty;
Query.QueryType := PropertyStandardQuery;
FillChar( Buffer, sizeof( Buffer ), 0 );
if not DeviceIoControl ( H, IOCTL_STORAGE_QUERY_PROPERTY, @Query, sizeof( Query ), @Buffer, sizeof( Buffer ), N, nil ) then Exit;
// Sanity checks.
if N < sizeof( STORAGE_DEVICE_DESCRIPTOR ) then Exit;
Desc := @Buffer;
if Desc^.Version < sizeof( STORAGE_DEVICE_DESCRIPTOR ) then Exit;
// And obtain result.
if Desc^.VendorIdOffset <> 0 then Result.VendorID := Trim( PAnsiChar( @Buffer[ Desc^.VendorIdOffset ] ) );
if Desc^.ProductIdOffset <> 0 then Result.ProductID := Trim( PAnsiChar( @Buffer[ Desc^.ProductIdOffset ] ) );
if Desc^.ProductRevisionOffset <> 0 then Result.Revision := Trim( PAnsiChar( @Buffer[ Desc^.ProductRevisionOffset ] ) );
if Desc^.SerialNumberOffset <> 0 then begin
// The serial number is encoded in HEX and with each two characters encoded swapped. ER ABCD -> BADC -> '42414443'
S := PAnsiChar( @Buffer[ Desc^.SerialNumberOffset ] );
X := '';
for i := 1 to Length( S ) do if S[ i ] in [ '0'..'9', 'A'..'F', 'a'..'f' ] then X := X + S[ i ];
S := '';
SetLength( S, Length( X ) div 2 );
// i = 1,2,3,4,5,6 -> 3,1,7,5,11,9
for i := 1 to Length( S ) do S[ i ] := AnsiChar( StrToInt( '$' + Copy( X, 1 + ( ( ( i - 1 ) div 2 ) * 4 ) + 2 * ( i and 1 ), 2 ) ) );
Result.Serial := Trim( S );
end;
if Desc^.BusType <= Longword( High( TDriveBusType ) ) then Result.BusType := TDriveBusType( Desc^.BusType );
Result.Removable := Desc^.RemovableMedia;
System.FillChar( Buffer, sizeof( Buffer ), 0 );
if DeviceIoControl ( H, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @Buffer, sizeof( Buffer ), N, nil ) then begin
Info := @Buffer;
Result.DeviceType := dtUnknown;
Result.DeviceNumber := Integer( Info^.DeviceNumber );
Result.Partition := Integer( Info^.PartitionNumber );
end;
finally
CloseHandle( H );
end;
end;
function GetLogicalDrives(): TStringDynArray;
var
Buffer: array [ 0..1023 ] of Char;
N, i: Integer;
begin
SetLength( Result, 0 );
N := GetLogicalDriveStrings( High( Buffer ), @Buffer );
if N >= Length( Buffer ) then raise Exception.Create( 'Oops' );
i := 0;
while ( i <= N ) and ( Buffer[ i ] <> #0 ) do begin
SetLength( Result, Length( Result ) + 1 );
Result[ High( Result ) ] := PChar( @( Buffer[ i ] ) );
Inc( i, Length( Result[ High( Result ) ] ) + 1 );
end;
end;
function RemoveTrailingPathDelimiter( const Path: string ): string;
begin
if ( Length( Path ) = 0 ) or ( Path[ Length( Path ) ] <> PathDelim ) then Result := Path else Result := Copy( Path, 1, Length( Path ) - 1 );
end;
procedure TForm7.Button1Click( Sender: TObject );
var
Drives: TStringDynArray;
Drive: string;
Res: TDriveInfoResult;
begin
Memo1.Lines.BeginUpdate();
try
Memo1.Lines.Clear();
Drives := GetLogicalDrives();
for Drive in Drives do begin
Res := DriveInfo( RemoveTrailingPathDelimiter( Drive ) );
Memo1.Lines.Add( 'DRIVE: ' + Drive );
Memo1.Lines.Add( 'VendorID = ' + Res.VendorID );
Memo1.Lines.Add( 'ProductID = ' + Res.ProductID );
Memo1.Lines.Add( 'Revision = ' + Res.Revision );
Memo1.Lines.Add( 'Serial = ' + Res.Serial );
Memo1.Lines.Add( 'BusType = ' + BusTypes[ Res.BusType ] );
Memo1.Lines.Add( 'Removable = ' + IntToStr( Ord( Res.Removable ) ) );
// device type.
Memo1.Lines.Add( 'Device = ' + IntToStr( Res.DeviceNumber ) );
Memo1.Lines.Add( 'Partition = ' + IntToStr( Res.Partition ) );
Memo1.Lines.Add( '' );
end;
finally
Memo1.Lines.EndUpdate();
end;
end;
类型
存储\查询\类型=DWORD;
常数
PropertyStandardQuery=0;//检索描述符
PropertyExistsQuery=1;//用于测试描述符是否受支持
PropertyMaskQuery=2;//用于检索描述符中可写字段的掩码
类型
存储属性ID=DWORD;
常数
StorageDeviceProperty=0;
//查询结构-特定查询的其他参数可以在标题后面
类型
存储\属性\查询=打包记录
PropertyId:存储\属性\ ID;
查询类型:存储\查询\类型;
其他参数:长单词;
结束;
常数
文件设备海量存储=0000002d;
IOCTL\u STORAGE\u BASE=文件\u设备\u海量存储;
文件访问权限=0;
方法_缓冲=0;
IOCTL\ U存储\查询\属性=(IOCTL\ U存储\基础shl 16)或($500 shl 2)或缓冲的方法\或(文件\任何\访问shl 14);
类型
存储总线类型=DWORD;
常数
BusTypeUnknown=$00;
bustypesscsi=$01;
BusTypeAtapi=$02;
BusTypeAta=$03;
BusType1394=$04;
BusTypeSsa=$05;
BusTypeFibre=$06;
BusTypeUsb=$07;
BusTypeRAID=$08;
bustypescsi=$09;
BusTypeSas=$0A;
BustypeSta=0亿美元;
BusTypeSd=$0C;
BustypeMc=$0D;
BusTypeVirtual=$0E;
BusTypeFileBackedVirtual=$0F;
BusTypeMax=10美元;
BusTypeMaxReserved=$7F;
类型
存储\设备\描述符=打包记录
//sizeof(存储设备描述符)
版本:德沃德;
//描述符的总大小,包括用于其他数据和id字符串的空间
尺寸:德沃德;
//SCSI-2设备类型
设备类型:字节;
//SCSI-2设备类型修饰符(如果有)-可能为零
DeviceTypeModifier:字节;
//指示设备的媒体(如果有)是否可移动的标志。对于无介质设备,应忽略此字段
移除媒体:布尔;
//指示设备是否支持多个未完成命令的标志。
//在这种情况下,端口驱动程序负责实际的同步。
命令排队:布尔;
//包含设备供应商id字符串的以零结尾的ascii字符串的字节偏移量。
//对于没有此类ID的设备,这将是零
卖方报价:德沃德;
//包含设备产品id字符串的以零结尾的ascii字符串的字节偏移量。
//对于没有此类ID的设备,这将是零
产品偏移量:DWORD;
//包含设备产品修订字符串的以零结尾的ascii字符串的字节偏移量。
//对于没有此类字符串的设备,这将是零
ProductRevisionOffset:DWORD;
//包含设备序列号的以零结尾的ascii字符串的字节偏移量。
//对于没有序列号的设备,这将是零
serialnumberofset:DWORD;
//包含设备的总线类型(如上所述)。它应该用来解释原始设备
//此结构末尾的属性(如果有)
BusType:存储\总线\类型;
//附加到此描述符的总线特定数据的字节数
原始属性长度:DWORD;
//总线特定属性数据的第一个字节的占位符
RawDeviceProperties:DWORD;
结束;
p存储设备描述符=^存储设备描述符;
存储设备编号=打包记录
设备类型:LONGWORD;//设备类型
设备编号:ULONG;
分区编号:乌龙;
结束;
p存储设备编号=^存储设备编号;
常数
IOCTL\ U存储\获取\设备\编号=(IOCTL\ U存储\基本shl 16)或($420 shl 2)或缓冲的方法\或(文件\任何\访问shl 14);
类型
TDriveBustType=(
众所周知,
dbtScsi,
dbtAtapi,
德塔塔,
dbt1394,
dbtSsa,
dbtFibre,
dbtUsb,
dbtRAID,