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.
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).
Related
I got code from here
The problem is that no matter what sector number I send the function, it always dumps the first sector
DumpSector(string drive, double sector, int bytesPerSector) always returns first disk sector
drive = "\\.\PHYSICALDRIVE1"
sector = from 0 to totalSectors
bytesPerSector = 512
I am sorry for huge amount of code.
class LowReader
{
#region "WMI LOW LEVEL COMMANDS"
public static 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;
}
public ArrayList GetDriveList()
{
ArrayList drivelist = new ArrayList();
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject queryObj in searcher.Get())
{
drivelist.Add(queryObj["DeviceID"].ToString());
}
}
catch (ManagementException)
{
return null;
}
return drivelist;
}
public static long 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 long.Parse(t.ToString());
}
driveCount++;
}
}
catch (ManagementException)
{
return -1;
}
return -1;
}
public static int GetSectorsPerTrack(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["SectorsPerTrack"];
return int.Parse(t.ToString());
}
driveCount++;
}
}
catch (ManagementException)
{
return -1;
}
return -1;
}
public static int GetTotalTracks(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["TotalTracks"];
return int.Parse((t.ToString()));
}
DriveCount++;
}
}
catch (ManagementException)
{
return -1;
}
return -1;
}
#endregion
#region "API CALLS"
public enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.ThisCall)]
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);
#endregion
public byte[] DumpSector(string drive, double sector, int bytesPerSector)
{
short FILE_ATTRIBUTE_NORMAL = 0x80;
short INVALID_HANDLE_VALUE = -1;
uint GENERIC_READ = 0x80000000;
uint GENERIC_WRITE = 0x40000000;
uint CREATE_NEW = 1;
uint CREATE_ALWAYS = 2;
uint OPEN_EXISTING = 3;
SafeFileHandle handleValue = CreateFile(drive, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (handleValue.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
double sec = sector * bytesPerSector;
int size = int.Parse(bytesPerSector.ToString());
byte[] buf = new byte[size];
int read = 0;
int moveToHigh;
SetFilePointer(handleValue, long.Parse(sec.ToString()), out moveToHigh, EMoveMethod.Begin);
ReadFile(handleValue, buf, size, out read, IntPtr.Zero);
handleValue.Close();
return buf;
}
private byte[] DumpTrack(string drive, double track, int bytesPerTrack, int TrackBufferSize)
{
short FILE_ATTRIBUTE_NORMAL = 0x80;
short INVALID_HANDLE_VALUE = -1;
uint GENERIC_READ = 0x80000000;
uint GENERIC_WRITE = 0x40000000;
uint CREATE_NEW = 1;
uint CREATE_ALWAYS = 2;
uint OPEN_EXISTING = 3;
SafeFileHandle handleValue = CreateFile(drive, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (handleValue.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
double trx = (track * bytesPerTrack * TrackBufferSize);
int size = int.Parse(bytesPerTrack.ToString());
byte[] buf = new byte[size * TrackBufferSize];
int read = 0;
int moveToHigh;
SetFilePointer(handleValue, long.Parse(trx.ToString()), out moveToHigh, EMoveMethod.Begin);
ReadFile(handleValue, buf, size, out read, IntPtr.Zero);
handleValue.Close();
return buf;
}
}
As #HansPassant said
"The SetFilePointer() declaration is quite wrong. Wrong in the original code but made worse in the snippet, looks like it was randomly hacked until it stopped generating the MDA warning. Always mention such a thing when you ask for help. Favor SetFilePointerEx. "
I changed this
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.ThisCall)]
static extern uint SetFilePointer(
[In] SafeFileHandle hFile,
[In] long lDistanceToMove,
[Out] out int lpDistanceToMoveHigh,
[In] EMoveMethod dwMoveMethod);
to this
[DllImport("kernel32.dll")]
public static extern bool SetFilePointerEx(
SafeFileHandle hFile,
long liDistanceToMove,
out long lpNewFilePointer,
uint dwMoveMethod);
and I refactored usages in code.
We suddenly have problems with the smart card api on some windows installations.
There seem to be a memory leak while calling the SCardEstablishContext function.
The problem can be reproduced in a console application with the code sample available at
http://www.pinvoke.net/default.aspx/winscard.scardestablishcontext
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
while (true)
{
SmartCardInserted();
System.Threading.Thread.Sleep(10);
}
}
internal static bool SmartCardInserted()
{
bool cardInserted = false;
IntPtr hContext = IntPtr.Zero;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
finally
{
SCardReleaseContext(hContext);
}
return cardInserted;
}
}
To test, we call the method SmartCardInserted() in an infinite loop with a small delay => the memory grows constantly and new hadles are allocated.
We see this problem on systems runing Windows 10 or Windows Server 2012, but not on Windows Server 2008.
Any ideas are greatly appreciated!
The problem seems to have been released with v1709 of Windows 10. The shortest amount of code to reproduce the bug is
while(true) {
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
SCardReleaseContext(hContext);
}
It leaks ~264 bytes of memory each time a context is established and released.
If you maintain hContext outside of the loop and only create a context if it's IntPtr.Zero you should be able to avoid the leak. Then when you call SCardListReaders, check to see if you get SCARD_E_INVALID_HANDLE back and invalidate your hContext.
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
IntPtr hContext = IntPtr.Zero;
while (true)
{
SmartCardInserted(hContext);
System.Threading.Thread.Sleep(10);
}
SCardReleaseContext(hContext);
}
internal static bool SmartCardInserted(IntPtr hContext)
{
bool cardInserted = false;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
if(hContext == IntPtr.Zero)
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
if(ret == 0x80100003) // SCARD_E_INVALID_HANDLE = 0x80100003, // The supplied handle was invalid
{
try
{
SCardReleaseContext(hContext);
}
catch {}
finally
{
hContext = IntPtr.Zero;
}
return false;
}
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
return cardInserted;
}
}
It's a workaround until the Winscard.dll API is fixed.
I mean how To find a section was allocated by another program in another process fastly, and it's size is (8 Bytes) so when i use this code :
public static uint FindSignature(IntPtr hProcess, byte[] sigBuffer)
{
byte[] lpBuffer = new byte[524288];
uint lpNumberOfBytesRead = 0;
uint CurAddr = 0x00000000;
bool Result = false;
Result = ReadProcessMemory(hProcess, CurAddr, lpBuffer, 524288, out lpNumberOfBytesRead);
while (CurAddr + 524288 <= 0x7FFFFFFF)
{
if (Result != false)
{
int Index = ByteSearch(lpBuffer, sigBuffer);
if (Index != -1)
{
return (uint)(CurAddr + Index);
}
}
CurAddr += (uint)(524288 - sigBuffer.Length);
Result = ReadProcessMemory(hProcess, CurAddr, lpBuffer, 524288, out lpNumberOfBytesRead);
}
return 0;
}
I can't find it because i read 512 KB at the same time so it returns false.
And thanks.
How do I programatically change Volume Serial of a Fat32 partition from C#. I found this example, but it is written with C++ which I don't read well. Could someone please answer a C# code snippet?
Update:
I can see the C++ function from above example which I think it's possible to direct port to C#
void CVolumeSerialDlg::ChangeSerialNumber(DWORD Drive, const DWORD newSerial)
{
const max_pbsi = 3;
struct partial_boot_sector_info
{
LPSTR Fs; // file system name
DWORD FsOffs; // offset of file system name in the boot sector
DWORD SerialOffs; // offset of the serialnumber in the boot sector
};
partial_boot_sector_info pbsi[max_pbsi] =
{
{"FAT32", 0x52, 0x43},
{"FAT", 0x36, 0x27},
{"NTFS", 0x03, 0x48}
};
TCHAR szDrive[12];
char Sector[512];
DWORD i;
sprintf(szDrive, "%c:\\", Drive & 0xFF);
if (!disk.Open(szDrive))
{
ShowErrorString("Could not open disk!");
return;
}
// read sector
if (!disk.ReadSector(0, Sector))
{
ShowErrorString("Could not read sector!");
return;
}
// try to search for a valid boot sector
for (i=0;i<max_pbsi;i++)
{
if (strncmp(pbsi[i].Fs, Sector+pbsi[i].FsOffs, strlen(pbsi[i].Fs)) == 0)
{
// we found a valid signature
break;
}
}
if (i >= max_pbsi)
{
MessageBox(_T("Cannot change serial number of this file system!"),
_T("Error"), MB_ICONERROR);
return;
}
// patch serial number
*(PDWORD)(Sector+pbsi[i].SerialOffs) = newSerial;
// write boot sector
if (!disk.WriteSector(0, Sector))
{
ShowErrorString("Could not write sector!");
return;
}
ShowErrorString("Volume serial number changed successfully!\r"
"You might want to restart your system for changes to take effect!");
}
No guarantees, be careful.
void ChangeSerialNumber(char volume, uint newSerial)
{
var fsInfo = new[]
{
new { Name = "FAT32", NameOffs = 0x52, SerialOffs = 0x43 },
new { Name = "FAT", NameOffs = 0x36, SerialOffs = 0x27 },
new { Name = "NTFS", NameOffs = 0x03, SerialOffs = 0x48 }
};
using (var disk = new Disk(volume))
{
var sector = new byte[512];
disk.ReadSector(0, sector);
var fs = fsInfo.FirstOrDefault(
f => Strncmp(f.Name, sector, f.NameOffs)
);
if (fs == null) throw new NotSupportedException("This file system is not supported");
var s = newSerial;
for (int i = 0; i < 4; ++i, s >>= 8) sector[fs.SerialOffs + i] = (byte)(s & 0xFF);
disk.WriteSector(0, sector);
}
}
bool Strncmp(string str, byte[] data, int offset)
{
for(int i = 0; i < str.Length; ++i)
{
if (data[i + offset] != (byte)str[i]) return false;
}
return true;
}
class Disk : IDisposable
{
private SafeFileHandle handle;
public Disk(char volume)
{
var ptr = CreateFile(
String.Format("\\\\.\\{0}:", volume),
FileAccess.ReadWrite,
FileShare.ReadWrite,
IntPtr.Zero,
FileMode.Open,
0,
IntPtr.Zero
);
handle = new SafeFileHandle(ptr, true);
if (handle.IsInvalid) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
public void ReadSector(uint sector, byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException("buffer");
if (SetFilePointer(handle, sector, IntPtr.Zero, EMoveMethod.Begin) == INVALID_SET_FILE_POINTER) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
uint read;
if (!ReadFile(handle, buffer, buffer.Length, out read, IntPtr.Zero)) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
if (read != buffer.Length) throw new IOException();
}
public void WriteSector(uint sector, byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException("buffer");
if (SetFilePointer(handle, sector, IntPtr.Zero, EMoveMethod.Begin) == INVALID_SET_FILE_POINTER) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
uint written;
if (!WriteFile(handle, buffer, buffer.Length, out written, IntPtr.Zero)) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
if (written != buffer.Length) throw new IOException();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (handle != null) handle.Dispose();
}
}
enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
const uint INVALID_SET_FILE_POINTER = 0xFFFFFFFF;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
[MarshalAs(UnmanagedType.U4)] FileShare fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
int flags,
IntPtr template);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint SetFilePointer(
[In] SafeFileHandle hFile,
[In] uint lDistanceToMove,
[In] IntPtr lpDistanceToMoveHigh,
[In] EMoveMethod dwMoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(SafeFileHandle hFile, [Out] byte[] lpBuffer,
int nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
[DllImport("kernel32.dll")]
static extern bool WriteFile(SafeFileHandle hFile, [In] byte[] lpBuffer,
int nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
[In] IntPtr lpOverlapped);
}
Use e.g. ChangeSerialNumber('D', 0x12345678);
Here is a small example for reading and writing the serial number of a
FAT32 volume. To keep the sample small all error handling has been omitted.
Please note that direct access to the sectors of a volume
may lead to data loss or corruption. So be careful in using the
example below (it is not an example suitable for production usage).
No guarantee!
In the example below I use the Win32 API GetDiskFreeSpace (using .Net interop) to get
the bytes per sector for the FAT32 volume.
To open the fat volume, I use the Win32 API CreateFile because the FileStream class
does not support opening disk partitions directly.
static uint GenericRead = 0x80000000;
static uint GenericWrite = 0x40000000;
static uint OpenExisting = 3;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr SecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters);
static void ReadAndSetSerialNumber()
{
const string driveLetter = "e:"; // Drive with FAT32 file system.
uint sectorsPerCluster;
uint bytesPerSector;
uint numberOfFreeClusters;
uint totalNumberOfClusters;
GetDiskFreeSpace(String.Format(#"{0}\", driveLetter), out sectorsPerCluster, out bytesPerSector,
out numberOfFreeClusters, out totalNumberOfClusters);
Console.Out.WriteLine("Info for drive {0}", driveLetter);
Console.Out.WriteLine("Bytes per sector: {0}", bytesPerSector);
const int fatSerialOffset = 0x43;
const int fatIdOffset = 0x52;
const string fatFileSystemId = "FAT32";
using (SafeFileHandle sfh = CreateFile(String.Format("\\\\.\\{0}", driveLetter), GenericRead | GenericWrite,
(uint)FileShare.ReadWrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero))
{
using (FileStream fs = new FileStream(sfh, FileAccess.ReadWrite))
{
byte[] firstSector = new byte[bytesPerSector];
fs.Read(firstSector, 0, (int)bytesPerSector);
if (Encoding.ASCII.GetString(firstSector, fatIdOffset, fatFileSystemId.Length) == fatFileSystemId)
{
Console.Out.WriteLine("FAT32 file system found...");
uint serial = BitConverter.ToUInt32(firstSector, fatSerialOffset);
Console.Out.WriteLine("Read serial number: {0:X4}-{1:X4}", serial >> 16, serial & 0xFFFF);
// Write new serial number.
byte[] newserial = BitConverter.GetBytes((uint)10000123);
Array.Copy(newserial, 0, firstSector, fatSerialOffset, newserial.Length);
fs.Seek(0, SeekOrigin.Begin);
fs.Write(firstSector, 0, (int)bytesPerSector);
}
}
}
}
Furthermore you could use the .Net Framework's DriveInfo class to enumerate the
available drives on your computer.
Hope, this helps.
I'm trying to determine worst case disk speed, so I wrote the following function.
static public decimal MBytesPerSec(string volume)
{
string filename = volume + "\\writetest.tmp";
if (System.IO.File.Exists(filename))
System.IO.File.Delete(filename);
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
char[] data = new char[64000];
Stopwatch watch = new Stopwatch();
watch.Start();
int i = 0;
for (; i < 1000; i++)
{
file.Write(data);
if (watch.ElapsedMilliseconds > 2000)
{
break;
}
}
watch.Stop();
file.Close();
System.IO.File.Delete(volume + "\\test.txt");
decimal mbytessec = (i * 64 / watch.ElapsedMilliseconds);
return mbytessec;
}
The function works OK, but the writes are getting cached, so the speed is not worst case.
In WIN32 C++, I would simply create the file with FILE_FLAG_NO_BUFFERING, FILE_FLAG_WRITE_THROUGH options, and then make sure to follow the non-cached writing rules (write to the file at sector size offsets, with minimum of 4k writes)
I found one article that discusses the .NET technique.
So I wrote a new function (ignore the math errors).
static public decimal MBytesPerSecNonCached(string volume)
{
const FileOptions FILE_FLAG_NO_BUFFERING = (FileOptions)0x20000000;
string filename = volume + "\\writetest.tmp";
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 1024, FileOptions.WriteThrough | FILE_FLAG_NO_BUFFERING))
{
byte[] data = new byte[65535];
int i = 0;
Stopwatch watch = new Stopwatch();
watch.Start();
for (; i < 1000; i++)
{
fs.Write(data, 0, 65535);
if (watch.ElapsedMilliseconds > 2000)
{
break;
}
}
watch.Stop();
fs.Close();
System.IO.File.Delete(filename);
decimal mbytessec = (i * 64 / watch.ElapsedMilliseconds);
return mbytessec;
}
}
This function works for 4k, 16K and 32K write sizes, but once I try 64K write sizes, I get an exception:
IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations.
So, how can I fix this so I can test with larger than 32KB write sizes (64KB to 4096KB)?
Try some unmanaged code:
[DllImport("kernel32", SetLastError = true)]
static extern unsafe SafeFileHandle CreateFile(
string FileName, // file name
uint DesiredAccess, // access mode
uint ShareMode, // share mode
IntPtr SecurityAttributes, // Security Attr
uint CreationDisposition, // how to create
uint FlagsAndAttributes, // file attributes
IntPtr hTemplate // template file
);
const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
SafeFileHandle handle = CreateFile("filename",
(uint)FileAccess.Write,
(uint)FileShare.None,
IntPtr.Zero,
(uint)FileMode.Open,
FILE_FLAG_NO_BUFFERING,
IntPtr.Zero);
var unBufferedStream = new FileStream(handle,FileAccess.Read,blockSize,false);
now you should have access to an unbuffered stream which you can read and write however you please with no constraints
For the record....you can also disable caching like this:
[DllImport("KERNEL32", SetLastError = true)]
public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode,
IntPtr lpInBuffer, uint InBufferSize,
IntPtr lpOutBuffer, uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("KERNEL32", SetLastError = true)]
public extern static int CloseHandle(
IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct DISK_CACHE_INFORMATION
{
public byte ParametersSavable;
public byte ReadCacheEnabled;
public byte WriteCacheEnabled;
public int ReadRetentionPriority;//DISK_CACHE_RETENTION_PRIORITY = enum = int
public int WriteRetentionPriority;//DISK_CACHE_RETENTION_PRIORITY = enum = int
public Int16 DisablePrefetchTransferLength;//WORD
public byte PrefetchScalar;
}
public void SetDiskCache(byte val)
{
IntPtr h = CreateFile("\\\\.\\PHYSICALDRIVE0", (uint)FileAccess.Read | (uint)FileAccess.Write, (uint)FileShare.Write, IntPtr.Zero, (uint)FileMode.Open, 0, IntPtr.Zero);
DISK_CACHE_INFORMATION sInfo = new DISK_CACHE_INFORMATION();
IntPtr ptrout = Marshal.AllocHGlobal(Marshal.SizeOf(sInfo));
Marshal.StructureToPtr(sInfo, ptrout, true);
uint dwWritten = 0;
int ret = DeviceIoControl(h,IOCTL_DISK_GET_CACHE_INFORMATION,IntPtr.Zero,0,ptrout,(uint)Marshal.SizeOf(sInfo),ref dwWritten,IntPtr.Zero);
sInfo = (DISK_CACHE_INFORMATION)Marshal.PtrToStructure(ptrout,typeof(DISK_CACHE_INFORMATION));
sInfo.ReadCacheEnabled = val;
// acuma trimite structura modificata
IntPtr ptrin = Marshal.AllocHGlobal(Marshal.SizeOf(sInfo));
Marshal.StructureToPtr(sInfo, ptrin, true);
ret = DeviceIoControl(h, IOCTL_DISK_SET_CACHE_INFORMATION, ptrin, (uint)Marshal.SizeOf(sInfo), IntPtr.Zero, 0, ref dwWritten, IntPtr.Zero);
CloseHandle(h);
}