How do I subscribe to Windows Low memory notification from c# ?
our c# app has substantial unmanaged memory allocation, which we can free if OS memory availability is low.
Using CreateMemoryResourceNotification and QueryMemoryResourceNotification to check memory status
enum MemoryResourceNotificationType : int
{
LowMemoryResourceNotification = 0,
HighMemoryResourceNotification = 1,
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateMemoryResourceNotification(MemoryResourceNotificationType notificationType);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool QueryMemoryResourceNotification(IntPtr resourceNotificationHandle, out int resourceState);
private static IntPtr MemoryResourceNotificationHandle;
public static void TryReclaim()
{
MemoryResourceNotificationHandle = CreateMemoryResourceNotification(MemoryResourceNotificationType.LowMemoryResourceNotification);
int sleepIntervalInMs = ReclaimIntervalInSeconds * 1000;
while (true)
{
Thread.Sleep(10_000);
bool isSuccecced = QueryMemoryResourceNotification(MemoryResourceNotificationHandle, out int memoryStatus);
if (isSuccecced)
{
if (memoryStatus >= 1)
{
DoReclaim();
}
}
}
}
Related
I'm using a MemoryMappedFile in .NET 5 and encounter a problem that seemed to have been solved long ago.
I use it for inter-process communication so it must be located in ring 0. For that to achieve I use the name prefix Global:
var mmf = MemoryMappedFile.CreateOrOpen(#"Global\test", 1024, MemoryMappedFileAccess.ReadWrite);
This works fine for privileged processes.
For non-privileged ones I want to use
mmf = MemoryMappedFile.OpenExisting(#"Global\test", MemoryMappedFileRights.ReadWrite);
But that fails with an "UnauthorizedAccessException".
The solution that worked for .NET Framework up to 4.8 was to explicitly set MemoryMappedFileSecurity as described e.g. here: Gaining access to a MemoryMappedFile from low-integrity process
Unfortunately the MemoryMappedFileSecurity does no longer seem to exist and MemoryMappedFile.OpenExisting (or CreateNew) do no have an overload for that or similar either.
Interim solution
.Net5 mostlikely won't get that missing feature. See https://github.com/dotnet/runtime/issues/941
As a workaround I use the P/Invoke approach. In case someone has the same problem here is a working solution using null DACL to grant all access
private void CreateFile()
{
try
{
using(var secAttribs = CreateSecAttribs())
{
memBuffer = NativeMethods.CreateFileMapping(
UIntPtr.MaxValue,
secAttribs,
(uint)FileMapProtection.PAGE_READWRITE,
0,
(uint)MemOffset.TotalSize,
BufferName);
if (memBuffer == IntPtr.Zero)
{
uint lasterror = NativeMethods.GetLastError();
throw new Win32Exception((int)lasterror, string.Format(CultureInfo.InvariantCulture, "Error creating shared memory. Errorcode is {0}", lasterror));
}
IntPtr accessor = NativeMethods.MapViewOfFile(memBuffer, (uint)ViewAccess.FILE_MAP_ALL_ACCESS, 0, 0, (int)MemOffset.TotalSize);
if (accessor == IntPtr.Zero)
{
uint lasterror = NativeMethods.GetLastError();
throw new Win32Exception((int)lasterror, string.Format(CultureInfo.InvariantCulture, "Error creating shared memory view. Errorcode is {0}", lasterror));
}
//Do sth with accessor using System.Runtime.InteropServices.Marshal
}
}
catch (Exception)
{
memBuffer = IntPtr.Zero;
throw;
}
}
private static NativeMethods.SECURITY_ATTRIBUTES CreateSecAttribs()
{
//Create the descriptor with a null DACL --> Everything is granted.
RawSecurityDescriptor sec = new RawSecurityDescriptor(ControlFlags.DiscretionaryAclPresent, null, null, null, null);
return new NativeMethods.SECURITY_ATTRIBUTES(sec);
}
For reference:
internal static class NativeMethods
{
#region Structures
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES : IDisposable
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
public SECURITY_ATTRIBUTES()
{
nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES));
lpSecurityDescriptor = IntPtr.Zero;
bInheritHandle = 1;
}
public SECURITY_ATTRIBUTES(RawSecurityDescriptor sec) :
this()
{
byte[] binDACL = new byte[sec.BinaryLength];
sec.GetBinaryForm(binDACL, 0);
lpSecurityDescriptor = Marshal.AllocHGlobal(sec.BinaryLength);
Marshal.Copy(binDACL, 0, lpSecurityDescriptor, sec.BinaryLength);
}
~SECURITY_ATTRIBUTES()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
if (lpSecurityDescriptor != IntPtr.Zero)
{
Marshal.FreeHGlobal(lpSecurityDescriptor);
lpSecurityDescriptor = IntPtr.Zero;
}
}
}
#endregion
#region General imports
[DllImport("kernel32", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int CloseHandle(IntPtr hHandle);
[DllImport("kernel32", EntryPoint = "GetLastError", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint GetLastError();
#endregion
#region Memory Mapped Files imports
[DllImport("kernel32.dll", EntryPoint = "CreateFileMapping", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateFileMapping(UIntPtr hFile, SECURITY_ATTRIBUTES lpAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", EntryPoint = "MapViewOfFile", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint /* UIntPtr */ dwNumberOfBytesToMap);
[DllImport("kernel32.dll", EntryPoint = "UnmapViewOfFile", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.VariantBool)]
internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
#endregion
}
Update
Still no official integration until .Net 7
Adam Sitnik was so friendly to manually recreate MemoryMappedFileSecurity and Wrappers for .Net7.
See https://gist.github.com/adamsitnik/6370b05b9e80bc14b62ac6efe5d1e2e2#file-mmfs-cs-L41-L184
You might need to add CreateOrOpen() based on .Net4 or remove the ERROR_ALREADY_EXIST error case in CreateCore()
Managed Solution
This works when first called by a windows service and then by a user program (and of course vv).
var everyoneRule = new AccessRule<MemoryMappedFileRights>(
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
MemoryMappedFileRights.ReadWrite,
AccessControlType.Allow);
MemoryMappedFileSecurity mmfSec = new MemoryMappedFileSecurity();
mmfSec.AddAccessRule(everyoneRule);
mmf = MemoryMappedFileFactory.Create(#"Global\Test", 1024, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, mmfSec, HandleInheritability.None);
I'm trying to crash my Console application after a certain amount of time (this is due to me testing whether the application will start itself after crashing. Following this tutorial)
What I have for this is this piece of code:
static class WebSocket
{
static int Main(string[] args)
{
Recovery.RegisterForAutostart();
Recovery.RegisterForRestart();
Test.Run();
// some more code
}
}
public static class Recovery
{
[Flags]
public enum RestartRestrictions
{
None = 0,
NotOnCrash = 1,
NotOnHang = 2,
NotOnPatch = 4,
NotOnReboot = 8
}
public delegate int RecoveryDelegate(RecoveryData parameter);
public static class ArrImports
{
[DllImport("kernel32.dll")]
public static extern void ApplicationRecoveryFinished(
bool success);
[DllImport("kernel32.dll")]
public static extern int ApplicationRecoveryInProgress(
out bool canceled);
[DllImport("kernel32.dll")]
public static extern int GetApplicationRecoveryCallback(
IntPtr processHandle,
out RecoveryDelegate recoveryCallback,
out RecoveryData parameter,
out uint pingInterval,
out uint flags);
[DllImport("KERNEL32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetApplicationRestartSettings(
IntPtr process,
IntPtr commandLine,
ref uint size,
out uint flags);
[DllImport("kernel32.dll")]
public static extern int RegisterApplicationRecoveryCallback(
RecoveryDelegate recoveryCallback,
RecoveryData parameter,
uint pingInterval,
uint flags);
[DllImport("kernel32.dll")]
public static extern int RegisterApplicationRestart(
[MarshalAs(UnmanagedType.BStr)] string commandLineArgs,
int flags);
[DllImport("kernel32.dll")]
public static extern int UnregisterApplicationRecoveryCallback();
[DllImport("kernel32.dll")]
public static extern int UnregisterApplicationRestart();
}
public class RecoveryData
{
string currentUser;
public RecoveryData(string who)
{
currentUser = who;
}
public string CurrentUser
{
get { return currentUser; }
}
}
// Restart after crash
public static void RegisterForRestart()
{
// Register for automatic restart if the application was terminated for any reason.
ArrImports.RegisterApplicationRestart("/restart",
(int)RestartRestrictions.None);
}
// Start app when PC starts
public static void RegisterForAutostart()
{
#if (!DEBUG)
RegistryKey key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
key.SetValue("websocket", #"c:\websocket\run.bat");
#endif
}
public static class Test
{
public static void Run()
{
crash();
}
static void crash()
{
double crashAfter = 1.5 * 60; // seconds
int secondsPassed = 0;
int waitSeconds = 1;
Console.WriteLine("\nCrash test startet, crash will occour in " + crashAfter + " seconds");
Timer timer = new Timer(
delegate (object seconds) {
secondsPassed += int.Parse(seconds.ToString());
if (secondsPassed > crashAfter)
{
Console.WriteLine("Crashing");
Environment.FailFast("Test - intentional crash."); // Error happens here
}
else
{
double timeUntilCrash = (crashAfter - secondsPassed);
Console.WriteLine("Time until crash = " + timeUntilCrash + " seconds");
}
},
waitSeconds,
TimeSpan.FromSeconds(waitSeconds),
TimeSpan.FromSeconds(waitSeconds));
}
}
When it's time to crash I get this message:
Cannot evaluate expression because a thread is stopped at a point
where garbage collection is impossible, possibly because the code is
optimized.
The checkbox for code optimization is unchecked.
I surpose this is because it's not in the main thread, if this is the case how do I return to the main thread. And if not, what might be the cause?
I created an application based on your code & found that everything runs as expected when the application is run from the command-line - it is only in Visual Studio debugger the restart does not work.
Thanks to PaulF we found the problem. I was testing in Debug mode, running the application in release mode outside of Visual Studio fixed the problem. The following NullReferenceException was caused by missing command line arguments upon restart.
There are already many answers about this topic, but is there a single way to get the the total amount of memory on a windows system from XP and above including Windows Server 2003?
What I have found:
Win32_LogicalMemoryConfiguration (Deprecated)
Win32_ComputerSystem (Minimum supported client: Vista)
Microsoft.VisualBasic.Devices.ComputerInfo (no XP support according to platforms)
thx
Add reference to Microsoft.VisualBasic and
var info = new Microsoft.VisualBasic.Devices.ComputerInfo();
Debug.WriteLine(info.TotalPhysicalMemory);
Debug.WriteLine(info.AvailablePhysicalMemory);
Debug.WriteLine(info.TotalVirtualMemory);
Debug.WriteLine(info.AvailableVirtualMemory);
edit : How can I get the total physical memory in C#?
or
You can make use of GlobalMemoryStatusEx : Example Here
private void DisplayMemory()
{
// Consumer of the NativeMethods class shown below
long tm = System.GC.GetTotalMemory(true);
NativeMethods oMemoryInfo = new NativeMethods();
this.lblMemoryLoadNumber.Text = oMemoryInfo.MemoryLoad.ToString();
this.lblIsMemoryTight.Text = oMemoryInfo.isMemoryTight().ToString();
if (oMemoryInfo.isMemoryTight())
this.lblIsMemoryTight.Text.Font.Bold = true;
else
this.lblIsMemoryTight.Text.Font.Bold = false;
}
Native class wrapper.
[CLSCompliant(false)]
public class NativeMethods {
private MEMORYSTATUSEX msex;
private uint _MemoryLoad;
const int MEMORY_TIGHT_CONST = 80;
public bool isMemoryTight()
{
if (_MemoryLoad > MEMORY_TIGHT_CONST )
return true;
else
return false;
}
public uint MemoryLoad
{
get { return _MemoryLoad; }
internal set { _MemoryLoad = value; }
}
public NativeMethods() {
msex = new MEMORYSTATUSEX();
if (GlobalMemoryStatusEx(msex)) {
_MemoryLoad = msex.dwMemoryLoad;
//etc.. Repeat for other structure members
}
else
// Use a more appropriate Exception Type. 'Exception' should almost never be thrown
throw new Exception("Unable to initalize the GlobalMemoryStatusEx API");
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
private class MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
public MEMORYSTATUSEX()
{
this.dwLength = (uint) Marshal.SizeOf(typeof( MEMORYSTATUSEX ));
}
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GlobalMemoryStatusEx( [In, Out] MEMORYSTATUSEX lpBuffer);
}
I have seen posts on changing console true type font and console colors (rgb) but nothing on setting or getting the console font size.
The reason I want to change the font size is because a grid is printed to the console, and the grid has many columns, so, it fits better with a smaller font. I'm wondering if it's possible to change it at runtime rather than allowing the default or configured fonts to take priority / override inheritance.
Maybe this article can help you
ConsoleHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
namespace ConsoleExtender {
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ConsoleFont {
public uint Index;
public short SizeX, SizeY;
}
public static class ConsoleHelper {
[DllImport("kernel32")]
public static extern bool SetConsoleIcon(IntPtr hIcon);
public static bool SetConsoleIcon(Icon icon) {
return SetConsoleIcon(icon.Handle);
}
[DllImport("kernel32")]
private extern static bool SetConsoleFont(IntPtr hOutput, uint index);
private enum StdHandle {
OutputHandle = -11
}
[DllImport("kernel32")]
private static extern IntPtr GetStdHandle(StdHandle index);
public static bool SetConsoleFont(uint index) {
return SetConsoleFont(GetStdHandle(StdHandle.OutputHandle), index);
}
[DllImport("kernel32")]
private static extern bool GetConsoleFontInfo(IntPtr hOutput, [MarshalAs(UnmanagedType.Bool)]bool bMaximize,
uint count, [MarshalAs(UnmanagedType.LPArray), Out] ConsoleFont[] fonts);
[DllImport("kernel32")]
private static extern uint GetNumberOfConsoleFonts();
public static uint ConsoleFontsCount {
get {
return GetNumberOfConsoleFonts();
}
}
public static ConsoleFont[] ConsoleFonts {
get {
ConsoleFont[] fonts = new ConsoleFont[GetNumberOfConsoleFonts()];
if(fonts.Length > 0)
GetConsoleFontInfo(GetStdHandle(StdHandle.OutputHandle), false, (uint)fonts.Length, fonts);
return fonts;
}
}
}
}
Here is how to use it to list true type fonts for console,
static void Main(string[] args) {
var fonts = ConsoleHelper.ConsoleFonts;
for(int f = 0; f < fonts.Length; f++)
Console.WriteLine("{0}: X={1}, Y={2}",
fonts[f].Index, fonts[f].SizeX, fonts[f].SizeY);
ConsoleHelper.SetConsoleFont(5);
ConsoleHelper.SetConsoleIcon(SystemIcons.Information);
}
Crucial functions: SetConsoleFont, GetConsoleFontInfo and GetNumberOfConsoleFonts. They're undocumented, so use at your own risk.
In this thread I found a much more elegant solution that now works perfectly fine.
ConsoleHelper.cs:
using System;
using System.Runtime.InteropServices;
public static class ConsoleHelper
{
private const int FixedWidthTrueType = 54;
private const int StandardOutputHandle = -11;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool GetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);
private static readonly IntPtr ConsoleOutputHandle = GetStdHandle(StandardOutputHandle);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FontInfo
{
internal int cbSize;
internal int FontIndex;
internal short FontWidth;
public short FontSize;
public int FontFamily;
public int FontWeight;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
//[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.wc, SizeConst = 32)]
public string FontName;
}
public static FontInfo[] SetCurrentFont(string font, short fontSize = 0)
{
Console.WriteLine("Set Current Font: " + font);
FontInfo before = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>()
};
if (GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref before))
{
FontInfo set = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>(),
FontIndex = 0,
FontFamily = FixedWidthTrueType,
FontName = font,
FontWeight = 400,
FontSize = fontSize > 0 ? fontSize : before.FontSize
};
// Get some settings from current font.
if (!SetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref set))
{
var ex = Marshal.GetLastWin32Error();
Console.WriteLine("Set error " + ex);
throw new System.ComponentModel.Win32Exception(ex);
}
FontInfo after = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>()
};
GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref after);
return new[] { before, set, after };
}
else
{
var er = Marshal.GetLastWin32Error();
Console.WriteLine("Get error " + er);
throw new System.ComponentModel.Win32Exception(er);
}
}
}
This way you can just do:
ConsoleHelper.SetCurrentFont("Consolas", 10);
After running the application (Ctrl + F5), right-click the title of the Console (it should say something like C:Windows\system32\cmd.exe) and select properties. Choose the "Font" tab, and you'll see the option to adjust the size.
The console does not support changing font size at runtime. A list of the available methods for modifying the current console windows settings can be found on MSDN. My understanding is that this is because:
The console is not a rich text interface, meaning it cannot display multiple fonts or font sizes.
as Noldorin states, this is something that should be up to the user, for example a person with vision problems may elect for a large fontsize.
I am writing a C# .NET app that accesses an USB device which exposes a serial port interface. I am using the handy .NET SerialPort class, which works just fine.
My problem is that I need to catch the DBT_DEVICEQUERYREMOVE event, but I don't get the event if I register using DBT_DEVTYP_DEVICEINTERFACE. I do get DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE, but no DBT_DEVICEQUERYREMOVE.
From my web research it seems that I need to register using DBT_DEVTYP_HANDLE, which requires a handle, such as that returned by CreateFile. Since the SerialPort class does not expose this handle, I'm wondering if there is some other way to get the handle (or some other way to get the event of interest).
I think you could use a bit of reflection to get the handle, I don't know of a better way to get the handle that the .NET Framework is currently using. The SerialPort uses an internal type called SerialStream. This Stream has the handle you want. Since I don't have a serial port to test on, this code is a bit of a guesswork:
var serialPort = new SerialPort();
object stream = typeof(SerialPort).GetField("internalSerialStream", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(serialPort);
var handle = (SafeFileHandle)stream.GetType().GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stream);
This will give you a SafeFileHandle, which is a wrapper for handles. You could call DangerousGetHandle to get the actual IntPtr.
This handle only has a valid value after you call Open on the SerialPort, and becomes invalid when it's disposed. You should check the IsInvalid and IsClosed values of the handle first before using DangerousGetHandle.
What is also a solution is to grab the Serialport implementation of .Net and change it to your liking.
This is what I did for a project where I needed access to the handle, and because I had timing/async issues with SerialPort. So I boiled it down to the bear necessities.
public class SimpleSerialPort : IDisposable
{
private const uint GenericRead = 0x80000000;
private const uint GenericWrite = 0x40000000;
private const int OpenExisting = 3;
private const uint Setdtr = 5; // Set DTR high
private FileStream _fileStream;
public void Close()
{
Dispose();
}
public int WriteTimeout { get; set; }
public SafeFileHandle Handle { get; private set; }
public void Open(string portName, uint baudrate)
{
// Check if port can be found
bool isValid =
SerialPort.GetPortNames()
.Any(x => String.Compare(x, portName, StringComparison.OrdinalIgnoreCase) == 0);
if (!isValid)
{
throw new IOException(string.Format("{0} port was not found", portName));
}
string port = #"\\.\" + portName;
Handle = CreateFile(port, GenericRead | GenericWrite, 0, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);
if (Handle.IsInvalid)
{
throw new IOException(string.Format("{0} port is already open", portName));
}
var dcb = new Dcb();
// first get the current dcb structure setup
if (GetCommState(Handle, ref dcb) == false)
{
throw new IOException(string.Format("GetCommState error {0}", portName));
}
dcb.BaudRate = baudrate;
dcb.ByteSize = 8;
dcb.Flags = 129;
dcb.XoffChar = 0;
dcb.XonChar = 0;
/* Apply the settings */
if (SetCommState(Handle, ref dcb) == false)
{
throw new IOException(string.Format("SetCommState error {0}", portName));
}
/* Set DTR, some boards needs a DTR = 1 level */
if (EscapeCommFunction(Handle, Setdtr) == false)
{
throw new IOException(string.Format("EscapeCommFunction error {0}", portName));
}
// Write default timeouts
var cto = new Commtimeouts
{
ReadTotalTimeoutConstant = 500,
ReadTotalTimeoutMultiplier = 0,
ReadIntervalTimeout = 10,
WriteTotalTimeoutConstant = WriteTimeout,
WriteTotalTimeoutMultiplier = 0
};
if (SetCommTimeouts(Handle, ref cto) == false)
{
throw new IOException(string.Format("SetCommTimeouts error {0}", portName));
}
// Create filestream
_fileStream = new FileStream(Handle, FileAccess.ReadWrite, 32, false);
}
public void Write(byte[] bytes)
{
_fileStream.Write(bytes, 0, bytes.Length);
}
public void Read(byte[] readArray)
{
for (int read = 0; read < readArray.Length;)
{
read += _fileStream.Read(readArray, read, readArray.Length - read);
}
}
public byte ReadByte()
{
byte[] readsBytes = new byte[1];
Read(readsBytes);
return readsBytes[0];
}
public void Dispose()
{
if (Handle != null)
{
Handle.Dispose();
}
if (_fileStream != null)
{
_fileStream.Dispose();
}
_fileStream = null;
Handle = null;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode,
IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool GetCommState(
SafeFileHandle hFile, // handle to communications device
ref Dcb lpDcb // device-control block
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetCommState(
SafeFileHandle hFile, // handle to communications device
ref Dcb lpDcb // device-control block
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool EscapeCommFunction(
SafeFileHandle hFile, // handle to communications device
uint dwFunc
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool SetCommTimeouts(
SafeFileHandle hFile, // handle to comm device
ref Commtimeouts lpCommTimeouts // time-out values
);
[StructLayout(LayoutKind.Sequential)]
private struct Commtimeouts
{
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
private struct Dcb
{
private readonly uint DCBlength;
public uint BaudRate;
public uint Flags;
private readonly ushort WReserved;
private readonly ushort XonLim;
private readonly ushort XoffLim;
public byte ByteSize;
private readonly byte Parity;
private readonly byte StopBits;
public byte XonChar;
public byte XoffChar;
private readonly byte ErrorChar;
private readonly byte EofChar;
private readonly byte EvtChar;
private readonly ushort WReserved1;
}
}