I need to access Windows MFT(Master File Table) using C# in my .net application.
I have googled about this and couldn't find any good results. I have been searching for the information from the past 2 days but have been unable to find any information on the same.
I am not looking for exact code to do the same, I am just looking for some information which can get me started.
The only thing I have been able to figure out is that I have to use P/Invoke.
I want to know the functions I would be using to have access to MFT.
If you are able to provide some code sample then that would be great.
First, you have to have and assert sufficient privileges to access the MFT - this is a pain all by itself. Then, you have to get a handle to a file/folder on the volume - for the calls in the last step...which is to call a Windows API (called DeviceIOControl) in a loop and read the entries from the returned API call - and this is it's own special headache.
Conceptually - this looks like:
static void Main( string[ ] args )
{
if ( Privileges.HasBackupAndRestorePrivileges )
{
using ( var volume = GetVolumeHandle( "C:\\" ) )
{
ReadMft( volume );
}
}
}
If you take each of these in turn, asserting sufficient privileges is the most obscure part. There's a Windows API to change the privileges of the running token - and you use that to add the necessary privileges. Here's an excerpt from a class that I use to assert those privileges. You could assert a bunch more privileges - but this should be sufficient for reading the MFT.
Your application will need to run under an account that can actually obtain the requisite privileges - an admin account is good. Also, a backup operator will work.
public static class Privileges
{
private static int asserted = 0;
private static bool hasBackupPrivileges = false;
public static bool HasBackupAndRestorePrivileges
{
get { return AssertPriveleges( ); }
}
/// <remarks>
/// First time this method is called, it attempts to set backup privileges for the current process.
/// Subsequently, it returns the results of that first call.
/// </remarks>
private static bool AssertPriveleges( )
{
bool success = false;
var wasAsserted = Interlocked.CompareExchange( ref asserted, 1, 0 );
if ( wasAsserted == 0 ) // first time here? come on in!
{
success =
AssertPrivelege( NativeMethods.SE_BACKUP_NAME ) &&
AssertPrivelege( NativeMethods.SE_RESTORE_NAME );
hasBackupPrivileges = success;
}
return hasBackupPrivileges;
}
private static bool AssertPrivelege( string privelege )
{
IntPtr token;
var tokenPrivileges = new NativeMethods.TOKEN_PRIVILEGES( );
tokenPrivileges.Privileges = new NativeMethods.LUID_AND_ATTRIBUTES[ 1 ];
var success =
NativeMethods.OpenProcessToken( NativeMethods.GetCurrentProcess( ), NativeMethods.TOKEN_ADJUST_PRIVILEGES, out token )
&&
NativeMethods.LookupPrivilegeValue( null, privelege, out tokenPrivileges.Privileges[ 0 ].Luid );
try
{
if ( success )
{
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[ 0 ].Attributes = NativeMethods.SE_PRIVILEGE_ENABLED;
success =
NativeMethods.AdjustTokenPrivileges( token, false, ref tokenPrivileges, Marshal.SizeOf( tokenPrivileges ), IntPtr.Zero, IntPtr.Zero )
&&
( Marshal.GetLastWin32Error( ) == 0 );
}
if ( !success )
{
Console.WriteLine( "Could not assert privilege: " + privelege );
}
}
finally
{
NativeMethods.CloseHandle( token );
}
return success;
}
}
Once you're past that hurdle, the rest is - well...still a festival of obscurity. You have to get a handle to a file or folder - with backup semantics. You can more-than-likely just open a FileStream on any old file on the volume you're after and the FileStream will have a handle you can use for subsequent calls. This isn't precisely what my application did - but my app had to do things this answer doesn't have to do.
internal static SafeFileHandle GetVolumeHandle( string pathToVolume, NativeMethods.EFileAccess access = NativeMethods.EFileAccess.AccessSystemSecurity | NativeMethods.EFileAccess.GenericRead | NativeMethods.EFileAccess.ReadControl )
{
var attributes = ( uint ) NativeMethods.EFileAttributes.BackupSemantics;
var handle = NativeMethods.CreateFile( pathToVolume, access, 7U, IntPtr.Zero, ( uint ) NativeMethods.ECreationDisposition.OpenExisting, attributes, IntPtr.Zero );
if ( handle.IsInvalid )
{
throw new IOException( "Bad path" );
}
return handle;
}
For ReadMft - There is a rather complex windows API function - DeviceIOControl - that takes buffers with an epic variety of inputs and returns buffers containing a mind-bending variety of outputs. It's a kind of catch-all API for querying information about various devices - and the volume containing the MFT is a device.
To read the MFT, you call DeviceIOControl with a device IO control code of FSCTL_ENUM_USN_DATA - which returns one USN record for each record in the MFT. There are lots of records per each invocation - and after each invocation, you parameterize the next call in the loop with the first bit of info returned by the previous call.
BTW - I renamed the windows API calls in my code to make them look more .Net-like. I'm not sure I'd do that in the future.
Special note here: You're getting one record for each file - regardless of how many hard links there are - you have to do additional calls to enumerate the hard links.
The file system hierarchy is encoded in the FileReferenceNumber and ParentFileReferenceNumber of the structures you get back from the call. You'd nominally save off these usn records to a list, sorted by FileReferenceNumber and make a secondary index for the ParentFileReferenceNumber - or something like that. For the purpose of illustration, this code just dumps the MFT entries.
This example uses unsafe code - and fixes the location of the buffers containing the input and output. There are different ways to approach this - but this is nice and zippy. If you use this, you have to allow unsafe code in your project settings.
public unsafe static bool ReadMft( SafeHandle volume )
{
var outputBufferSize = 1024 * 1024;
var input = new NativeMethods.MFTEnumDataV0( );
var usnRecord = new NativeMethods.UsnRecordV2( );
var outputBuffer = new byte[ outputBufferSize ];
var okay = true;
var doneReading = false;
try
{
fixed ( byte* pOutput = outputBuffer )
{
input.StartFileReferenceNumber = 0;
input.LowUsn = 0;
input.HighUsn = long.MaxValue;
using ( var stream = new MemoryStream( outputBuffer, true ) )
{
while ( !doneReading )
{
var bytesRead = 0U;
okay = NativeMethods.DeviceIoControl
(
volume.DangerousGetHandle( ),
NativeMethods.DeviceIOControlCode.FsctlEnumUsnData,
( byte* ) &input.StartFileReferenceNumber,
( uint ) Marshal.SizeOf( input ),
pOutput,
( uint ) outputBufferSize,
out bytesRead,
IntPtr.Zero
);
if ( !okay )
{
var error = Marshal.GetLastWin32Error( );
okay = error == NativeMethods.ERROR_HANDLE_EOF;
if ( !okay )
{
Console.WriteLine( "Crap! Windows error " + error.ToString( ) );
break;
}
else
{
doneReading = true;
}
}
input.StartFileReferenceNumber = stream.ReadULong( );
while ( stream.Position < bytesRead )
{
usnRecord.Read( stream );
//-->>>>>>>>>>>>>>>>>
//--> just an example of reading out the record...
Console.WriteLine( "FRN:" + usnRecord.FileReferenceNumber.ToString( ) );
Console.WriteLine( "Parent FRN:" + usnRecord.ParentFileReferenceNumber.ToString( ) );
Console.WriteLine( "File name:" + usnRecord.FileName );
Console.WriteLine( "Attributes: " + ( NativeMethods.EFileAttributes ) usnRecord.FileAttributes );
Console.WriteLine( "Timestamp:" + usnRecord.TimeStamp );
//-->>>>>>>>>>>>>>>>>>>
}
stream.Seek( 0, SeekOrigin.Begin );
}
}
}
}
catch ( Exception ex )
{
Console.Write( ex );
okay = false;
}
return okay;
}
I do something probably kind of cheesy to save myself a lot of work - I add pseudo-serialization methods to windows API structures - so that they can read themselves out of streams. For example, the usnRecord used to read the buffer in the foregoing code is a windows API structure - but with a serialization interface implemented:
[StructLayout( LayoutKind.Sequential )]
internal struct UsnRecordV2: IBinarySerialize
{
public uint RecordLength;
public ushort MajorVersion;
public ushort MinorVersion;
public ulong FileReferenceNumber;
public ulong ParentFileReferenceNumber;
public long Usn;
public long TimeStamp;
public UsnReason Reason;
public uint SourceInfo;
public uint SecurityId;
public uint FileAttributes;
public ushort FileNameLength;
public ushort FileNameOffset;
public string FileName;
/// <remarks>
/// Note how the read advances to the FileNameOffset and reads only FileNameLength bytes.
/// </remarks>
public void Read( Stream stream )
{
var startOfRecord = stream.Position;
RecordLength = stream.ReadUInt( );
MajorVersion = stream.ReadUShort( );
MinorVersion = stream.ReadUShort( );
FileReferenceNumber = stream.ReadULong( );
ParentFileReferenceNumber = stream.ReadULong( );
Usn = stream.ReadLong( );
TimeStamp = stream.ReadLong( );
Reason = ( UsnReason ) stream.ReadUInt( );
SourceInfo = stream.ReadUInt( );
SecurityId = stream.ReadUInt( );
FileAttributes = stream.ReadUInt( );
FileNameLength = stream.ReadUShort( );
FileNameOffset = stream.ReadUShort( );
stream.Position = startOfRecord + FileNameOffset;
FileName = Encoding.Unicode.GetString( stream.ReadBytes( FileNameLength ) );
stream.Position = startOfRecord + RecordLength;
}
/// <summary>We never write instances of this structure</summary>
void IBinarySerialize.Write( Stream stream )
{
throw new NotImplementedException( );
}
}
...where IBinarySerialze is:
public interface IBinarySerialize
{
/// <summary>Reads an object's data from a <see cref="Stream"/></summary>
void Read( Stream stream );
/// <summary>Writes an objects serializable content to a <see cref="Stream"/></summary>
void Write( Stream stream );
}
There are stream extension methods used in this structure. Basically, they're lifted from BinaryReader. Why? Because in .Net 3.5 - where I had to write this originally - the BCL BinaryReader would close the stream you wrapped it around - and I had lots of places where that was just intolerable.
internal static class StreamingExtensions
{
public static ushort ReadUShort( this Stream stream )
{
return BitConverter.ToUInt16( ReadBytes( stream, 2 ), 0 );
}
public static uint ReadUInt( this Stream stream )
{
return BitConverter.ToUInt32( ReadBytes( stream, 4 ), 0 );
}
public static long ReadLong( this Stream stream )
{
return BitConverter.ToInt64( ReadBytes( stream, 8 ), 0 );
}
public static ulong ReadULong( this Stream stream )
{
return BitConverter.ToUInt64( ReadBytes( stream, 8 ), 0 );
}
public static byte[ ] ReadBytes( this Stream stream, int length, bool throwIfIncomplete = false )
{
var bytes = new byte[ length ];
var bytesRead = 0;
var offset = 0;
if ( length > 0 )
{
while ( offset < length )
{
bytesRead = stream.Read( bytes, offset, length - offset );
if ( bytesRead == 0 )
{
if ( throwIfIncomplete ) throw new InvalidOperationException( "incomplete" );
break;
}
offset += bytesRead;
}
}
return bytes;
}
}
And for completeness, here are the native methods, enums, constants, and noise. Most are from from PInvoke.net, but again...the names of many of these things were .Net-ified. Apologies to the purists.
internal class NativeMethods
{
internal const int ERROR_HANDLE_EOF = 38;
//--> Privilege constants....
internal const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
internal const string SE_BACKUP_NAME = "SeBackupPrivilege";
internal const string SE_RESTORE_NAME = "SeRestorePrivilege";
internal const string SE_SECURITY_NAME = "SeSecurityPrivilege";
internal const string SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege";
internal const string SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege";
internal const string SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege";
internal const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
internal const string SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege";
internal const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege";
internal const string SE_TCB_NAME = "SeTcbPrivilege";
internal const string SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege";
internal const string SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege";
//--> For starting a process in session 1 from session 0...
internal const int TOKEN_DUPLICATE = 0x0002;
internal const uint MAXIMUM_ALLOWED = 0x2000000;
internal const int CREATE_NEW_CONSOLE = 0x00000010;
internal const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
internal const int TOKEN_QUERY = 0x00000008;
[DllImport( "advapi32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
internal static extern bool OpenProcessToken( IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle );
[DllImport( "kernel32.dll" )]
internal static extern IntPtr GetCurrentProcess( );
[DllImport( "advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
[return: MarshalAs( UnmanagedType.Bool )]
internal static extern bool LookupPrivilegeValue( string lpSystemName, string lpName, out LUID lpLuid );
[DllImport( "advapi32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
internal static extern bool AdjustTokenPrivileges( IntPtr TokenHandle, [MarshalAs( UnmanagedType.Bool )]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength );
[DllImport( "kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode )]
[return: MarshalAs( UnmanagedType.Bool )]
internal static unsafe extern bool DeviceIoControl( IntPtr hDevice, DeviceIOControlCode controlCode, byte* lpInBuffer, uint nInBufferSize, byte* lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped );
[DllImport( "kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
internal static extern SafeFileHandle CreateFile( string lpFileName, EFileAccess dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile );
[DllImport( "kernel32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
internal static extern bool CloseHandle( IntPtr hObject );
[Flags]
internal enum EMethod: uint
{
Buffered = 0,
InDirect = 1,
OutDirect = 2,
Neither = 3
}
[Flags]
internal enum EFileAccess: uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
AccessSystemSecurity = 0x1000000,
MaximumAllowed = 0x2000000
}
[Flags]
internal enum EFileDevice: uint
{
Beep = 0x00000001,
CDRom = 0x00000002,
CDRomFileSytem = 0x00000003,
Controller = 0x00000004,
Datalink = 0x00000005,
Dfs = 0x00000006,
Disk = 0x00000007,
DiskFileSystem = 0x00000008,
FileSystem = 0x00000009,
InPortPort = 0x0000000a,
Keyboard = 0x0000000b,
Mailslot = 0x0000000c,
MidiIn = 0x0000000d,
MidiOut = 0x0000000e,
Mouse = 0x0000000f,
MultiUncProvider = 0x00000010,
NamedPipe = 0x00000011,
Network = 0x00000012,
NetworkBrowser = 0x00000013,
NetworkFileSystem = 0x00000014,
Null = 0x00000015,
ParallelPort = 0x00000016,
PhysicalNetcard = 0x00000017,
Printer = 0x00000018,
Scanner = 0x00000019,
SerialMousePort = 0x0000001a,
SerialPort = 0x0000001b,
Screen = 0x0000001c,
Sound = 0x0000001d,
Streams = 0x0000001e,
Tape = 0x0000001f,
TapeFileSystem = 0x00000020,
Transport = 0x00000021,
Unknown = 0x00000022,
Video = 0x00000023,
VirtualDisk = 0x00000024,
WaveIn = 0x00000025,
WaveOut = 0x00000026,
Port8042 = 0x00000027,
NetworkRedirector = 0x00000028,
Battery = 0x00000029,
BusExtender = 0x0000002a,
Modem = 0x0000002b,
Vdm = 0x0000002c,
MassStorage = 0x0000002d,
Smb = 0x0000002e,
Ks = 0x0000002f,
Changer = 0x00000030,
Smartcard = 0x00000031,
Acpi = 0x00000032,
Dvd = 0x00000033,
FullscreenVideo = 0x00000034,
DfsFileSystem = 0x00000035,
DfsVolume = 0x00000036,
Serenum = 0x00000037,
Termsrv = 0x00000038,
Ksec = 0x00000039,
// From Windows Driver Kit 7
Fips = 0x0000003A,
Infiniband = 0x0000003B,
Vmbus = 0x0000003E,
CryptProvider = 0x0000003F,
Wpd = 0x00000040,
Bluetooth = 0x00000041,
MtComposite = 0x00000042,
MtTransport = 0x00000043,
Biometric = 0x00000044,
Pmi = 0x00000045
}
internal enum EFileIOCtlAccess: uint
{
Any = 0,
Special = Any,
Read = 1,
Write = 2
}
internal enum DeviceIOControlCode: uint
{
FsctlEnumUsnData = ( EFileDevice.FileSystem << 16 ) | ( 44 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
FsctlReadUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 46 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
FsctlReadFileUsnData = ( EFileDevice.FileSystem << 16 ) | ( 58 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
FsctlQueryUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 61 << 2 ) | EMethod.Buffered | ( EFileIOCtlAccess.Any << 14 ),
FsctlCreateUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 57 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 )
}
/// <summary>Control structure used to interrogate MFT data using DeviceIOControl from the user volume</summary>
[StructLayout( LayoutKind.Sequential )]
internal struct MFTEnumDataV0
{
public ulong StartFileReferenceNumber;
public long LowUsn;
public long HighUsn;
}
/// <summary>A structure resurned form USN queries</summary>
/// <remarks>
/// FileName is synthetic...composed during a read of the structure and is not technically
/// part of the Win32 API's definition...although the actual FileName is contained
/// "somewhere" in the structure's trailing bytes, according to FileNameLength and FileNameOffset.
///
/// Alignment boundaries are enforced, and so, the RecordLength
/// may be somewhat larger than the accumulated lengths of the members plus the FileNameLength.
/// </remarks>
[StructLayout( LayoutKind.Sequential )]
internal struct UsnRecordV2: IBinarySerialize
{
public uint RecordLength;
public ushort MajorVersion;
public ushort MinorVersion;
public ulong FileReferenceNumber;
public ulong ParentFileReferenceNumber;
public long Usn;
public long TimeStamp;
public UsnReason Reason;
public uint SourceInfo;
public uint SecurityId;
public uint FileAttributes;
public ushort FileNameLength;
public ushort FileNameOffset;
public string FileName;
/// <remarks>Note how the read advances to the FileNameOffset and reads only FileNameLength bytes</remarks>
public void Read( Stream stream )
{
var startOfRecord = stream.Position;
RecordLength = stream.ReadUInt( );
MajorVersion = stream.ReadUShort( );
MinorVersion = stream.ReadUShort( );
FileReferenceNumber = stream.ReadULong( );
ParentFileReferenceNumber = stream.ReadULong( );
Usn = stream.ReadLong( );
TimeStamp = stream.ReadLong( );
Reason = ( UsnReason ) stream.ReadUInt( );
SourceInfo = stream.ReadUInt( );
SecurityId = stream.ReadUInt( );
FileAttributes = stream.ReadUInt( );
FileNameLength = stream.ReadUShort( );
FileNameOffset = stream.ReadUShort( );
stream.Position = startOfRecord + FileNameOffset;
FileName = Encoding.Unicode.GetString( stream.ReadBytes( FileNameLength ) );
stream.Position = startOfRecord + RecordLength;
}
void IBinarySerialize.Write( Stream stream )
{
throw new NotImplementedException( );
}
}
/// <summary>Structure returned from USN query that describes the state of the journal</summary>
[StructLayout( LayoutKind.Sequential )]
internal struct UsnJournalDataV1: IBinarySerialize
{
public ulong UsnJournalId;
public long FirstUsn;
public long NextUsn;
public long LowestValidUsn;
public long MaxUsn;
public ulong MaximumSize;
public ulong AllocationDelta;
public ushort MinSupportedMajorVersion;
public ushort MaxSupportedMajorVersion;
public void Read( Stream stream )
{
UsnJournalId = stream.ReadULong( );
FirstUsn = stream.ReadLong( );
NextUsn = stream.ReadLong( );
LowestValidUsn = stream.ReadLong( );
MaxUsn = stream.ReadLong( );
MaximumSize = stream.ReadULong( );
AllocationDelta = stream.ReadULong( );
MinSupportedMajorVersion = stream.ReadUShort( );
MaxSupportedMajorVersion = stream.ReadUShort( );
}
void IBinarySerialize.Write( Stream stream )
{
throw new NotImplementedException( );
}
}
[StructLayout( LayoutKind.Sequential )]
internal struct LUID
{
public UInt32 LowPart;
public Int32 HighPart;
}
[StructLayout( LayoutKind.Sequential )]
internal struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public UInt32 Attributes;
}
internal struct TOKEN_PRIVILEGES
{
public UInt32 PrivilegeCount;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 1 )] // !! think we only need one
public LUID_AND_ATTRIBUTES[ ] Privileges;
}
[Flags]
internal enum EFileAttributes: uint
{
/// <summary/>
None = 0,
//--> these are consistent w/ .Net FileAttributes...
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
//--> additional CreateFile call attributes...
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
/// <summary>Reasons the file changed (from USN journal)</summary>
[Flags]
public enum UsnReason: uint
{
BASIC_INFO_CHANGE = 0x00008000,
CLOSE = 0x80000000,
COMPRESSION_CHANGE = 0x00020000,
DATA_EXTEND = 0x00000002,
DATA_OVERWRITE = 0x00000001,
DATA_TRUNCATION = 0x00000004,
EA_CHANGE = 0x00000400,
ENCRYPTION_CHANGE = 0x00040000,
FILE_CREATE = 0x00000100,
FILE_DELETE = 0x00000200,
HARD_LINK_CHANGE = 0x00010000,
INDEXABLE_CHANGE = 0x00004000,
NAMED_DATA_EXTEND = 0x00000020,
NAMED_DATA_OVERWRITE = 0x00000010,
NAMED_DATA_TRUNCATION = 0x00000040,
OBJECT_ID_CHANGE = 0x00080000,
RENAME_NEW_NAME = 0x00002000,
RENAME_OLD_NAME = 0x00001000,
REPARSE_POINT_CHANGE = 0x00100000,
SECURITY_CHANGE = 0x00000800,
STREAM_CHANGE = 0x00200000,
None = 0x00000000
}
internal enum ECreationDisposition: uint
{
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
}
}
You can use this https://sourceforge.net/projects/ntfsreader/ open source library that is written in C# by Danny Couture.
I tested it and its performance is good. It can parse an NTFS drive with more than 100000 entry(file and folder) in less than 2 seconds.
Using the https://sourceforge.net/projects/ntfsreader/ library by Danny Couture is quite straightforward:
There are two main activities in which I am interested.
The first is reading the information for a given drive
The second part is processing the information retrieved (Search for a given file/folder, etc, rename all files, find files with length greater than 260, etc).
All information is returned as nodes and these can be files or folders or some other strange NTFS stuff such as extensions, etc, which I currently do not need.
You can test for which types of nodes you are interested in by testing node.Attributes - for example:
if((node.Attributes & Attributes.Directory) != 0 ) { /*...*/ }
On a 10 TB hard-drive (not particularly fast!) with 5 million files and 70k folders retrieving the nodes takes around 60 seconds using minimal retrieval mode - see later
Searching the collection of Inode(s) - in my case searching for all folders with archive in the name - took less than 500ms.
Because of the huge difference in times for these two activities, I find it best to retrieve all nodes (60 seconds) once and then perform repeated searches (~500ms) on the node collection.
The node retrieval only needs to be re-run if folders or files have changed.
You have to add a reference to ntfsreader and an example of its use is shown in the following pseudocode:
using System.IO.Filesystem.Ntfs;
public IEnumerable<INode> nodes;
public void DoStuff
{
// Obviously you can also use whatever you like to store this collection; array, dictionary, datatable, etc.
public List<string> foundObjects = new List<string>();
// string drive is "c" or "d" or whatever. Note that there is no "c:\" or "c:".
// Just "c" or "d" for the string drive argument.
// Read the NTFS MFT
RefreshNodes("f");
// If, for example, you are looking for a folder with a given name then
string strPattern="archive";
foreach (INode node in nodes)
{
if ((node.Attributes & Attributes.Directory) != 0 && node.Name.Contains(strPattern))
{
foundObjects.Add(node.FullName.Replace(#"\\", #"\"));
}
}
}
public void RefreshNodes(string drive)
{
DriveInfo driveToAnalyze = new DriveInfo(drive);
NtfsReader ntfsReader = new NtfsReader(driveToAnalyze, RetrieveMode.Minimal);
nodes = ntfsReader.GetNodes(driveToAnalyze.Name);
}
The time taken to retrieve the nodes depends on the RetrieveMode you use. RetrieveMode.Minimal brings back most of the information I need and it is quicker.
Hope this helps and happy NTFSing!
Related
I am trying to take some old code written by my predecessor in C and migrate it to C#. I have tried using the P/invoke way but running into issues with sprint_s. Any recommendation on how to fix this or maybe write it using C#'s SerialPort class?
[StructLayout(LayoutKind.Sequential)]
internal struct Dcb
{
internal uint DCBLength;
internal uint BaudRate;
private BitVector32 Flags;
private ushort wReserved; // not currently used
internal ushort XonLim; // transmit XON threshold
internal ushort XoffLim; // transmit XOFF threshold
internal byte ByteSize;
internal Parity Parity;
internal StopBits StopBits;
internal sbyte XonChar; // Tx and Rx XON character
internal sbyte XoffChar; // Tx and Rx XOFF character
internal sbyte ErrorChar; // error replacement character
internal sbyte EofChar; // end of input character
internal sbyte EvtChar; // received event character
private ushort wReserved1; // reserved; do not use
private static readonly int fBinary;
private static readonly int fParity;
private static readonly int fOutxCtsFlow;
private static readonly int fOutxDsrFlow;
private static readonly BitVector32.Section fDtrControl;
private static readonly int fDsrSensitivity;
private static readonly int fTXContinueOnXoff;
private static readonly int fOutX;
private static readonly int fInX;
private static readonly int fErrorChar;
private static readonly int fNull;
private static readonly BitVector32.Section fRtsControl;
private static readonly int fAbortOnError;
static Dcb()
{
// Create Boolean Mask
int previousMask;
fBinary = BitVector32.CreateMask();
fParity = BitVector32.CreateMask(fBinary);
fOutxCtsFlow = BitVector32.CreateMask(fParity);
fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
previousMask = BitVector32.CreateMask(fOutxDsrFlow);
previousMask = BitVector32.CreateMask(previousMask);
fDsrSensitivity = BitVector32.CreateMask(previousMask);
fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
fInX = BitVector32.CreateMask(fOutX);
fErrorChar = BitVector32.CreateMask(fInX);
fNull = BitVector32.CreateMask(fErrorChar);
previousMask = BitVector32.CreateMask(fNull);
previousMask = BitVector32.CreateMask(previousMask);
fAbortOnError = BitVector32.CreateMask(previousMask);
// Create section Mask
BitVector32.Section previousSection;
previousSection = BitVector32.CreateSection(1);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fDtrControl = BitVector32.CreateSection(2, previousSection);
previousSection = BitVector32.CreateSection(1, fDtrControl);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
previousSection = BitVector32.CreateSection(1, previousSection);
fRtsControl = BitVector32.CreateSection(3, previousSection);
previousSection = BitVector32.CreateSection(1, fRtsControl);
}
public bool Binary
{
get { return Flags[fBinary]; }
set { Flags[fBinary] = value; }
}
public bool CheckParity
{
get { return Flags[fParity]; }
set { Flags[fParity] = value; }
}
public bool OutxCtsFlow
{
get { return Flags[fOutxCtsFlow]; }
set { Flags[fOutxCtsFlow] = value; }
}
public bool OutxDsrFlow
{
get { return Flags[fOutxDsrFlow]; }
set { Flags[fOutxDsrFlow] = value; }
}
public DtrControl DtrControl
{
get { return (DtrControl)Flags[fDtrControl]; }
set { Flags[fDtrControl] = (int)value; }
}
public bool DsrSensitivity
{
get { return Flags[fDsrSensitivity]; }
set { Flags[fDsrSensitivity] = value; }
}
public bool TxContinueOnXoff
{
get { return Flags[fTXContinueOnXoff]; }
set { Flags[fTXContinueOnXoff] = value; }
}
public bool OutX
{
get { return Flags[fOutX]; }
set { Flags[fOutX] = value; }
}
public bool InX
{
get { return Flags[fInX]; }
set { Flags[fInX] = value; }
}
public bool ReplaceErrorChar
{
get { return Flags[fErrorChar]; }
set { Flags[fErrorChar] = value; }
}
public bool Null
{
get { return Flags[fNull]; }
set { Flags[fNull] = value; }
}
public RtsControl RtsControl
{
get { return (RtsControl)Flags[fRtsControl]; }
set { Flags[fRtsControl] = (int)value; }
}
public bool AbortOnError
{
get { return Flags[fAbortOnError]; }
set { Flags[fAbortOnError] = value; }
}
}
public enum DtrControl : int
{
/// <summary>
/// Disables the DTR line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the DTR line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables DTR handshaking. If handshaking is enabled, it is an error for the application to adjust the line by
/// using the EscapeCommFunction function.
/// </summary>
Handshake = 2
}
public enum RtsControl : int
{
/// <summary>
/// Disables the RTS line when the device is opened and leaves it disabled.
/// </summary>
Disable = 0,
/// <summary>
/// Enables the RTS line when the device is opened and leaves it on.
/// </summary>
Enable = 1,
/// <summary>
/// Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer
/// is less than one-half full and lowers the RTS line when the buffer is more than
/// three-quarters full. If handshaking is enabled, it is an error for the application to
/// adjust the line by using the EscapeCommFunction function.
/// </summary>
Handshake = 2,
/// <summary>
/// Specifies that the RTS line will be high if bytes are available for transmission. After
/// all buffered bytes have been sent, the RTS line will be low.
/// </summary>
Toggle = 3
}
public enum Parity : byte
{
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
public enum StopBits : byte
{
One = 0,
OnePointFive = 1,
Two = 2
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(IntPtr handle,
byte[] buffer, uint toRead, ref uint read, IntPtr lpOverLapped);
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int byteCount);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);
struct COMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
struct FILE
{
IntPtr _ptr;
int _cnt;
IntPtr _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
IntPtr _tmpfname;
};
[DllImport("kernel32.dll")]
static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
IntPtr lpOverLapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FlushFileBuffers(IntPtr handle);
public bool InitSerialComms()
{
FILE file = new FILE();
COMMTIMEOUTS timeouts;
Dcb dcb = new Dcb();
long len;
char[] name = new char[10];
char[] settings = new char[40];
string str;
// Form the initialization file name
sprintf_s(str, 800, "%s\\SerialComms.ini", path);
// Open the initialization file
fopen_s(&file, str, "r");
// Check for errors
if (file)
{
Console.WriteLine("Error: cannot open file %s\n");
return false;
}
// Scan the serial port name
fgets(name, 10, file);
len = strlen(name);
name[len - 1] = 0;
// Scan the serial port settings
fgets(settings, 40, file);
len = settings.Length;
settings[len - 1] = 0;
// Scan the timeout settings
fgets(str, 40, file); len = strlen(str); string[len - 1] = 0;
sscanf_s(str, "%d,%d,%d,%d,%d",
&timeouts.ReadIntervalTimeout,
&timeouts.ReadTotalTimeoutConstant,
&timeouts.ReadTotalTimeoutMultiplier,
&timeouts.WriteTotalTimeoutConstant,
&timeouts.WriteTotalTimeoutMultiplier);
// Close the initialization file
fclose(file);
// Open the serial port
port = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
// Check for errors
if (port == INVALID_HANDLE_VALUE)
{
// Report the error and return
fprintf(stderr, "Error: cannot open serial port %s\n", name);
fflush(stderr);
return false;
}
// Build the serial port device control block
MemSet(dcb., 0, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!BuildCommDCB(settings, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot create device control block for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Configure the serial port
if (!SetCommState(port, &dcb))
{
// Report the error and return
fprintf(stderr, "Error: cannot configure serial port %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Set the timeouts for the serial port
if (!SetCommTimeouts(port, &timeouts))
{
// Report the error and return
fprintf(stderr, "Error: cannot set timeouts for %s\n", name);
CloseHandle(port);
fflush(stderr);
return false;
}
// Success
return true;
}
bool ReceiveReply(IntPtr port, ref byte[] reply, ref byte num)
{
uint num_read = 0;
uint num_to_read = 255;
ushort crc = 0XFFFF;
byte i, j;
// Clear the reply buffer
//reply = new byte[255];
num = 0;
// Read the data
if (!ReadFile(port, reply, num_to_read, ref num_read, IntPtr.Zero)) return false;
// Check number of bytes that were read
if (num_read < 2) return false;
// Check number of bytes that were read
if (num_read > 255) return false;
// Form the CRC
for (i = 0; i < num_read - 2; i++)
{
crc ^= reply[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1;
//TODO: risky flag check
if (flag == 0) crc ^= 0XA001;
}
}
// Check the CRC
if (reply[i++] != (crc & 0X00FF)) return false;
if (reply[i++] != (crc & 0XFF00) >> 8) return false;
num = (byte)(num_read - 2);
// Success
return true;
}
public static bool SendRequest(IntPtr port, ref byte[] request, ref byte num)
{
ushort crc = 0XFFFF;
byte i, j;
// Check number of bytes
if (num > 253) return false;
// Set number of bytes to write
uint num_to_write = num;
// Form the CRC
for (i = 0; i < num_to_write; i++)
{
crc ^= request[i];
for (j = 0; j < 8; j++)
{
ushort flag = (ushort) (crc & 0X0001);
crc >>= 1; if (flag == 0) crc = (ushort) (crc ^ 0XA001);
}
}
// Set the CRC bytes in the request
request[num_to_write++] = (byte) (crc & 0X00FF);
request[num_to_write++] = (byte) ((crc & 0XFF00) >> 8);
// Send the request
if (!WriteFile(port, request, num_to_write, out uint _, IntPtr.Zero)) return false;
string text = request.ToString().Substring(0, (int) num_to_write).Replace("\r\n", " ");
// Flush the serial line
if (!FlushFileBuffers(port)) return false;
// Success
return true;
}
You don't need sprintf-family functions in higher level languages like C# since they normally allow string concatenation and assignment with the simple = and += operators.
Just write idiomatic C# code for it:
str = path + "\\SerialComms.ini";
Commenter #itsme86 points out that for the task of building a path, you should instead use Path.Combine:
Path.Combine(path, "SerialComms.ini");
I have this code for connect a process to proxy, but it returns false:
The code works if I do not enter username and password in SetConnectionProxy() and send only the two options PROXY flags and PROXY name. When I add the INTERNET_PER_CONN_OPTION set username and set password, it returns false.
I do not know anything about wininet.
Please, help me. Thanks.
{
private static string progressName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; //extrae el nombre de la aplicación
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr InternetOpen(
string lpszAgent, int dwAccessType, string lpszProxyName,
string lpszProxyBypass, int dwFlags);
[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InternetCloseHandle(IntPtr hInternet);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct INTERNET_PER_CONN_OPTION_LIST
{
public int Size;
// The connection to be set. NULL means LAN.
public System.IntPtr Connection;
public int OptionCount;
public int OptionError;
// List of INTERNET_PER_CONN_OPTIONs.
public System.IntPtr pOptions;
}
private enum INTERNET_OPTION
{
// Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies
// a list of options for a particular connection.
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
// Notify the system that the registry settings have been changed so that
// it verifies the settings on the next call to InternetConnect.
INTERNET_OPTION_SETTINGS_CHANGED = 39,
// Causes the proxy data to be reread from the registry for a handle.
INTERNET_OPTION_REFRESH = 37
}
private enum INTERNET_PER_CONN_OptionEnum
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6,
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9,
INTERNET_PER_CONN_FLAGS_UI = 10,
INTERNET_OPTION_PROXY_USERNAME = 43,
INTERNET_OPTION_PROXY_PASSWORD = 44
}
private const int INTERNET_OPEN_TYPE_DIRECT = 1; // direct to net
private const int INTERNET_OPEN_TYPE_PRECONFIG = 0; // read registry
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTON struct.
/// </summary>
private enum INTERNET_OPTION_PER_CONN_FLAGS
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
/// <summary>
/// Used in INTERNET_PER_CONN_OPTION.
/// When create a instance of OptionUnion, only one filed will be used.
/// The StructLayout and FieldOffset attributes could help to decrease the struct size.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct INTERNET_PER_CONN_OPTION_OptionUnion
{
// A value in INTERNET_OPTION_PER_CONN_FLAGS.
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public System.IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
[StructLayout(LayoutKind.Sequential)]
private struct INTERNET_PER_CONN_OPTION
{
// A value in INTERNET_PER_CONN_OptionEnum.
public int dwOption;
public INTERNET_PER_CONN_OPTION_OptionUnion Value;
}
/// <summary>
/// Sets an Internet option.
/// </summary>
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool InternetSetOption(
IntPtr hInternet,
INTERNET_OPTION dwOption,
IntPtr lpBuffer,
int lpdwBufferLength);
/// <summary>
/// Queries an Internet option on the specified handle. The Handle will be always 0.
/// </summary>
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true,
EntryPoint = "InternetQueryOption")]
private extern static bool InternetQueryOptionList(
IntPtr Handle,
INTERNET_OPTION OptionFlag,
ref INTERNET_PER_CONN_OPTION_LIST OptionList,
ref int size);
/// <summary>
/// Set the proxy server for LAN connection.
/// </summary>
public static bool SetConnectionProxy(string proxyServer, string username, string password)
{
IntPtr hInternet = InternetOpen(progressName, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
// Create 4 options.
int opciones = 4;
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[opciones];
// Set PROXY flags.
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
// Set proxy name.
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);
//Set username.
Options[2] = new INTERNET_PER_CONN_OPTION();
Options[2].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_OPTION_PROXY_USERNAME;
Options[2].Value.pszValue = Marshal.StringToCoTaskMemAuto(username);
// Set password.
Options[3] = new INTERNET_PER_CONN_OPTION();
Options[3].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_OPTION_PROXY_PASSWORD;
Options[3].Value.pszValue = Marshal.StringToCoTaskMemAuto(password);
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0]) + Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]) + Marshal.SizeOf(Options[3]));
System.IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
option_list.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
option_list.Size = Marshal.SizeOf(option_list);
// IntPtr.Zero means LAN connection.
option_list.Connection = IntPtr.Zero;
option_list.OptionCount = Options.Length;
option_list.OptionError = 0;
int size = Marshal.SizeOf(option_list);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(option_list, intptrStruct, true);
// Set internet settings.
bool bReturn = InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
InternetCloseHandle(hInternet); //
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ApplicationException(" Set Internet Option Failed!");
}
return bReturn;
}
}
After a long time I found the answer and it was much simpler than it seemed. It was as simple as putting the original function, without username or password, of SetConnectionProxy () and then, in the first navigation, insert the user and password of the proxy in the url: http: // user: password # url, and I have done like this:
The connection to the proxy first:
public static bool SetConnectionProxy(string proxyServer)
{
IntPtr hInternet = InternetOpen(miniYo, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[2];
// Set PROXY flags.
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
// Set proxy name.
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0]) + Marshal.SizeOf(Options[1]));
System.IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
option_list.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
option_list.Size = Marshal.SizeOf(option_list);
// IntPtr.Zero means LAN connection.
option_list.Connection = IntPtr.Zero;
option_list.OptionCount = Options.Length;
option_list.OptionError = 0;
int size = Marshal.SizeOf(option_list);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(option_list, intptrStruct, true);
// Set internet settings.
bool bReturn = InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
InternetCloseHandle(hInternet); //¿Esto lo cierra?
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ApplicationException(" Set Internet Option Failed!");
}
return bReturn;
}
}
The first navigation with the user and the password, after:
UriBuilder uriSite = new UriBuilder(string);
uriSite.UserName = usuario;
uriSite.Password = contraseña;
webBrowser.Navigate(uriSite.Uri);
I regret to say that I did not feel helped by the community
Info:
.Net 4.5
Tested on:
Win7 64 bit
Win10 64 bit (Virtual Box)
I am trying to get a list of handles of an external process and return their names as string so I can close a specific one afterwards. Therefore i wrote this function using the Win32API which will check if the handle is the handle i want to close: `
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
public static string getObjectTypeName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
{
IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
IntPtr ipHandle = IntPtr.Zero;
var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
IntPtr ipObjectType = IntPtr.Zero;
IntPtr ipObjectName = IntPtr.Zero;
string strObjectTypeName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle,
Win32API.GetCurrentProcess(), out ipHandle,
0, false, Win32API.DUPLICATE_SAME_ACCESS))
return null;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation,
ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(
ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType,
nLength, ref nLength)) ==
Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectType.Name.Buffer;
}
strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
Marshal.FreeHGlobal(ipObjectType);
Win32API.CloseHandle(ipHandle);
return strObjectTypeName;
}`
The problem however is that this code works in Win7 64bit, not in Win10! --> In Win 10 strObjectTypeName = Marshal.PtrToStringUni(); throws a AcessViolationException (Last few lines in the code)
System.AccessViolationException Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Am I missing something here about how unmanaged memory has to be accessed in win10?
I have just come across the same issue. I haven't tried Win7, but when you run the code on Win10(x64) as 32bit (e.g. set the "Prefer 32-bit flag" of your application) it should work.
When the exception happens, drag&drop the variable "ipTemp" to the "memory window" of Visual Studio, if it displays only question marks or an error message you don't have a valid pointer.
As far as I figured out, there are (more) padding bytes in the 64bit versions of the structs that are used by this API:
OBJECT_TYPE_INFORMATION contains a UNICODE_STRING and UNICODE_STRING has 4 padding bytes before the Buffer-field in 64bit Mode.
My workaraound was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UNICODE_STRING
{
private IntPtr _dummy; // the two ushorts seem to be padded with 4 bytes in 64bit mode only
/// <summary>
/// The length, in bytes, of the string stored in Buffer. If the string is null-terminated, Length does not include the trailing null character.
/// </summary>
public ushort Length
{
get { return (ushort)Marshal.ReadInt16(this, 0); }
}
/// <summary>
/// The length, in bytes, of Buffer.
/// </summary>
public ushort MaximumLength
{
get { return (ushort)Marshal.ReadInt16(this, 2); }
}
public IntPtr Buffer;
}
During my research I found so many questions regarding this topic and basically two kinds of sample code
that have been copied all over the Internet that I am considering to create an open-source library named WinKernelObjectsDotNet.
Update: The library is now available here. It supports finding the process that is locking a file or a serial-port (COM) with a single line of code.
I suggest to change UNICODE_STRING structure in a such way.
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)] public string Buffer;
}
So getObjectTypeName method will be something like this and will work for both 32/64:
public static string getObjectTypeName(SYSTEM_HANDLE_INFORMATION shHandle, Process process) {
IntPtr ipProcessHwnd = OpenProcess(ProcessAccessFlags.All, false, process.Id);
if (!DuplicateHandle(ipProcessHwnd, shHandle.Handle, GetCurrentProcess(), out IntPtr ipHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
return null;
}
OBJECT_BASIC_INFORMATION objBasicInformation = new OBJECT_BASIC_INFORMATION();
IntPtr ipBasicInformation = Marshal.AllocHGlobal(Marshal.SizeOf(objBasicInformation));
int iBasicInformationLength = 0;
NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectBasicInformation, ipBasicInformation, Marshal.SizeOf(objBasicInformation), ref iBasicInformationLength);
objBasicInformation = (OBJECT_BASIC_INFORMATION) Marshal.PtrToStructure(ipBasicInformation, typeof(OBJECT_BASIC_INFORMATION));
Marshal.FreeHGlobal(ipBasicInformation);
int iObjectTypeInformationLength = objBasicInformation.TypeInformationLength;
IntPtr ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
while (Win32API.STATUS_INFO_LENGTH_MISMATCH == (uint) (NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectTypeInformation, ipObjectTypeInformation, iObjectTypeInformationLength, ref iObjectTypeInformationLength))) {
Marshal.FreeHGlobal(ipObjectTypeInformation);
ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
}
CloseHandle(ipHandle);
OBJECT_TYPE_INFORMATION objObjectType = (OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectTypeInformation, typeof(OBJECT_TYPE_INFORMATION));
Marshal.FreeHGlobal(ipObjectTypeInformation);
return objObjectType.Name.Buffer;
}
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);
}
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.