0.0.1/classes/Win32DiskAccess.cs
using System;
using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace Posh.DiskWriter { public enum EMoveMethod : int { Begin = 0, Current = 1, End = 2 } [StructLayout(LayoutKind.Sequential)] internal struct DiskGeometry { public long Cylinders; public int MediaType; public int TracksPerCylinder; public int SectorsPerTrack; public int BytesPerSector; } [StructLayout(LayoutKind.Sequential)] internal struct DiskGeometryEx { public DiskGeometry Geometry; public long DiskSize; public byte Data; } [StructLayout(LayoutKind.Sequential)] internal struct DISK_EXTENT { public int DiskNumber; public ulong StartingOffset; public ulong ExtentLength; } [StructLayout(LayoutKind.Sequential)] internal struct VolumeDiskExtents { public uint NumberOfDiskExtents; public DISK_EXTENT DiskExtent1; } public static class NativeMethods { internal const uint OPEN_EXISTING = 3; internal const uint GENERIC_WRITE = (0x40000000); internal const uint GENERIC_READ = 0x80000000; internal const uint FSCTL_LOCK_VOLUME = 0x00090018; internal const uint FSCTL_UNLOCK_VOLUME = 0x0009001c; internal const uint FSCTL_DISMOUNT_VOLUME = 0x00090020; internal const uint FILE_SHARE_READ = 0x1; internal const uint FILE_SHARE_WRITE = 0x2; internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x70000; internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = 0x700a0; internal const uint IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000; internal const uint IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080; internal const uint BCM_SETSHIELD = 0x160C; internal const int INVALID_SET_FILE_POINTER = -1; [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern internal IntPtr LoadIcon(IntPtr hInstance, string lpIconName); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern internal IntPtr LoadLibrary(string lpFileName); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern internal int SetFilePointer([In] SafeFileHandle hFile, [In] int lDistanceToMove, ref int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern internal SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32", SetLastError = true)] static extern internal int ReadFile(SafeFileHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero); [DllImport("kernel32.dll", SetLastError = true)] static extern internal int WriteFile(SafeFileHandle handle, byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr overlapped_MustBeZero); [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] static extern internal bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped); [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)] public static extern bool DeviceIoControl(SafeFileHandle device, uint dwIoControlCode, IntPtr inBuffer, uint inBufferSize, IntPtr outBuffer, uint outBufferSize, ref uint bytesReturned, IntPtr overlapped); [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] static extern internal bool CloseHandle(SafeFileHandle handle); [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] static extern int SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam); } public class Win32DiskAccess { #region Fields SafeFileHandle _partitionHandle = null; SafeFileHandle _diskHandle = null; #endregion #region IDiskAccess Members //public event EventHandler OnDiskChanged; public int Open(string drivePath) { int intOut; // // Now that we've dismounted the logical volume mounted on the removable drive we can open up the physical disk to write // var diskHandle = NativeMethods.CreateFile(drivePath, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero); if (diskHandle.IsInvalid) { //LogMsg(@"Failed to open device: " + Marshal.GetHRForLastWin32Error()); return -1; } var success = NativeMethods.DeviceIoControl(diskHandle, NativeMethods.FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero); if (!success) { //LogMsg(@"Failed to lock device"); diskHandle.Dispose(); return -1; } _diskHandle = diskHandle; return 0; } public bool LockDrive(string drivePath) { bool success; int intOut; SafeFileHandle partitionHandle; // // Unmount partition (Todo: Note that we currently only handle unmounting of one partition, which is the usual case for SD Cards) // // // Open the volume /// partitionHandle = NativeMethods.CreateFile(@"\\.\" + drivePath, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero); if (partitionHandle.IsInvalid) { //LogMsg(@"Failed to open device"); partitionHandle.Dispose(); return false; } // // Lock it // success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero); if (!success) { //LogMsg(@"Failed to lock device"); partitionHandle.Dispose(); return false; } // // Dismount it // success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero); if (!success) { //LogMsg(@"Error dismounting volume: " + Marshal.GetHRForLastWin32Error()); NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero); partitionHandle.Dispose(); return false; } _partitionHandle = partitionHandle; return true; } public void UnlockDrive() { if(_partitionHandle != null) { _partitionHandle.Dispose(); _partitionHandle = null; } } public int Read(byte[] buffer, int readMaxLength, out int readBytes) { readBytes = 0; if(_diskHandle == null) return -1; return NativeMethods.ReadFile(_diskHandle, buffer, readMaxLength, out readBytes, IntPtr.Zero); } public int Write(byte[] buffer, int bytesToWrite, out int wroteBytes) { wroteBytes = 0; if(_diskHandle == null) return -1; return NativeMethods.WriteFile(_diskHandle, buffer, bytesToWrite, out wroteBytes, IntPtr.Zero); } public void Close() { if (_diskHandle != null) { _diskHandle.Dispose(); _diskHandle = null; } } public string GetPhysicalPathForLogicalPath(string logicalPath) { var diskIndex = -1; // // Now that we've dismounted the logical volume mounted on the removable drive we can open up the physical disk to write // var diskHandle = NativeMethods.CreateFile(@"\\.\" + logicalPath, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero); if (diskHandle.IsInvalid) { //LogMsg(@"Failed to open device: " + Marshal.GetHRForLastWin32Error()); return null; } var vdeSize = Marshal.SizeOf(typeof(VolumeDiskExtents)); var vdeBlob = Marshal.AllocHGlobal(vdeSize); uint numBytesRead = 0; var success = NativeMethods.DeviceIoControl(diskHandle, NativeMethods.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, vdeBlob, (uint)vdeSize, ref numBytesRead, IntPtr.Zero); var vde = (VolumeDiskExtents)Marshal.PtrToStructure(vdeBlob, typeof(VolumeDiskExtents)); if (success) { if (vde.NumberOfDiskExtents == 1) diskIndex = vde.DiskExtent1.DiskNumber; } else { //LogMsg(@"Failed get physical disk: " + Marshal.GetHRForLastWin32Error()); } Marshal.FreeHGlobal(vdeBlob); diskHandle.Dispose(); var path = ""; if(diskIndex >= 0) path = @"\\.\PhysicalDrive" + diskIndex; return path; } public long GetDriveSize(string drivePath) { // // Now that we've dismounted the logical volume mounted on the removable drive we can open up the physical disk to write // var diskHandle = NativeMethods.CreateFile(drivePath, NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero); if (diskHandle.IsInvalid) { //LogMsg( @"Failed to open device: " + Marshal.GetHRForLastWin32Error()); return -2; } // // Get drive size (NOTE: that WMI and IOCTL_DISK_GET_DRIVE_GEOMETRY don't give us the right value so we do it this way) // long size = -1; var geometrySize = Marshal.SizeOf(typeof(DiskGeometryEx)); var geometryBlob = Marshal.AllocHGlobal(geometrySize); uint numBytesRead = 0; var success = NativeMethods.DeviceIoControl(diskHandle, NativeMethods.IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IntPtr.Zero, 0, geometryBlob, (uint)geometrySize, ref numBytesRead, IntPtr.Zero); var geometry = (DiskGeometryEx)Marshal.PtrToStructure(geometryBlob, typeof(DiskGeometryEx)); if (success) size = geometry.DiskSize; Marshal.FreeHGlobal(geometryBlob); diskHandle.Dispose(); return size; } #endregion private void Progress(int progressValue) { //if (OnProgress != null) //OnProgress(this, progressValue); } private void LogMsg(string msg) { //if (OnLogMsg != null) //OnLogMsg(this, msg); } } } |