Search code examples
c#pinvoke

SetFilePointer on C# suddenly stop work


Background:
In my project I need to scan my HDD sector by sector. I am using Pinvoke of Kernel32.dll. The size of the HDD is 160GB (nearly 312,000,000 LBA). The loop scans every single iteration 8000 sectors.

The problem:
Somehow, after scanning 40000 sectors successfuly, the loop stacks without moving, and I even cannot terminate the application unless I will disconnect the HDD (is external media). I know that my media doesn't have any corrupted sectors.

When setting the file pointer using SetFilePointer of Kernel32.dll, I pay attention to Low and High ints for offset, but the offset even not reaching 1GB, so I guess that there is nothing to do in this point, but somewhere else (I think so, but I am not sure, I am quite new to this Pinvoke).

This is my following code:

    public enum EMoveMethod : uint
    {
        Begin = 0,
        Current = 1,
        End = 2
    }

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern int SetFilePointer(
        [In] SafeFileHandle hFile,
        [In] int lDistanceToMove,
        [In, Out] ref 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.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal extern static int ReadFile(SafeFileHandle handle, byte[] bytes,
       int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    static public int BytesPerSector(int drive)
    {
        int driveCounter = 0;
        try
        {
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskDrive");
            foreach (ManagementObject queryObj in searcher.Get())
            {
                if (driveCounter == drive)
                {
                    var t = queryObj["BytesPerSector"];
                    return int.Parse(t.ToString());
                }
                driveCounter++;
            }
        }
        catch (ManagementException) { return -1; }
        return 0;
    }

    static public int GetTotalSectors(int drive)
    {
        int driveCount = 0;
        try
        {
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskDrive");
            foreach (ManagementObject queryObj in searcher.Get())
            {
                if (driveCount == drive)
                {
                    var t = queryObj["TotalSectors"];
                    return int.Parse(t.ToString());
                }
                driveCount++;
            }
        }
        catch (ManagementException) { return -1; }
        return -1;
    }

    static private byte[] DumpSector(string drive, int sector, int bytesPerSector)
    {
        const uint GENERIC_READ = 0x80000000;
        const uint OPEN_EXISTING = 3;

        byte[] buf = null;
        try
        {
            SafeFileHandle handleValue = CreateFile(drive, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
            if (handleValue.IsInvalid) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); }
            long sec = (long)sector * (long)bytesPerSector;
            buf = new byte[bytesPerSector];
            int read = 0, moveToHigh = (int)(sec >> 32);
            int Res=SetFilePointer(handleValue, (int)(sec & 0xffffffff), ref moveToHigh, EMoveMethod.Begin);
            if (Res == -1) { Console.WriteLine("ERROR: ");  return null; }
            if (ReadFile(handleValue, buf, bytesPerSector, out read, IntPtr.Zero)==0)
            {
                Console.WriteLine("ERROR: "); return null; 
            }
            handleValue.Close();
        }
        catch (Exception Ex) { Console.WriteLine("ERROR: {0}", Ex.Message); return null; }
        return buf;
    }

    static void Scanner()
    {
        if (DRV == -1) { Console.WriteLine("ERROR: Please select drive using <A>+ drive index number."); return; } // error
        const int BFB = 8000;
        byte[] b = DumpSector(HDDs[DRV], MyOffset, BlockSize * BFB);
        int Sec16 = 0, IntOff = 0, JMP = 0;
        string DMP = "";
        long FF = ((long)MyOffset * BlockSize) + (IntOff * 16);
        Console.Write("0x{0}   ", FF.ToString("X10"));
        for (int byt = 0; byt < b.Length; byt++)
        {
            DMP += (char)b[byt];
            Console.Write("{0} ", b[byt].ToString("X2"));
            Sec16++; FF++;
            if (Sec16 == 8) Console.Write("  ");
            if (Sec16 == 16)
            {
                Console.Write("  {0}", DMP.Replace("\x07", "").Replace("\x08", "").Replace("\x0a", "").Replace("\x0d", "").Replace("\x09", "")); Console.WriteLine();
                DMP = ""; Sec16 = 0; IntOff++; JMP++;
                if (JMP == 32) { JMP = 0; IntOff += 224; FF += 3584; byt += 3584; }
                Console.Write("0x{0}   ", FF.ToString("X10"));
            }
        }
        Console.WriteLine("- End of scan -");
    }

The program is Console application in order to speedup the process.


Solution

  • Even if you had misused some API your program would not just have stopped. THis is unlikely to be an API usage error.

    This is probably a hardware error. Non-killable processes are a typical symptom. A process can only be killed by Windows when the last pending IO is completed. The disk driver is waiting to complete your IO but the disk never responds (or only after a long timeout).