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);
}
Related
I am trying to do login in the phone. I am developing in c# and the library is in C++. The function "lineDevSpecific" returns the value "-2147483595", but it must to be positive.
[DllImport("Tapi32.dll", SetLastError = true)]
unsafe private static extern int lineDevSpecific(IntPtr hLine, IntPtr lpParams);
[StructLayout(LayoutKind.Sequential)]
public struct UserRec
{
//[MarshalAs(UnmanagedType.I4)]
public int dwMode=8;
public int dwParam1=201;
public int dwParam2=0;
}
unsafe static void Iniciar()
{
string vline = "Ip Office Phone: 201";
IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result=lineDevSpecific(hline, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Version 2:
I have modified the initial post adding the lineInitializeEx method and lineOpen.
These methods returns a positive value, after this lineDevSpecific continues returning the same value.
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern long lineInitializeEx(ref uint lphLineApp, uint hInstance, uint lpfnCallback, uint lpszFriendlyAppName, ref uint lpdwNumDevs, ref uint lpdwAPIVersion, ref uint lpLineInitializeExParams);
[DllImport("Tapi32.dll", SetLastError = true)]
internal static extern long lineOpen(ref uint hLineApp, uint dwDeviceID, uint lphLine, uint dwAPIVersion, uint dwExtVersion, uint dwCallbackInstance, uint dwPrivileges, uint dwMediaModes, ref uint lpCallParams);
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern int lineDevSpecific(uint hLine, IntPtr lpParams);
uint lineApp = 0;
uint numdevs = 0;
uint apiversion = 0;
uint exparams = 0;
uint lpcallparams = 0;
string sParams = "";
long lSize = 0;
long inicio = lineInitializeEx(ref lineApp, 0, 0, 0, ref numdevs, ref apiversion, ref exparams);
if (inicio > 0)
{
long open = lineOpen(ref lineApp, 0, 0, 0, 0, 0, 0, 0, ref lpcallparams);
//string vline = "Ip Office Phone: 201";
//IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result = lineDevSpecific(lineApp, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Solution: I have called to Julmar Atapi.
string extension = "201";
char[] c = new char[extension.Length + 2];
c[0] = (char)0x08; //login character
int i = 1;
foreach (char s in extension)
{
c[i] = s;
i++;
}
c[i] = (char)0x00; //null terminator
CurrentAddress.DeviceSpecific(Encoding.ASCII.GetBytes(c));
That is a LINEERR_ constant, see MSDN LINEERR_ Constants page
These use a hexadecimal unsigned "0x8000 00xx" style, turning them negative if you look at them as a signed int.
So this one is 0x8000 0035 LINEERR_INVALPOINTER
Your hline is wrong, this is a handle to a line not a text in a pointer. You need to get it from a lineOpen
Update for version 2
You are mixing up hLineApp and hLine. From lineOpen MSDN:
hLineApp: Handle to the application's registration with TAPI.
lphLine: Pointer to an HLINE handle that is then loaded with the handle representing the opened line device. Use this handle to identify the device when invoking other functions on the open line device.
I am reading directly from a disk using C# and pinvoking the kernel32 ReadFile method.i want just read a particular sector for save time but ReadFile read from first to N sector. How can read only own sector with my choice?
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED
{
public uint Internal;
public uint InternalHigh;
public uint Offset;
public uint OffsetHigh;
public int hEvent;
}
[DllImport("kernel32", SetLastError = true)]
static extern int CreateFile(string filename, uint desiredAccess, uint shareMode, IntPtr attributes, uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile);
[DllImport("kernel32", SetLastError = true)]
public static extern Boolean CloseHandle(int handle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean ReadFile(IntPtr hFile, Byte[] buffer, UInt32 BytesToRead, ref UInt32 BytedRead, OVERLAPPED OverLapped);
static int EIGHT_K = 8192;
static int FIVE_TWELVE_BYTES = 512;
static uint GENERIC_READ = 0x80000000;
static uint OPEN_EXISTING = 3;
static uint FILE_SHARE_READ = 1;
static uint FILE_SHARE_WRITE = 2;
[STAThread]
private void button1_Click(object sender, EventArgs e)
{
int fileHandle = 0;
bool returnVal = true;
try
{
// Open the device specified (Using the boot partition)
string deviceName = #"\\.\f:";
fileHandle = CreateFile(deviceName, GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE, (IntPtr)0, OPEN_EXISTING, 0,(IntPtr)0);
if (fileHandle != -1)
{
Byte[] sector = new Byte[EIGHT_K];
UInt32 bytesRead = (uint)EIGHT_K;
OVERLAPPED ol = new OVERLAPPED();
// Can't get a FileStream ctor to work so I am using Win32 API ReadFile
bool worked = ReadFile((IntPtr)fileHandle, sector, (uint)EIGHT_K, ref bytesRead, ol);
return;
}
}
catch (Exception ex)
{
return;
}
finally
{
CloseHandle(fileHandle);
}
return;
}
I want to mark the DVD till required Original DVD to run the program.
Your OVERLAPPED struct is declared poorly and is incorrect in a 64 bit process. But in any case you don't need it. You are not performing overlapped I/O. Which is just as well because the declaration of ReadFile is incorrect. That function wants a pointer to an OVERLAPPED struct. You pass it by value.
In any case, you just don't need to consider overlapped I/O. So fix this issue by deleting the OVERLAPPED struct declaration from your code. And declare ReadFile like this:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean ReadFile(IntPtr hFile, Byte[] buffer,
UInt32 BytesToRead, out UInt32 BytedRead, IntPtr Overlapped);
Pass IntPtr.Zero as the Overlapped parameter. And do make sure that you check the return value of ReadFile for an error.
The next step is to seek to a location in the file. Use SetFilePointerEx for that.
DllImport("kernel32.dll")]
static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove,
out long lpNewFilePointer, uint dwMoveMethod);
Consult the documentation for SetFilePointerEx to work out how to call this function.
Since you are using direct disk access, you will of course need to align the reads to sector boundaries.
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).
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 have to modify a text in a file programmatically with C#. What's the best way to do this and avoid rewriting the entire file content. I want to edit only some words and save it.
As others have said, you can easily append to an existing file. Inserting data in the middle of the file requires that you write out the remainder of the file. There's no way to just shift everything in the file. If you want to overwrite individual bytes in place, you can open a FileStream. You then use the "Seek" to go to the specific bytes you want to overwrite, and use Write, or WriteByte to overwrite the existing data with new data.
You can append to an existing file using StreamWriter and AppendText easily.
If you want to do more serious modifications, then I think you must read the contents of the file into memory in C# using StreamReader for example, modifying the contents programmatically of that stream, and then re-writing the file with the stream's contents.
I think that's how it is anyway.
If you are targeting windows, you can use the winapi CreateFile, ReadFile, WriteFile, etc. You can easily write to any spot in a file and write as much data as you want. You do not have to rewrite the file. This doesn't even require unsafe code. I limited the functionality of the functions (no asynch or wacky file mappings) just to get this done quickly, but it can read and write files no problem. To get this example to work, make a small text file 100 bytes or so, and make sure it is in the location of the exe. A little bit of this code comes from the MSDN website. I have modified it significantly though.
using System;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Forms;
using Path = System.IO.Path;
class Program {
static void Main() {
string sTestFile = Path.Combine(Path.GetDirectoryName(
Application.ExecutablePath), "Test.txt");
// test reading a file
WinApiFile File = new WinApiFile(sTestFile,
WinApiFile.DesiredAccess.GENERIC_READ);
byte[] aBuffer = new byte[1000];
uint cbRead = File.Read(aBuffer, 1000);
File.Close();
// Test writing the file
File.Open(WinApiFile.DesiredAccess.GENERIC_WRITE);
// write the time to a 10 byte offset in the file
// conver the date time to byte array
int i = 0;
foreach (var ch in DateTime.Now.ToString())
aBuffer[i++] = (byte)ch;
// now write it out
File.MoveFilePointer(10); // 10 byte offset
File.Write(aBuffer, (uint)i); // write the time
File.Dispose();
}
}
public class WinApiFile : IDisposable {
/* ---------------------------------------------------------
* private members
* ------------------------------------------------------ */
private SafeFileHandle _hFile = null;
private string _sFileName = "";
private bool _fDisposed;
/* ---------------------------------------------------------
* properties
* ------------------------------------------------------ */
public bool IsOpen { get { return (_hFile != null); } }
public SafeFileHandle Handle { get { return _hFile; } }
public string FileName {
get { return _sFileName; }
set {
_sFileName = (value ?? "").Trim();
if (_sFileName.Length == 0)
CloseHandle(_hFile);
}
}
public int FileLength {
get {
return (_hFile != null) ? (int)GetFileSize(_hFile,
IntPtr.Zero) : 0;
}
set {
if (_hFile == null)
return;
MoveFilePointer(value, MoveMethod.FILE_BEGIN);
if (!SetEndOfFile(_hFile))
ThrowLastWin32Err();
}
}
/* ---------------------------------------------------------
* Constructors
* ------------------------------------------------------ */
public WinApiFile(string sFileName,
DesiredAccess fDesiredAccess) {
FileName = sFileName;
Open(fDesiredAccess);
}
public WinApiFile(string sFileName,
DesiredAccess fDesiredAccess,
CreationDisposition fCreationDisposition) {
FileName = sFileName;
Open(fDesiredAccess, fCreationDisposition);
}
/* ---------------------------------------------------------
* Open/Close
* ------------------------------------------------------ */
public void Open(
DesiredAccess fDesiredAccess) {
Open(fDesiredAccess, CreationDisposition.OPEN_EXISTING);
}
public void Open(
DesiredAccess fDesiredAccess,
CreationDisposition fCreationDisposition) {
ShareMode fShareMode;
if (fDesiredAccess == DesiredAccess.GENERIC_READ) {
fShareMode = ShareMode.FILE_SHARE_READ;
} else {
fShareMode = ShareMode.FILE_SHARE_NONE;
}
Open(fDesiredAccess, fShareMode, fCreationDisposition, 0);
}
public void Open(
DesiredAccess fDesiredAccess,
ShareMode fShareMode,
CreationDisposition fCreationDisposition,
FlagsAndAttributes fFlagsAndAttributes) {
if (_sFileName.Length == 0)
throw new ArgumentNullException("FileName");
_hFile = CreateFile(_sFileName, fDesiredAccess, fShareMode,
IntPtr.Zero, fCreationDisposition, fFlagsAndAttributes,
IntPtr.Zero);
if (_hFile.IsInvalid) {
_hFile = null;
ThrowLastWin32Err();
}
_fDisposed = false;
}
public void Close() {
if (_hFile == null)
return;
_hFile.Close();
_hFile = null;
_fDisposed = true;
}
/* ---------------------------------------------------------
* Move file pointer
* ------------------------------------------------------ */
public void MoveFilePointer(int cbToMove) {
MoveFilePointer(cbToMove, MoveMethod.FILE_CURRENT);
}
public void MoveFilePointer(int cbToMove,
MoveMethod fMoveMethod) {
if (_hFile != null)
if (SetFilePointer(_hFile, cbToMove, IntPtr.Zero,
fMoveMethod) == INVALID_SET_FILE_POINTER)
ThrowLastWin32Err();
}
public int FilePointer {
get {
return (_hFile != null) ? (int)SetFilePointer(_hFile, 0,
IntPtr.Zero, MoveMethod.FILE_CURRENT) : 0;
}
set {
MoveFilePointer(value);
}
}
/* ---------------------------------------------------------
* Read and Write
* ------------------------------------------------------ */
public uint Read(byte[] buffer, uint cbToRead) {
// returns bytes read
uint cbThatWereRead = 0;
if (!ReadFile(_hFile, buffer, cbToRead,
ref cbThatWereRead, IntPtr.Zero))
ThrowLastWin32Err();
return cbThatWereRead;
}
public uint Write(byte[] buffer, uint cbToWrite) {
// returns bytes read
uint cbThatWereWritten = 0;
if (!WriteFile(_hFile, buffer, cbToWrite,
ref cbThatWereWritten, IntPtr.Zero))
ThrowLastWin32Err();
return cbThatWereWritten;
}
/* ---------------------------------------------------------
* IDisposable Interface
* ------------------------------------------------------ */
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool fDisposing) {
if (!_fDisposed) {
if (fDisposing) {
if (_hFile != null)
_hFile.Dispose();
_fDisposed = true;
}
}
}
~WinApiFile() {
Dispose(false);
}
/* ---------------------------------------------------------
* WINAPI STUFF
* ------------------------------------------------------ */
private void ThrowLastWin32Err() {
Marshal.ThrowExceptionForHR(
Marshal.GetHRForLastWin32Error());
}
[Flags]
public enum DesiredAccess : uint {
GENERIC_READ = 0x80000000,
GENERIC_WRITE = 0x40000000
}
[Flags]
public enum ShareMode : uint {
FILE_SHARE_NONE = 0x0,
FILE_SHARE_READ = 0x1,
FILE_SHARE_WRITE = 0x2,
FILE_SHARE_DELETE = 0x4,
}
public enum MoveMethod : uint {
FILE_BEGIN = 0,
FILE_CURRENT = 1,
FILE_END = 2
}
public enum CreationDisposition : uint {
CREATE_NEW = 1,
CREATE_ALWAYS = 2,
OPEN_EXISTING = 3,
OPEN_ALWAYS = 4,
TRUNCATE_EXSTING = 5
}
[Flags]
public enum FlagsAndAttributes : uint {
FILE_ATTRIBUTES_ARCHIVE = 0x20,
FILE_ATTRIBUTE_HIDDEN = 0x2,
FILE_ATTRIBUTE_NORMAL = 0x80,
FILE_ATTRIBUTE_OFFLINE = 0x1000,
FILE_ATTRIBUTE_READONLY = 0x1,
FILE_ATTRIBUTE_SYSTEM = 0x4,
FILE_ATTRIBUTE_TEMPORARY = 0x100,
FILE_FLAG_WRITE_THROUGH = 0x80000000,
FILE_FLAG_OVERLAPPED = 0x40000000,
FILE_FLAG_NO_BUFFERING = 0x20000000,
FILE_FLAG_RANDOM_ACCESS = 0x10000000,
FILE_FLAG_SEQUENTIAL_SCAN = 0x8000000,
FILE_FLAG_DELETE_ON = 0x4000000,
FILE_FLAG_POSIX_SEMANTICS = 0x1000000,
FILE_FLAG_OPEN_REPARSE_POINT = 0x200000,
FILE_FLAG_OPEN_NO_CALL = 0x100000
}
public const uint INVALID_HANDLE_VALUE = 0xFFFFFFFF;
public const uint INVALID_SET_FILE_POINTER = 0xFFFFFFFF;
// Use interop to call the CreateFile function.
// For more information about CreateFile,
// see the unmanaged MSDN reference library.
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern SafeFileHandle CreateFile(
string lpFileName,
DesiredAccess dwDesiredAccess,
ShareMode dwShareMode,
IntPtr lpSecurityAttributes,
CreationDisposition dwCreationDisposition,
FlagsAndAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32", SetLastError = true)]
internal static extern Int32 CloseHandle(
SafeFileHandle hObject);
[DllImport("kernel32", SetLastError = true)]
internal static extern bool ReadFile(
SafeFileHandle hFile,
Byte[] aBuffer,
UInt32 cbToRead,
ref UInt32 cbThatWereRead,
IntPtr pOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteFile(
SafeFileHandle hFile,
Byte[] aBuffer,
UInt32 cbToWrite,
ref UInt32 cbThatWereWritten,
IntPtr pOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern UInt32 SetFilePointer(
SafeFileHandle hFile,
Int32 cbDistanceToMove,
IntPtr pDistanceToMoveHigh,
MoveMethod fMoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetEndOfFile(
SafeFileHandle hFile);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern UInt32 GetFileSize(
SafeFileHandle hFile,
IntPtr pFileSizeHigh);
}
When you open a file for writing there are only two possible outcomes. Either you open it for appending which will allow you to write at the end only, or you open it for normal write in which case you will write from the beginning of the file. There is no "open at mid point, write then shift remaining content according to insertion length".
It is possible that some API will allow such modifications but behind the scenes, they will only allow you those two basic operations. You shouldn't worry to much about rewriting the whole file however, it is not such a bad operation unless your file is huge and your hard drive is very slow. You should worry more about the number of times you do it instead.
Open the file
Read the whole file into a variable or array
Insert modifications
Write back the file
It's probably simpler to read the file in and modify it then read it out again.
To really be efficient (i.e. not read/write the whole file for every operation) you need to do some house keeping to keep track of metadata about the file, and then do binary editing.
You'll still have to manage the case where the file grows and needs to be appended, etc.