如何使用 .Net 直接读取磁盘?

Posted

技术标签:

【中文标题】如何使用 .Net 直接读取磁盘?【英文标题】:How do I read a disk directly with .Net? 【发布时间】:2010-09-07 11:32:57 【问题描述】:

是否可以直接使用 .Net 读取磁盘?直接我的意思是通过绕过文件系统的设备。我想我会通过以某种方式打开设备“\Device\Ide\IdeDeviceP2T0L0-1”来解决这个问题。

如果我无法使用 .NET api 打开设备,知道使用哪个 Win32 API 会很有帮助。

【问题讨论】:

男人。有了所有这些编组和东西,为什么不直接用 C 编写一个 dll 而放弃 .NET 你可以 p/invoke 你的 dll 并拥有更轻松的时间 【参考方案1】:

CreateFile 支持直接磁盘访问。阅读“物理磁盘和卷”下的注释。您应该能够 P/Invoke 调用。

请注意,Vista 和 Server 2008 有 severely restricted this。

【讨论】:

【参考方案2】:

酷,谢谢你,马克,我忘记了 CreateFile 也可以打开东西。我正在查看卷管理 API,但没有看到如何打开东西。

这是一个总结事情的小类。将 SafeFileHandle 传递到 FileStream 中也可能/正确。

using System;
using System.Runtime.InteropServices;
using System.IO;
using Microsoft.Win32.SafeHandles;

namespace ReadFromDevice

    public class DeviceStream : Stream, IDisposable
    
        public const short FILE_ATTRIBUTE_NORMAL = 0x80;
        public const short INVALID_HANDLE_VALUE = -1;
        public const uint GENERIC_READ = 0x80000000;
        public const uint GENERIC_WRITE = 0x40000000;
        public const uint CREATE_NEW = 1;
        public const uint CREATE_ALWAYS = 2;
        public const uint OPEN_EXISTING = 3;

        // Use interop to call the CreateFile function.
        // For more information about CreateFile,
        // see the unmanaged MSDN reference library.
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
          uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
          uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool ReadFile(
            IntPtr hFile,                        // handle to file
            byte[] lpBuffer,                // data buffer
            int nNumberOfBytesToRead,        // number of bytes to read
            ref int lpNumberOfBytesRead,    // number of bytes read
            IntPtr lpOverlapped
            //
            // ref OVERLAPPED lpOverlapped        // overlapped buffer
            );

        private SafeFileHandle handleValue = null;
        private FileStream _fs = null;

        public DeviceStream(string device)
        
            Load(device);
        

        private void Load(string Path)
        
            if (string.IsNullOrEmpty(Path))
            
                throw new ArgumentNullException("Path");
            

            // Try to open the file.
            IntPtr ptr = CreateFile(Path, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);

            handleValue = new SafeFileHandle(ptr, true);
            _fs = new FileStream(handleValue, FileAccess.Read);

            // If the handle is invalid,
            // get the last Win32 error 
            // and throw a Win32Exception.
            if (handleValue.IsInvalid)
            
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            
        

        public override bool CanRead
        
            get  return true; 
        

        public override bool CanSeek
        
            get  return false; 
        

        public override bool CanWrite
        
            get  return false; 
        

        public override void Flush()
        
            return;
        

        public override long Length
        
            get  return -1; 
        

        public override long Position
        
            get
            
                throw new NotImplementedException();
            
            set
            
                throw new NotImplementedException();
            
        
        /// <summary>
        /// </summary>
        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and 
        /// (offset + count - 1) replaced by the bytes read from the current source. </param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream. </param>
        /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
        /// <returns></returns>
        public override int Read(byte[] buffer, int offset, int count)
        
            int BytesRead =0;
            var BufBytes = new byte[count];
            if (!ReadFile(handleValue.DangerousGetHandle(), BufBytes, count, ref BytesRead, IntPtr.Zero))
            
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            
            for (int i = 0; i < BytesRead; i++)
            
                buffer[offset + i] = BufBytes[i];
            
            return BytesRead;
        
        public override int ReadByte()
        
            int BytesRead = 0;
            var lpBuffer = new byte[1];
            if (!ReadFile(
            handleValue.DangerousGetHandle(),                        // handle to file
            lpBuffer,                // data buffer
            1,        // number of bytes to read
            ref BytesRead,    // number of bytes read
            IntPtr.Zero
            ))
             Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); ;
            return lpBuffer[0];
        

        public override long Seek(long offset, SeekOrigin origin)
        
            throw new NotImplementedException();
        

        public override void SetLength(long value)
        
            throw new NotImplementedException();
        

        public override void Write(byte[] buffer, int offset, int count)
        
            throw new NotImplementedException();
        

        public override void Close()
        
            handleValue.Close();
            handleValue.Dispose();
            handleValue = null;
            base.Close();
        
        private bool disposed = false;

        new void Dispose()
        
            Dispose(true);
            base.Dispose();
            GC.SuppressFinalize(this);
        

        private new void Dispose(bool disposing)
        
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            
                if (disposing)
                
                    if (handleValue != null)
                    
                        _fs.Dispose();
                        handleValue.Close();
                        handleValue.Dispose();
                        handleValue = null;
                    
                
                // Note disposing has been done.
                disposed = true;

            
        

    

还有一个使用类的例子

static void Main(string[] args)
        
            var reader = new BinaryReader(new DeviceStream(@"\\.\PhysicalDrive3"));
            var writer = new BinaryWriter(new FileStream(@"g:\test.dat", FileMode.Create));
            var buffer = new byte[MB];
            int count;
            int loopcount=0;
            try
                while((count=reader.Read(buffer,0,MB))>0)
                
                    writer.Write(buffer,0,count);
                    System.Console.Write('.');
                    if(loopcount%100==0)
                    
                        System.Console.WriteLine();
                        System.Console.WriteLine("100MB written");
                        writer.Flush();
                    
                    loopcount++;
                
            
            catch(Exception e)
            
                Console.WriteLine(e.Message);
            
            reader.Close();
            writer.Flush();
            writer.Close();
        

标准免责声明适用,此代码可能对您的健康有害。

【讨论】:

【参考方案3】:

同意马克的回答。请注意,如果启用了用户帐户控制(这是 Windows Vista 及更高版本的默认设置),则您的程序必须以提升的权限运行(具有管理权限)。如果您的程序只供少数用户使用,您可以要求用户右键单击可执行文件并选择“以管理员身份运行”。否则,您可以将清单文件编译到程序中,并在清单中指定程序需要以提升的权限运行(搜索“requestedExecutionLevel requireAdministrator”以获取更多信息)。

【讨论】:

【参考方案4】:

在 .NET 5 中,您可以使用 FileStream 方法读取磁盘上的文件。

new FileStream(@"\\.\PhysicalDrive1", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)

【讨论】:

以上是关于如何使用 .Net 直接读取磁盘?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中清空/刷新 Windows READ 磁盘缓存?

使用C直接读/写磁盘[重复]

如何直接从磁盘复制锁定的文件并确保文件完好无损?

如何将 MJPEG 流保存到磁盘(C# .NET)?

如何使用 ATA 命令读取磁盘的特定扇区?

机械硬盘提示磁盘结构损坏且无法读取要如何找到文件