diff --git a/trunk/ProcessHacker.Native/Api/NativeEnums.cs b/trunk/ProcessHacker.Native/Api/NativeEnums.cs index 2811c7176..ef9c95882 100644 --- a/trunk/ProcessHacker.Native/Api/NativeEnums.cs +++ b/trunk/ProcessHacker.Native/Api/NativeEnums.cs @@ -1551,7 +1551,7 @@ namespace ProcessHacker.Native.Api FullDuplex = 2 } - public enum PipeState : uint + public enum PipeState : int { Disconnected = 1, Listening = 2, diff --git a/trunk/ProcessHacker.Native/Objects/FileHandle.cs b/trunk/ProcessHacker.Native/Objects/FileHandle.cs index a62a106f3..c92ae1fd2 100644 --- a/trunk/ProcessHacker.Native/Objects/FileHandle.cs +++ b/trunk/ProcessHacker.Native/Objects/FileHandle.cs @@ -510,12 +510,12 @@ namespace ProcessHacker.Native.Objects } } - public void BeginRead(AsyncIoContext asyncContext, byte[] buffer) + public void BeginRead(AsyncIoContext asyncContext, long fileOffset, byte[] buffer) { - this.BeginRead(asyncContext, buffer, 0, buffer.Length); + this.BeginRead(asyncContext, fileOffset, buffer, 0, buffer.Length); } - public void BeginRead(AsyncIoContext asyncContext, byte[] buffer, int offset, int length) + public void BeginRead(AsyncIoContext asyncContext, long fileOffset, byte[] buffer, int offset, int length) { PinnedObject pinnedBuffer; @@ -526,31 +526,34 @@ namespace ProcessHacker.Native.Objects // this call returns. pinnedBuffer = new PinnedObject(buffer); asyncContext.KeepAlive(pinnedBuffer); - this.BeginRead(asyncContext, pinnedBuffer.Address.Increment(offset), length); + this.BeginRead(asyncContext, fileOffset, pinnedBuffer.Address.Increment(offset), length); } - public void BeginRead(AsyncIoContext asyncContext, MemoryRegion buffer) + public void BeginRead(AsyncIoContext asyncContext, long fileOffset, MemoryRegion buffer) { asyncContext.NotifyPreBegin(); asyncContext.KeepAlive(buffer); - this.BeginRead(asyncContext, buffer, buffer.Size); + this.BeginRead(asyncContext, fileOffset, buffer, buffer.Size); } - protected void BeginRead(AsyncIoContext asyncContext, IntPtr buffer, int length) + protected void BeginRead(AsyncIoContext asyncContext, long fileOffset, IntPtr buffer, int length) { NtStatus status; - status = Win32.NtReadFile( - this, - asyncContext.EventHandle ?? IntPtr.Zero, - null, - asyncContext.Context, - asyncContext.StatusMemory, - buffer, - length, - IntPtr.Zero, - IntPtr.Zero - ); + unsafe + { + status = Win32.NtReadFile( + this, + asyncContext.EventHandle ?? IntPtr.Zero, + null, + asyncContext.Context, + asyncContext.StatusMemory, + buffer, + length, + fileOffset != -1 ? new IntPtr(&fileOffset) : IntPtr.Zero, + IntPtr.Zero + ); + } asyncContext.NotifyBegin(); @@ -562,12 +565,12 @@ namespace ProcessHacker.Native.Objects } } - public void BeginWrite(AsyncIoContext asyncContext, byte[] buffer) + public void BeginWrite(AsyncIoContext asyncContext, long fileOffset, byte[] buffer) { - this.BeginWrite(asyncContext, buffer, 0, buffer.Length); + this.BeginWrite(asyncContext, fileOffset, buffer, 0, buffer.Length); } - public void BeginWrite(AsyncIoContext asyncContext, byte[] buffer, int offset, int length) + public void BeginWrite(AsyncIoContext asyncContext, long fileOffset, byte[] buffer, int offset, int length) { PinnedObject pinnedBuffer; @@ -576,31 +579,34 @@ namespace ProcessHacker.Native.Objects pinnedBuffer = new PinnedObject(buffer); asyncContext.KeepAlive(pinnedBuffer); - this.BeginWrite(asyncContext, pinnedBuffer.Address.Increment(offset), length); + this.BeginWrite(asyncContext, fileOffset, pinnedBuffer.Address.Increment(offset), length); } - public void BeginWrite(AsyncIoContext asyncContext, MemoryRegion buffer) + public void BeginWrite(AsyncIoContext asyncContext, long fileOffset, MemoryRegion buffer) { asyncContext.NotifyPreBegin(); asyncContext.KeepAlive(buffer); - this.BeginWrite(asyncContext, buffer, buffer.Size); + this.BeginWrite(asyncContext, fileOffset, buffer, buffer.Size); } - protected void BeginWrite(AsyncIoContext asyncContext, IntPtr buffer, int length) + protected void BeginWrite(AsyncIoContext asyncContext, long fileOffset, IntPtr buffer, int length) { NtStatus status; - status = Win32.NtWriteFile( - this, - asyncContext.EventHandle ?? IntPtr.Zero, - null, - asyncContext.Context, - asyncContext.StatusMemory, - buffer, - length, - IntPtr.Zero, - IntPtr.Zero - ); + unsafe + { + status = Win32.NtWriteFile( + this, + asyncContext.EventHandle ?? IntPtr.Zero, + null, + asyncContext.Context, + asyncContext.StatusMemory, + buffer, + length, + fileOffset != -1 ? new IntPtr(&fileOffset) : IntPtr.Zero, + IntPtr.Zero + ); + } asyncContext.NotifyBegin(); @@ -612,6 +618,10 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Cancels all asynchronous file operations initiated on the file object by the current thread. + /// + /// An I/O status block. public IoStatusBlock CancelIo() { NtStatus status; @@ -623,14 +633,6 @@ namespace ProcessHacker.Native.Objects return isb; } - internal void CancelIo(IntPtr isb) - { - NtStatus status; - - if ((status = Win32.NtCancelIoFile(this, isb)) >= NtStatus.Error) - Win32.ThrowLastError(status); - } - /// /// Deletes the file when the handle is closed. /// @@ -642,6 +644,11 @@ namespace ProcessHacker.Native.Objects ); } + /// + /// Waits for an asynchronous file operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// The operation-specific result. protected int EndCommonIo(AsyncIoContext asyncContext) { asyncContext.Wait(); @@ -653,16 +660,31 @@ namespace ProcessHacker.Native.Objects return asyncContext.StatusBlock.Information.ToInt32(); } + /// + /// Waits for an asynchronous file system control operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// The bytes returned in the output buffer. public int EndFsControl(AsyncIoContext asyncContext) { return this.EndCommonIo(asyncContext); } + /// + /// Waits for an asynchronous I/O control operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// The bytes returned in the output buffer. public int EndIoControl(AsyncIoContext asyncContext) { return this.EndCommonIo(asyncContext); } + /// + /// Waits for an asynchronous lock operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// True if the lock was acquired, otherwise false. public bool EndLock(AsyncIoContext asyncContext) { asyncContext.Wait(); @@ -677,16 +699,30 @@ namespace ProcessHacker.Native.Objects return true; } + /// + /// Waits for an asynchronous read operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// The number of bytes read as a result of the operation. public int EndRead(AsyncIoContext asyncContext) { return this.EndCommonIo(asyncContext); } + /// + /// Waits for an asynchronous write operation to complete. + /// + /// An asynchronous I/O context object representing the operation. + /// The number of bytes written as a result of the operation. public int EndWrite(AsyncIoContext asyncContext) { return this.EndCommonIo(asyncContext); } + /// + /// Enumerates the files contained in the directory. + /// + /// The callback function to use. public void EnumFiles(EnumFilesDelegate callback) { NtStatus status; @@ -777,6 +813,10 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Enumerates the streams contained in the file. + /// + /// The callback function to use. public void EnumStreams(EnumStreamsDelegate callback) { using (var data = this.QueryVariableSize(FileInformationClass.FileStreamInformation)) @@ -802,6 +842,9 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Flushes the buffers for the file. + /// public void Flush() { NtStatus status; @@ -822,6 +865,13 @@ namespace ProcessHacker.Native.Objects Win32.ThrowLastError(status); } + /// + /// Sends a file system control message to the device's associated driver. + /// + /// The device-specific control code. + /// The input buffer. + /// The output buffer. + /// The bytes returned in the output buffer. public int FsControl(int controlCode, byte[] inBuffer, byte[] outBuffer) { return this.FsControl( @@ -933,16 +983,28 @@ namespace ProcessHacker.Native.Objects return status; } + /// + /// Gets the attributes of the file. + /// + /// The attributes of the file. public FileAttributes GetAttributes() { return this.GetBasicInformation().FileAttributes; } + /// + /// Gets basic information about the file. + /// + /// Basic information about the file. public FileBasicInformation GetBasicInformation() { return this.QueryStruct(FileInformationClass.FileBasicInformation); } + /// + /// Gets the partial name of the file. + /// + /// The name of the file, relative to its volume. public string GetFileName() { using (var data = this.QueryVariableSize(FileInformationClass.FileNameInformation)) @@ -956,6 +1018,10 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Gets a list of the files contained in the directory. + /// + /// An array of file information structures. public FileEntry[] GetFiles() { List files = new List(); @@ -969,11 +1035,19 @@ namespace ProcessHacker.Native.Objects return files.ToArray(); } + /// + /// Gets the current position. + /// + /// A byte offset from the beginning of the file. public long GetPosition() { return this.QueryStruct(FileInformationClass.FilePositionInformation).CurrentByteOffset; } + /// + /// Gets a list of the streams contained in the file. + /// + /// An array of stream information structures. public FileStreamEntry[] GetStreams() { List streams = new List(); @@ -987,16 +1061,28 @@ namespace ProcessHacker.Native.Objects return streams.ToArray(); } + /// + /// Gets the size of the file. + /// + /// The size of the file. public long GetSize() { return this.GetStandardInformation().EndOfFile; } + /// + /// Gets standard information about the file. + /// + /// Standard information about the file. public FileStandardInformation GetStandardInformation() { return this.QueryStruct(FileInformationClass.FileStandardInformation); } + /// + /// Gets the file system name of the file's associated volume. + /// + /// The volume's file system name. public string GetVolumeFsName() { NtStatus status; @@ -1022,6 +1108,10 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Gets the label of the file's associated volume. + /// + /// The volume label. public string GetVolumeLabel() { NtStatus status; @@ -1051,7 +1141,7 @@ namespace ProcessHacker.Native.Objects /// Sends an I/O control message to the device's associated driver. /// /// The device-specific control code. - /// The input. + /// The input buffer. /// The output buffer. /// The bytes returned in the output buffer. public int IoControl(int controlCode, byte[] inBuffer, byte[] outBuffer) @@ -1184,16 +1274,45 @@ namespace ProcessHacker.Native.Objects return status; } + /// + /// Locks a byte range in the file. + /// + /// The starting offset of the byte range. + /// The length of the byte range. + /// True if the lock was acquired, otherwise false. public bool Lock(long offset, long length) { return this.Lock(offset, length, false); } + /// + /// Locks a byte range in the file. + /// + /// The starting offset of the byte range. + /// The length of the byte range. + /// + /// True to wait for the lock to be acquired, false to return if the + /// lock cannot be acquired immediately. + /// + /// True if the lock was acquired, otherwise false. public bool Lock(long offset, long length, bool wait) { return this.Lock(offset, length, wait, true); } + /// + /// Locks a byte range in the file. + /// + /// The starting offset of the byte range. + /// The length of the byte range. + /// + /// True to wait for the lock to be acquired, false to return if the + /// lock cannot be acquired immediately. + /// + /// + /// True to acquire an exclusive lock, false to acquire a shared lock. + /// + /// True if the lock was acquired, otherwise false. public bool Lock(long offset, long length, bool wait, bool exclusive) { NtStatus status; @@ -1307,7 +1426,30 @@ namespace ProcessHacker.Native.Objects return this.Read(buffer, 0, buffer.Length); } + /// + /// Reads data from the file. + /// + /// The data. + /// The offset into the buffer to use. + /// The number of bytes to read. + /// The number of bytes read from the file. public int Read(byte[] buffer, int offset, int length) + { + return this.Read(-1, buffer, offset, length); + } + + /// + /// Reads data from the file. + /// + /// + /// The offset into the file to start reading from. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The offset into the buffer to use. + /// The number of bytes to read. + /// The number of bytes read from the file. + public int Read(long fileOffset, byte[] buffer, int offset, int length) { Utils.ValidateBuffer(buffer, offset, length); @@ -1315,32 +1457,77 @@ namespace ProcessHacker.Native.Objects { fixed (byte* bufferPtr = buffer) { - return this.Read(&bufferPtr[offset], length); + return this.Read(fileOffset, &bufferPtr[offset], length); } } } + /// + /// Reads data from the file. + /// + /// The data. + /// The number of bytes to read. + /// The number of bytes read from the file. public unsafe int Read(void* buffer, int length) { - return this.Read(new IntPtr(buffer), length); + return this.Read(-1, buffer, length); } + /// + /// Reads data from the file. + /// + /// + /// The offset into the file to start reading from. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The number of bytes to read. + /// The number of bytes read from the file. + public unsafe int Read(long fileOffset, void* buffer, int length) + { + return this.Read(fileOffset, new IntPtr(buffer), length); + } + + /// + /// Reads data from the file. + /// + /// The data. + /// The number of bytes to read. + /// The number of bytes read from the file. public int Read(IntPtr buffer, int length) + { + return this.Read(-1, buffer, length); + } + + /// + /// Reads data from the file. + /// + /// + /// The offset into the file to start reading from. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The number of bytes to read. + /// The number of bytes read from the file. + public int Read(long fileOffset, IntPtr buffer, int length) { NtStatus status; IoStatusBlock isb; - status = Win32.NtReadFile( - this, - IntPtr.Zero, - null, - IntPtr.Zero, - out isb, - buffer, - length, - IntPtr.Zero, - IntPtr.Zero - ); + unsafe + { + status = Win32.NtReadFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + buffer, + length, + fileOffset != -1 ? new IntPtr(&fileOffset) : IntPtr.Zero, + IntPtr.Zero + ); + } if (status == NtStatus.Pending) { @@ -1354,6 +1541,10 @@ namespace ProcessHacker.Native.Objects return isb.Information.ToInt32(); } + /// + /// Truncates or extends the file. + /// + /// A byte offset from the beginning of the file. public void SetEnd(long offset) { this.SetStruct( @@ -1362,16 +1553,29 @@ namespace ProcessHacker.Native.Objects ); } + /// + /// Associates an I/O completion port with the file object. + /// + /// An asynchronous I/O completion port. public void SetIoCompletion(AsyncIoCompletionPort asyncCompletionPort) { this.SetIoCompletion(asyncCompletionPort.Handle); } + /// + /// Associates an I/O completion port with the file object. + /// + /// A handle to an I/O completion port. public void SetIoCompletion(IoCompletionHandle ioCompletionHandle) { this.SetIoCompletion(ioCompletionHandle, IntPtr.Zero); } + /// + /// Associates an I/O completion port with the file object. + /// + /// A handle to an I/O completion port. + /// A key to associate with the file object. public void SetIoCompletion(IoCompletionHandle ioCompletionHandle, IntPtr keyContext) { FileCompletionInformation info = new FileCompletionInformation(); @@ -1381,6 +1585,10 @@ namespace ProcessHacker.Native.Objects this.SetStruct(FileInformationClass.FileCompletionInformation, info); } + /// + /// Sets the current file position. + /// + /// A byte offset from the beginning of the file. public void SetPosition(long offset) { this.SetStruct( @@ -1389,6 +1597,11 @@ namespace ProcessHacker.Native.Objects ); } + /// + /// Sets the current file position. + /// + /// A byte offset. + /// The origin from which the offset is calculated. public long SetPosition(long offset, PositionOrigin origin) { long currentPosition; @@ -1434,6 +1647,11 @@ namespace ProcessHacker.Native.Objects } } + /// + /// Unlocks a byte range in the file. + /// + /// The starting offset of the byte range. + /// The length of the byte range. public void Unlock(long offset, long length) { NtStatus status; @@ -1461,7 +1679,30 @@ namespace ProcessHacker.Native.Objects return this.Write(buffer, 0, buffer.Length); } + /// + /// Writes data to the file. + /// + /// The data. + /// The offset into the buffer to use. + /// The number of bytes to write. + /// The number of bytes written to the file. public int Write(byte[] buffer, int offset, int length) + { + return this.Write(-1, buffer, offset, length); + } + + /// + /// Writes data to the file. + /// + /// + /// The offset into the file to start writing at. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The offset into the buffer to use. + /// The number of bytes to write. + /// The number of bytes written to the file. + public int Write(long fileOffset, byte[] buffer, int offset, int length) { Utils.ValidateBuffer(buffer, offset, length); @@ -1469,15 +1710,36 @@ namespace ProcessHacker.Native.Objects { fixed (byte* bufferPtr = buffer) { - return this.Write(&bufferPtr[offset], length); + return this.Write(fileOffset, &bufferPtr[offset], length); } } } + /// + /// Writes data to the file. + /// + /// The data. + /// The number of bytes to write. + /// The number of bytes written to the file. public unsafe int Write(void* buffer, int length) { - return this.Write(new IntPtr(buffer), length); - } + return this.Write(-1, buffer, length); + } + + /// + /// Writes data to the file. + /// + /// + /// The offset into the file to start writing at. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The number of bytes to write. + /// The number of bytes written to the file. + public unsafe int Write(long fileOffset, void* buffer, int length) + { + return this.Write(fileOffset, new IntPtr(buffer), length); + } /// /// Writes data to the file. @@ -1486,21 +1748,39 @@ namespace ProcessHacker.Native.Objects /// The number of bytes to write. /// The number of bytes written to the file. public int Write(IntPtr buffer, int length) + { + return this.Write(-1, buffer, length); + } + + /// + /// Writes data to the file. + /// + /// + /// The offset into the file to start writing at. Specify -1 to + /// use the file object's current position. + /// + /// The data. + /// The number of bytes to write. + /// The number of bytes written to the file. + public int Write(long fileOffset, IntPtr buffer, int length) { NtStatus status; IoStatusBlock isb; - status = Win32.NtWriteFile( - this, - IntPtr.Zero, - null, - IntPtr.Zero, - out isb, - buffer, - length, - IntPtr.Zero, - IntPtr.Zero - ); + unsafe + { + status = Win32.NtWriteFile( + this, + IntPtr.Zero, + null, + IntPtr.Zero, + out isb, + buffer, + length, + fileOffset != -1 ? new IntPtr(&fileOffset) : IntPtr.Zero, + IntPtr.Zero + ); + } if (status == NtStatus.Pending) { @@ -1558,11 +1838,17 @@ namespace ProcessHacker.Native.Objects public AsyncIoContext Remove(long timeout, bool relative) { + bool result; AsyncIoContext asyncContext; + IoStatusBlock isb; IntPtr keyContext; IntPtr apcContext; - _ioCompletionHandle.Remove(out keyContext, out apcContext, relative ? -timeout : timeout, false); + result = _ioCompletionHandle.Remove(out isb, out keyContext, out apcContext, relative ? -timeout : timeout, false); + + // Fail? + if (!result) + return null; asyncContext = AsyncIoContext.GetAsyncIoContext(apcContext); asyncContext.NotifyEnd(); @@ -1594,7 +1880,12 @@ namespace ProcessHacker.Native.Objects public UnmanagedIsb() { + // Allocate an ISB. _ioStatusBlock = (IoStatusBlock*)MemoryAlloc.PrivateHeap.Allocate(0, _isbSize); + // Zero the ISB. + _ioStatusBlock->Pointer = IntPtr.Zero; + _ioStatusBlock->Information = IntPtr.Zero; + _ioStatusBlock->Status = 0; } protected override void DisposeObject(bool disposing) diff --git a/trunk/ProcessHacker.Native/Objects/IoCompletionHandle.cs b/trunk/ProcessHacker.Native/Objects/IoCompletionHandle.cs index a0e905c32..8b84dadc3 100644 --- a/trunk/ProcessHacker.Native/Objects/IoCompletionHandle.cs +++ b/trunk/ProcessHacker.Native/Objects/IoCompletionHandle.cs @@ -92,22 +92,21 @@ namespace ProcessHacker.Native.Objects : this(name, 0, null, access) { } - public IoStatusBlock Remove(out IntPtr keyContext, out IntPtr apcContext, long timeout) + public bool Remove(out IoStatusBlock isb, out IntPtr keyContext, out IntPtr apcContext, long timeout) { - return this.Remove(out keyContext, out apcContext, timeout, true); + return this.Remove(out isb, out keyContext, out apcContext, timeout, true); } - public IoStatusBlock Remove(out IntPtr keyContext, out IntPtr apcContext, long timeout, bool relative) + public bool Remove(out IoStatusBlock isb, out IntPtr keyContext, out IntPtr apcContext, long timeout, bool relative) { NtStatus status; - IoStatusBlock ioStatus; long realTimeout = relative ? -timeout : timeout; if ((status = Win32.NtRemoveIoCompletion( - this, out keyContext, out apcContext, out ioStatus, ref realTimeout)) >= NtStatus.Error) + this, out keyContext, out apcContext, out isb, ref realTimeout)) >= NtStatus.Error) Win32.ThrowLastError(status); - return ioStatus; + return status != NtStatus.Timeout; } public void Set(IntPtr keyContext, IntPtr apcContext, NtStatus ioStatus, IntPtr ioInformation) diff --git a/trunk/ProcessHacker.Native/Objects/NamedPipeHandle.cs b/trunk/ProcessHacker.Native/Objects/NamedPipeHandle.cs index f95eff612..f19423074 100644 --- a/trunk/ProcessHacker.Native/Objects/NamedPipeHandle.cs +++ b/trunk/ProcessHacker.Native/Objects/NamedPipeHandle.cs @@ -47,7 +47,10 @@ namespace ProcessHacker.Native.Objects FileAccess access, string fileName, PipeType type, + FileCreateOptions createOptions, int maximumInstances, + int inboundQuota, + int outboundQuota, long defaultTimeout ) { @@ -58,13 +61,13 @@ namespace ProcessHacker.Native.Objects null, FileShareMode.ReadWrite, FileCreationDisposition.OpenIf, - FileCreateOptions.NonDirectoryFile, + createOptions, type, type, PipeCompletionMode.Queue, maximumInstances, - 0, - 0, + inboundQuota, + outboundQuota, defaultTimeout ); } @@ -91,6 +94,10 @@ namespace ProcessHacker.Native.Objects IoStatusBlock isb; IntPtr handle; + // If a timeout wasn't specified, use a default value. + if (defaultTimeout == 0) + defaultTimeout = -50 * Win32.TimeMsTo100Ns; // 50 milliseconds + try { if ((status = Win32.NtCreateNamedPipeFile(