Xerix
Xerix

Reputation: 431

C# - Accessing Raw Sectors in high offset (over 4G) on external HDD

I am using in my program "Kernel32.dll" functionality to access raw disk sectors on WinXP SP3 OS (external HDD).

Everything works fine till the program reaches sector number 8388607 - which means the bytes offset in SetFilePointer exceeds 32 bit (uint!). But my code, as below, uses all variables as "long". What I am doing wrong?

The code (on "Dump" button click):

 int drive = DRV.SelectedIndex; // DRV is the drive combo box
 long bps = BytesPerSector(drive), spt = GetTotalSectors(drive);
 string dr = DRV.SelectedItem.ToString();
 int moveToHigh, read = 0;

 uint GENERIC_READ = 0x80000000;
 uint OPEN_EXISTING = 3;
 SafeFileHandle handleValue = CreateFile(dr, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
 if (handleValue.IsInvalid)
     Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

 // idx = Loop starting index
 // FS = The starting sector index 
 // TS = The final sector index 
 long idx = (FS == -1) ? 0 : FS, tot = (TS == -1) ? spt : TS;

 for ( ; idx < tot; idx++)
 {
      byte[] b = new byte[bps];

      // HERE IS THE ISSUE!!!
      SetFilePointer(handleValue, idx*bps), out moveToHigh, EMoveMethod.Current);

      if (ReadFile(handleValue, b, bps, out read, IntPtr.Zero) == 0)
          Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());



       if (this.IsDisposed == true) { handleValue.Close(); break; }
            Application.DoEvents();
  }
  handleValue.Close();

The kernel32.dll external functions:

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetFilePointer(
        [In] SafeFileHandle hFile,
        [In] long lDistanceToMove,
        [Out] out int lpDistanceToMoveHigh,
        [In] EMoveMethod dwMoveMethod);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
      uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
      uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32", SetLastError = true)]
internal extern static int ReadFile(SafeFileHandle handle, byte[] bytes,
       int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

I have tried many things, but no idea what is wrong, application just ending up with a fatal exception asking to send bug report

Thanks a lot

Upvotes: 1

Views: 158

Answers (1)

Sami Kuhmonen
Sami Kuhmonen

Reputation: 31143

Your P/Invoke definition is wrong. The function takes a 32bit value in but you defined it as a 64bit value. It won't work properly and definitely not past the value range of 32bit variables.

See the definition and example on how to use at pinvoke.net:

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SetFilePointer(IntPtr handle, int lDistanceToMove, out int lpDistanceToMoveHigh, uint dwMoveMethod);


int lo = (int)(offset & 0xffffffff);
int hi = (int)(offset >> 32);

lo = SetFilePointer(handle, lo, out hi, moveMethod);

So you need to split the 64bit value in two and provide both parts for the function.

Also don't use doubles for integers. You will get into trouble when the accuracy ends and there is no reason to use them.

Upvotes: 1

Related Questions