Проекты

DeviceIoControl Plugin

Копаясь в папке с проектами на C++, откопал исходный код приложения Network Administrator. Основной смысл приложения был в отправке S.M.A.R.T. данных клиенту с большого кол-ва серверов через WinSock. Автор чтения SMART'ов является Andrew I. Reshin, написавшим в 2001 году приложение с исходником на C++. В то время, код управления получения параметров ещё не был задокументирован в MSDN'е и вызывался как:

#define DFP_RECEIVE_DRIVE_DATA	0x0007c088

Ради спортивного интереса, пришла в голову идея портировать код на .NET. Использовать unsafe код было не интересно, так что всё написано на управляемом коде. Сейчас плагин объеденён через ILMerge со сборкой, исходный код которой размещён на GitHub.

UI

Плагин доступен в меню Tools→WinAPI→Device Info. При запуске сканирует устройства через WinAPI метод GetLogicalDriveStrings. Автоматическое определение подключения или отключения устройств пока не реализовано. При выборе устройства выводит следующие параметры:

  • General
    • Power
    • Capabilities
    • Channel
  • Geometry
    • Media type
    • Capacity
    • Cylinders
    • Tracks per cylinder
    • Sectors per track
    • Bytes per sector
  • SMART
    • Type
    • Serial Number
    • Firmware Version
    • Model Number
    • Capabilities
    • Total Addressable sectors
    • Cylinders
    • Number of heads
    • Current number of cylinders
    • Current number of heads
    • Current sectors per track
    • Current sector capacity

DeviceInfo internals

Самой интересной частью сборки является вызов native метода DeviceIoControl из kernel32.dll. Для вызова использованных в сборке кодов управления (IOCTRL) было интересно написать универсальный метод, который соответствует всем обёрнутым кодам. Метод был-бы значительно проще, если-бы не коды проверки наличия кода в определённой версии ОС. Для них пришлось сделать метод с возвратом выполнения HRESULT'а. Для большинства остальных кодов достаточно выбросить Win32ErrorException, если HRESULT вернул 0.

* Для совместимости с CLS необходимо заменить UInt32 на Int32 и UInt16 на Int16.

[DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
private static extern Boolean DeviceIoControl(
	IntPtr hDevice,
	UInt32 dwIoControlCode,
	IntPtr lpInBuffer,
	UInt32 nInBufferSize,
	IntPtr lpOutBuffer,
	UInt32 nOutBufferSize,
	ref UInt32 lpBytesReturned,
	[In] ref NativeOverlapped lpOverlapped);

public static Boolean DeviceIoControl(
	IntPtr hDevice,
	UInt32 dwIoControlCode,
	Object inParams,
	out UInt32 lpBytesReturned,
	out T outBuffer) where T : struct
{
	Int32 nInBufferSize = 0;
	IntPtr lpInBuffer = IntPtr.Zero;
	IntPtr lpOutBuffer = IntPtr.Zero;

	try
	{
		if(inParams != null)
		{
			nInBufferSize = Marshal.SizeOf(inParams);
			lpInBuffer = Marshal.AllocHGlobal(nInBufferSize);
			Marshal.StructureToPtr(inParams, lpInBuffer, true);
		}

		outBuffer = new T();
		UInt32 nOutBufferSize = Marshal.SizeOf(typeof(T));

		lpOutBuffer = Marshal.AllocHGlobal(nOutBufferSize);
		Marshal.StructureToPtr(outBuffer, lpOutBuffer, true);

		lpBytesReturned = 0;
		NativeOverlapped lpOverlapped = new NativeOverlapped();

		Boolean result = Methods.DeviceIoControl(
			hDevice,
			dwIoControlCode,
			lpInBuffer,
			(UInt32)nInBufferSize,
			lpOutBuffer,
			(UInt32)nOutBufferSize,
			ref lpBytesReturned,
			ref lpOverlapped);
		//if(result) В некоторых случаях даже при отрицательном значении необходимо читать исходящий буфер
		outBuffer = (T)Marshal.PtrToStructure(lpOutBuffer, typeof(T));

		return result;
	} finally
	{
		if(lpInBuffer != IntPtr.Zero)
			Marshal.FreeHGlobal(lpInBuffer);
		if(lpOutBuffer != IntPtr.Zero)
			Marshal.FreeHGlobal(lpOutBuffer);
	}
}

internal static T DeviceIoControl(
	IntPtr hDevice,
	UInt32 dwIoControlCode,
	Object inParams,
	out UInt32 lpBytesReturned) where T : struct
{
	T result;
	if(Methods.DeviceIoControl(
		hDevice,
		dwIoControlCode,
		inParams,
		out lpBytesReturned,
		out result))
		return result;
	else
		throw new Win32Exception();
}

И чтобы не хардкодить все коды управления, то приведу вид препроцессорного макроса CTL_CODE. В C++ он разворачивается во время компиляции, а в .NET'е выполняется как статичный метод класса.

private static UInt32 CTL_CODE(UInt16 deviceType, UInt16 function, WinAPI.FILE_ACCESS access)
{
	return CTL_CODE(deviceType, function, WinAPI.METHOD.BUFFERED, access);
}

private static UInt32 CTL_CODE(UInt16 deviceType, UInt16 function, WinAPI.METHOD method, WinAPI.FILE_ACCESS access)
{
	return (UInt32)((deviceType << 16) | ((UInt16)access << 14) | (function << 2) | (Byte)method);
}

На момент написания статьи поддерживаются следующие коды управления:

  • IOCTL_VOLUME — Volume control
    • GET_VOLUME_DISK_EXTENTS — Retrieves the physical location of a specified volume on one or more disks.
    • IS_CLUSTERED — Determines whether the specified volume is clustered.
  • IOCTL_DISC — Disc control
    • PERFORMANCE — Increments a reference counter that enables the collection of disk performance statistics, such as the numbers of bytes read and written since the driver last processed this request, for a corresponding disk monitoring application.
    • IS_WRITABLE — Determines whether the specified disk is writable.
    • PERFORMANCE_OFF — Disables the counters that were enabled by previous calls to IOCTL_DISK_PERFORMANCE. This request is available in Windows XP and later operating systems. Caller must be running at IRQL = PASSIVE_LEVEL.
    • SMART_GET_VERSION — Returns version information, a capabilities mask, and a bitmask for the device.
    • SMART_SEND_DRIVE_COMMAND — Sends one of the following Self-Monitoring Analysis and Reporting Technology (SMART) commands to the device.
    • SMART_RCV_DRIVE_DATA — Returns the ATA-2 identify data, the Self-Monitoring Analysis and Reporting Technology (SMART) thresholds, or the SMART attributes for the device.
    • GET_DRIVE_GEOMETRY_EX — Returns information about the physical disk's geometry (media type, number of cylinders, tracks per cylinder, sectors per track, and bytes per sector).
  • IOCTL_STORAGE — Storage control
    • CHECK_VERIFY — Determines whether the media has changed on a removable-media device that the caller has opened for read or write access.
    • CHECK_VERIFY2 — Determines whether the media has changed on a removable-media device - the caller has opened with FILE_READ_ATTRIBUTES.
    • MEDIA_REMOVAL — Enables or disables the mechanism that ejects media, for those devices possessing that locking capability.
    • EJECT_MEDIA — Causes the device to eject the media if the device supports ejection capabilities.
    • GET_MEDIA_TYPES_EX — Retrieves information about the types of media supported by a device.
    • GET_MEDIA_SERIAL_NUMBER — Retrieves the serial number of a USB device.
    • GET_HOTPLUG_INFO — Retrieves the hotplug configuration of the specified device.
    • GET_DEVICE_NUMBER — Retrieves the device type, device number, and, for a partitionable device, the partition number of a device.
    • PREDICT_FAILURE — Polls for a prediction of device failure.
    • QUERY_PROPERTY — Returns properties of a storage device or adapter. The request indicates the kind of information to retrieve, such as inquiry data for a device or capabilities and limitations of an adapter.
  • FSCTL — File System control
    • LOCK_VOLUME — Locks a volume if it is not in use.
    • UNLOCK_VOLUME — Unlocks a volume.
    • DISMOUNT_VOLUME — Dismounts a volume regardless of whether or not the volume is currently in use.
    • IS_VOLUME_MOUNTED — Determines whether the specified volume is mounted, or if the specified file or directory is on a mounted volume.
    • FILESYSTEM_GET_STATISTICS — Retrieves the information from various file system performance counters.
    • GET_NTFS_VOLUME_DATA — Retrieves information about the specified NTFS file system volume.
    • GET_VOLUME_BITMAP — Retrieves a bitmap of occupied and available clusters on a volume.
Теги:

Скачать

Original
  • 6 апреля 2013 г.
    Автор оригинального кода на C++ является Andrew I. Reshin он был адаптирован под MFC и STL и использован в приложении Network Administrator, а затем в DeviceInfo приложении.

Ссылки

Родительские файлы