I need to send very large zip files via http. The files are too large to write to the file system or memory.I would like to zip on the fly and send back via http output stream. I'm using a custom web server that will only take a stream as a response, I don't have access to the http response output stream. (response.Send(stream)).
My idea is to create a helper/proxy stream that can be passed into the response.Send method. (See ZipCreateStream below)
Basically this encapsulates a memory stream, the files to be zipped and the archive. The response.Send method will buffer copy the stream passed in calling stream.Read, the ZipCreateStream implements a Read method that will zip a buffered chunk into it's memory stream (the archive) then copy that to the buffer passed in by response.Send.
This works great until I try to truncate the memory stream in ZipCreateStream Read method. What I'm trying to do there is keep the memory usage low, after the bytes in the temporary stream are copied I want to "clear" the memory stream.
When I do this the zip file ends up corrupted.
Any help/thoughts would be much appreciated! I'm truly stumped on this one.
Note: I'm using SharpZipLib for zipping.
public class ZipCreateStream : Stream
{
readonly string[] entries;
readonly string[] files;
ZipOutputStream archive;
ZipEntry archiveEntry;
int fileNumber;
Stream fileStream;
volatile MemoryStream stream;
bool hasDisposed;
int streamOffset;
readonly bool truncate;
public ZipCreateStream(string[] files, string[] entries, int compressionLevel = 0, bool truncate = false)
{
this.files = files;
this.entries = entries;
if (files.Length != entries.Length)
{
throw new ArgumentException("Files and entries mismatch");
}
this.truncate = truncate;
stream = new MemoryStream();
archive = new ZipOutputStream(stream);
archive.SetLevel(compressionLevel);
}
public override bool CanRead
{
get
{
return true;
}
}
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <returns>
/// true if the stream supports seeking; otherwise, false.
/// </returns>
public override bool CanSeek
{
get
{
return false;
}
}
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
/// </summary>
/// <returns>
/// true if the stream supports writing; otherwise, false.
/// </returns>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// When overridden in a derived class, gets the length in bytes of the stream.
/// </summary>
/// <returns>
/// A long value representing the length of the stream in bytes.
/// </returns>
/// <exception cref="T:System.NotSupportedException">A class derived from Stream does not support seeking. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Length
{
get
{
throw new NotSupportedException();
}
}
/// <summary>
/// When overridden in a derived class, gets or sets the position within the current stream.
/// </summary>
/// <returns>
/// The current position within the stream.
/// </returns>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">The stream does not support seeking. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Position { get; set; }
protected override void Dispose(bool disposing)
{
archive.Dispose();
stream.Dispose();
if (fileStream != null)
{
fileStream.Dispose();
}
base.Dispose(disposing);
}
/// <summary>
/// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written
/// to the underlying device.
/// </summary>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
public override void Flush()
{
if (stream == null)
{
return;
}
stream.Flush();
}
/// <summary>
/// When overridden in a derived class, sets the position within the current stream.
/// </summary>
/// <returns>
/// The new position within the current stream.
/// </returns>
/// <param name="offset">A byte offset relative to the <paramref name="origin" /> parameter. </param>
/// <param name="origin">
/// A value of type <see cref="T:System.IO.SeekOrigin" /> indicating the reference point used to
/// obtain the new position.
/// </param>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">
/// The stream does not support seeking, such as if the stream is
/// constructed from a pipe or console output.
/// </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Seek(long offset, SeekOrigin origin)
{
return stream.Seek(offset, origin);
}
/// <summary>
/// When overridden in a derived class, sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes. </param>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">
/// The stream does not support both writing and seeking, such as if the
/// stream is constructed from a pipe or console output.
/// </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override void SetLength(long value)
{
stream.SetLength(value);
}
/// <summary>
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position
/// within the stream by the number of bytes read.
/// </summary>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many
/// bytes are not currently available, or zero (0) if the end of the stream has been reached.
/// </returns>
/// <param name="buffer">
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the
/// values between <paramref name="offset" /> and (<paramref name="offset" /> + <paramref name="count" /> - 1) replaced
/// by the bytes read from the current source.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in <paramref name="buffer" /> at which to begin storing the data read
/// from the current stream.
/// </param>
/// <param name="count">The maximum number of bytes to be read from the current stream. </param>
/// <exception cref="T:System.ArgumentException">
/// The sum of <paramref name="offset" /> and <paramref name="count" /> is
/// larger than the buffer length.
/// </exception>
/// <exception cref="T:System.ArgumentNullException"><paramref name="buffer" /> is null. </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="offset" /> or <paramref name="count" /> is
/// negative.
/// </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">The stream does not support reading. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override int Read(byte[] buffer, int offset, int count)
{
// Get the next set of buffered data from internal buffer
if (offset != 0)
{
throw new ArgumentException("Not seekable");
}
if (streamOffset == stream.Length)
{
// when all buffered data is copied, clear the memory stream
if (truncate)
{
streamOffset = 0;
stream.SetLength(0);
}
if (fileStream != null && fileStream.Position == fileStream.Length)
{
fileNumber++;
fileStream.Dispose();
fileStream = null;
}
if (fileNumber < files.Length)
{
if (fileStream == null)
{
string file = files[fileNumber];
string entry = entries[fileNumber];
fileStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read);
archiveEntry = new ZipEntry(ZipEntry.CleanName(entry));
archive.PutNextEntry(archiveEntry);
}
byte[] writebuffer = new byte[buffer.Length];
while (stream.Length - streamOffset < buffer.Length)
{
int bytesRead = fileStream.Read(writebuffer, 0, writebuffer.Length);
if (bytesRead > 0)
{
archive.Write(writebuffer, 0, bytesRead);
}
else
{
archive.Flush();
break;
}
}
}
}
if (streamOffset == stream.Length && !hasDisposed)
{
hasDisposed = true;
archive.Finish();
}
stream.Seek(streamOffset, SeekOrigin.Begin);
int readCount = stream.Read(buffer, 0, count);
streamOffset += readCount;
return readCount;
}
/// <summary>
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current
/// position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">
/// An array of bytes. This method copies <paramref name="count" /> bytes from
/// <paramref name="buffer" /> to the current stream.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in <paramref name="buffer" /> at which to begin copying bytes to the
/// current stream.
/// </param>
/// <param name="count">The number of bytes to be written to the current stream. </param>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
The problem is in Position property.
Below you can find 2 working classes for this task. They use standard .NET ZipArchive class:
public class ThroughZipStream : Stream
{
private readonly ThroughMemoryStream _ms = new ThroughMemoryStream();
private readonly ZipArchive _zip;
private readonly Stream _entryStream;
private readonly Stream _input;
private byte[] _buffer = new byte[10000]; // buffer to read from input stream
public ThroughZipStream(Stream input, string entryName)
{
_input = input;
_zip = new ZipArchive(_ms, ZipArchiveMode.Create, true);
var entry = _zip.CreateEntry(entryName);
_entryStream = entry.Open();
}
public override int Read(byte[] buffer, int offset, int count)
{
while (_ms.Length < count && _buffer != null)
{
var len = _input.Read(_buffer, 0, _buffer.Length);
if (len == 0)
{
_entryStream.Dispose();
_zip.Dispose();
_buffer = null;
break;
}
_entryStream.Write(_buffer, 0, len);
}
return _ms.Read(buffer, offset, count);
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return false; } } // we can't seek
public override bool CanWrite { get { return false; } }
public override void Flush() { }
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
public override long Length { get { throw new NotImplementedException(); } }
public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
public override void SetLength(long value) { throw new NotImplementedException(); }
public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); }
}
public class ThroughMemoryStream : MemoryStream
{
private long _position;
public override bool CanSeek { get { return false; } }
public override void Write(byte[] buffer, int offset, int count)
{
base.Write(buffer, offset, count);
_position += count;
}
public override int Read(byte[] buffer, int offset, int count)
{
var msBuffer = GetBuffer();
var realCount = Math.Min(Length, count);
Array.Copy(msBuffer, 0, buffer, 0, realCount);
Array.Copy(msBuffer, realCount, msBuffer, 0, Length - realCount);
SetLength(Length - realCount);
return (int)realCount;
}
public override long Position
{
get { return _position; }
set { throw new NotImplementedException(); }
}
}
Usage:
var compressedStream = new ThroughZipStream(fileStream, "somefile.txt")
PS: I had the same problem and couldn't google any solution. It was a big surprise for me, I think this way is the best one to pipeline a stream.
Related
I am using a Client Server architecture to download files from a centralized server. Due to the Limits of the Client machines I'm using Framework 3.5 on them and 4.6 on the Server.
On the Server I'm doing the following:
public IHttpActionResult MyControllerMedhod()
{
return MySecondMethod();
}
private HttpResponseMessage MySecondMethod()
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
FileStream stream = new FileStream("c:\temp\tester.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "c:\temp\tester.dat";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = stream.Length;
return result;
}
On the Client machine I'm using:
webClient.DownloadFile("http://127.0.0.1/Download/MyControllerMethod", "c:\temp\test.dat");
It works quite fine BUT I have absolutely no control over how much bandwidth is used. For some of the Clients it would be of Advantage if less bandwidth is being used to download the files.
Now my question is: Is there any way to Limit the bandwidth that is being used per download? (either from Client side or Server side).
As remark: The Downloads can be data files as big as 800 or 900 MB.
One way that this can be achieved from the server side, if you don't have the ability to adjust IIS settings is to use a ThrottledStream. This is a class that I found somewhere way back when, if I can located the original source I'll be sure to link back to it.
Throttled Stream:
public class ThrottledStream : Stream
{
/// <summary>
/// A constant used to specify an infinite number of bytes that can be transferred per second.
/// </summary>
public const long Infinite = 0;
#region Private members
/// <summary>
/// The base stream.
/// </summary>
private Stream _baseStream;
/// <summary>
/// The maximum bytes per second that can be transferred through the base stream.
/// </summary>
private long _maximumBytesPerSecond;
/// <summary>
/// The number of bytes that has been transferred since the last throttle.
/// </summary>
private long _byteCount;
/// <summary>
/// The start time in milliseconds of the last throttle.
/// </summary>
private long _start;
#endregion
#region Properties
/// <summary>
/// Gets the current milliseconds.
/// </summary>
/// <value>The current milliseconds.</value>
protected long CurrentMilliseconds
{
get
{
return Environment.TickCount;
}
}
/// <summary>
/// Gets or sets the maximum bytes per second that can be transferred through the base stream.
/// </summary>
/// <value>The maximum bytes per second.</value>
public long MaximumBytesPerSecond
{
get
{
return _maximumBytesPerSecond;
}
set
{
if (MaximumBytesPerSecond != value)
{
_maximumBytesPerSecond = value;
Reset();
}
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
/// <returns>true if the stream supports reading; otherwise, false.</returns>
public override bool CanRead
{
get
{
return _baseStream.CanRead;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <value></value>
/// <returns>true if the stream supports seeking; otherwise, false.</returns>
public override bool CanSeek
{
get
{
return _baseStream.CanSeek;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
/// <value></value>
/// <returns>true if the stream supports writing; otherwise, false.</returns>
public override bool CanWrite
{
get
{
return _baseStream.CanWrite;
}
}
/// <summary>
/// Gets the length in bytes of the stream.
/// </summary>
/// <value></value>
/// <returns>A long value representing the length of the stream in bytes.</returns>
/// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Length
{
get
{
return _baseStream.Length;
}
}
/// <summary>
/// Gets or sets the position within the current stream.
/// </summary>
/// <value></value>
/// <returns>The current position within the stream.</returns>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Position
{
get
{
return _baseStream.Position;
}
set
{
_baseStream.Position = value;
}
}
#endregion
#region Ctor
/// <summary>
/// Initializes a new instance of the <see cref="T:ThrottledStream"/> class with an
/// infinite amount of bytes that can be processed.
/// </summary>
/// <param name="baseStream">The base stream.</param>
public ThrottledStream(Stream baseStream)
: this(baseStream, ThrottledStream.Infinite)
{
// Nothing todo.
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ThrottledStream"/> class.
/// </summary>
/// <param name="baseStream">The base stream.</param>
/// <param name="maximumBytesPerSecond">The maximum bytes per second that can be transferred through the base stream.</param>
/// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream"/> is a null reference.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="maximumBytesPerSecond"/> is a negative value.</exception>
public ThrottledStream(Stream baseStream, long maximumBytesPerSecond)
{
if (baseStream == null)
{
throw new ArgumentNullException("baseStream");
}
if (maximumBytesPerSecond < 0)
{
throw new ArgumentOutOfRangeException("maximumBytesPerSecond",
maximumBytesPerSecond, "The maximum number of bytes per second can't be negatie.");
}
_baseStream = baseStream;
_maximumBytesPerSecond = maximumBytesPerSecond;
_start = CurrentMilliseconds;
_byteCount = 0;
}
#endregion
#region Public methods
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
public override void Flush()
{
_baseStream.Flush();
}
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
/// </returns>
/// <exception cref="T:System.ArgumentException">The sum of offset and count is larger than the buffer length. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
/// <exception cref="T:System.NotSupportedException">The base stream does not support reading. </exception>
/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>
public override int Read(byte[] buffer, int offset, int count)
{
Throttle(count);
return _baseStream.Read(buffer, offset, count);
}
/// <summary>
/// Sets the position within the current stream.
/// </summary>
/// <param name="offset">A byte offset relative to the origin parameter.</param>
/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"></see> indicating the reference point used to obtain the new position.</param>
/// <returns>
/// The new position within the current stream.
/// </returns>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">The base stream does not support seeking, such as if the stream is constructed from a pipe or console output. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override long Seek(long offset, SeekOrigin origin)
{
return _baseStream.Seek(offset, origin);
}
/// <summary>
/// Sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
/// <exception cref="T:System.NotSupportedException">The base stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. </exception>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
public override void SetLength(long value)
{
_baseStream.SetLength(value);
}
/// <summary>
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
/// <exception cref="T:System.NotSupportedException">The base stream does not support writing. </exception>
/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>
/// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length. </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>
public override void Write(byte[] buffer, int offset, int count)
{
Throttle(count);
_baseStream.Write(buffer, offset, count);
}
/// <summary>
/// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
/// </returns>
public override string ToString()
{
return _baseStream.ToString();
}
#endregion
#region Protected methods
/// <summary>
/// Throttles for the specified buffer size in bytes.
/// </summary>
/// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
protected void Throttle(int bufferSizeInBytes)
{
// Make sure the buffer isn't empty.
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
{
return;
}
_byteCount += bufferSizeInBytes;
long elapsedMilliseconds = CurrentMilliseconds - _start;
if (elapsedMilliseconds > 0)
{
// Calculate the current bps.
long bps = _byteCount * 1000L / elapsedMilliseconds;
// If the bps are more then the maximum bps, try to throttle.
if (bps > _maximumBytesPerSecond)
{
// Calculate the time to sleep.
long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond;
int toSleep = (int)(wakeElapsed - elapsedMilliseconds);
if (toSleep > 1)
{
try
{
// The time to sleep is more then a millisecond, so sleep.
Thread.Sleep(toSleep);
}
catch (ThreadAbortException)
{
// Eatup ThreadAbortException.
}
// A sleep has been done, reset.
Reset();
}
}
}
}
/// <summary>
/// Will reset the bytecount to 0 and reset the start time to the current time.
/// </summary>
protected void Reset()
{
long difference = CurrentMilliseconds - _start;
// Only reset counters when a known history is available of more then 1 second.
if (difference > 1000)
{
_byteCount = 0;
_start = CurrentMilliseconds;
}
}
#endregion
}
This is used by placing the original stream into the constructor along with the maximum bytes per second that you would like to throttle it to. For example:
public IHttpActionResult MyControllerMedhod()
{
return MySecondMethod();
}
private HttpResponseMessage MySecondMethod()
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
FileStream stream = new FileStream("c:\temp\tester.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
result.Content = new ThrottledStream(stream, 153600);
result.Content.Headers.ContentDisposition = new
ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "c:\temp\tester.dat";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = stream.Length;
return result;
}
Edit:
Original Source: https://gist.github.com/passy/637319
I'm trying to create a trainer for a game. Which causes a error (I think) because im trying to access the game memory of a 64 bit game with a 32 bit command.
Source code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
namespace Infinite_Trainer___Cod_IW
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void bunifuImageButton1_Click(object sender, EventArgs e)
{
this.Close();
}
private void bunifuFlatButton1_Click(object sender, EventArgs e)
{
string prestigeLevel = bunifuDropdown1.selectedValue;
string xp = bunifuSlider1.Value.ToString();
string winrate = bunifuMaterialTextbox1.Text;
string loserate = bunifuMaterialTextbox2.Text;
Process[] process = Process.GetProcessesByName("iw7_ship");
if (process.Length > 0)
{
using (CheatEngine.Memory memory = new CheatEngine.Memory(process[0]))
{
IntPtr prestigeAddress = memory.GetAddress("\"iw7_ship.exe\"+04105320+6E4");
memory.WriteUInt32(prestigeAddress, 1);
}
}
else
{
MessageBox.Show("Game isn't running");
}
}
}
}
And the memory.cs class:
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace Infinite_Trainer___Cod_IW.CheatEngine
{
/// <summary>
/// Represents an access to a remote process memory
/// </summary>
public class Memory : IDisposable
{
private Process process;
private IntPtr processHandle;
private bool isDisposed;
public const string OffsetPattern = "(\\+|\\-){0,1}(0x){0,1}[a-fA-F0-9]{1,}";
/// <summary>
/// Initializes a new instance of the Memory
/// </summary>
/// <param name="process">Remote process</param>
public Memory(Process process)
{
if (process == null)
throw new ArgumentNullException("process");
this.process = process;
processHandle = Win32.OpenProcess(
Win32.ProcessAccessType.PROCESS_VM_READ | Win32.ProcessAccessType.PROCESS_VM_WRITE |
Win32.ProcessAccessType.PROCESS_VM_OPERATION, true, (uint)process.Id);
if (processHandle == IntPtr.Zero)
throw new InvalidOperationException("Could not open the process");
}
#region IDisposable
~Memory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (isDisposed)
return;
Win32.CloseHandle(processHandle);
process = null;
processHandle = IntPtr.Zero;
isDisposed = true;
}
#endregion
#region Properties
/// <summary>
/// Gets the process to which this memory is attached to
/// </summary>
public Process Process
{
get
{
return process;
}
}
#endregion
/// <summary>
/// Finds module with the given name
/// </summary>
/// <param name="name">Module name</param>
/// <returns></returns>
protected ProcessModule FindModule(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
foreach (ProcessModule module in process.Modules)
{
if (module.ModuleName.ToLower() == name.ToLower())
return module;
}
return null;
}
/// <summary>
/// Gets module based address
/// </summary>
/// <param name="moduleName">Module name</param>
/// <param name="baseAddress">Base address</param>
/// <param name="offsets">Collection of offsets</param>
/// <returns></returns>
public IntPtr GetAddress(string moduleName, IntPtr baseAddress, int[] offsets)
{
if (string.IsNullOrEmpty(moduleName))
throw new ArgumentNullException("moduleName");
ProcessModule module = FindModule(moduleName);
if (module == null)
return IntPtr.Zero;
else
{
int address = module.BaseAddress.ToInt32() + baseAddress.ToInt32();
return GetAddress((IntPtr)address, offsets);
}
}
/// <summary>
/// Gets address
/// </summary>
/// <param name="baseAddress">Base address</param>
/// <param name="offsets">Collection of offsets</param>
/// <returns></returns>
public IntPtr GetAddress(IntPtr baseAddress, int[] offsets)
{
if (baseAddress == IntPtr.Zero)
throw new ArgumentException("Invalid base address");
int address = baseAddress.ToInt32();
if (offsets != null && offsets.Length > 0)
{
byte[] buffer = new byte[4];
foreach (int offset in offsets)
address = ReadInt32((IntPtr)address) + offset;
}
return (IntPtr)address;
}
/// <summary>
/// Gets address pointer
/// </summary>
/// <param name="address">Address</param>
/// <returns></returns>
public IntPtr GetAddress(string address)
{
if (string.IsNullOrEmpty(address))
throw new ArgumentNullException("address");
string moduleName = null;
int index = address.IndexOf('"');
if (index != -1)
{
// Module name at the beginning
int endIndex = address.IndexOf('"', index + 1);
if (endIndex == -1)
throw new ArgumentException("Invalid module name. Could not find matching \"");
moduleName = address.Substring(index + 1, endIndex - 1);
address = address.Substring(endIndex + 1);
}
int[] offsets = GetAddressOffsets(address);
int[] _offsets = null;
IntPtr baseAddress = offsets != null && offsets.Length > 0 ?
(IntPtr)offsets[0] : IntPtr.Zero;
if (offsets != null && offsets.Length > 1)
{
_offsets = new int[offsets.Length - 1];
for (int i = 0; i < offsets.Length - 1; i++)
_offsets[i] = offsets[i + 1];
}
if (moduleName != null)
return GetAddress(moduleName, baseAddress, _offsets);
else
return GetAddress(baseAddress, _offsets);
}
/// <summary>
/// Gets address offsets
/// </summary>
/// <param name="address">Address</param>
/// <returns></returns>
protected static int[] GetAddressOffsets(string address)
{
if (string.IsNullOrEmpty(address))
return new int[0];
else
{
MatchCollection matches = Regex.Matches(address, OffsetPattern);
int[] offsets = new int[matches.Count];
string value;
char ch;
for (int i = 0; i < matches.Count; i++)
{
ch = matches[i].Value[0];
if (ch == '+' || ch == '-')
value = matches[i].Value.Substring(1);
else
value = matches[i].Value;
offsets[i] = Convert.ToInt32(value, 16);
if (ch == '-')
offsets[i] = -offsets[i];
}
return offsets;
}
}
/// <summary>
/// Reads memory at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="buffer">Buffer</param>
/// <param name="size">Size in bytes</param>
public void ReadMemory(IntPtr address, byte[] buffer, int size)
{
if (isDisposed)
throw new ObjectDisposedException("Memory");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (size <= 0)
throw new ArgumentException("Size must be greater than zero");
if (address == IntPtr.Zero)
throw new ArgumentException("Invalid address");
uint read = 0;
if (!Win32.ReadProcessMemory(processHandle, address, buffer, (uint)size, ref read) ||
read != size)
throw new AccessViolationException();
}
/// <summary>
/// Writes memory at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="buffer">Buffer</param>
/// <param name="size">Size in bytes</param>
public void WriteMemory(IntPtr address, byte[] buffer, int size)
{
if (isDisposed)
throw new ObjectDisposedException("Memory");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (size <= 0)
throw new ArgumentException("Size must be greater than zero");
if (address == IntPtr.Zero)
throw new ArgumentException("Invalid address");
uint write = 0;
if (!Win32.WriteProcessMemory(processHandle, address, buffer, (uint)size, ref write) ||
write != size)
throw new AccessViolationException();
}
/// <summary>
/// Reads 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public int ReadInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToInt32(buffer, 0);
}
/// <summary>
/// Reads 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public uint ReadUInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToUInt32(buffer, 0);
}
/// <summary>
/// Reads single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public float ReadFloat(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToSingle(buffer, 0);
}
/// <summary>
/// Reads double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public double ReadDouble(IntPtr address)
{
byte[] buffer = new byte[8];
ReadMemory(address, buffer, 8);
return BitConverter.ToDouble(buffer, 0);
}
/// <summary>
/// Writes 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteUInt32(IntPtr address, uint value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteInt32(IntPtr address, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteFloat(IntPtr address, float value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteDouble(IntPtr address, double value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 8);
}
}
}
Sadly I'm receiving the following error code:
An unhandled exception of type 'System.ComponentModel.Win32Exception' occurred in System.dll
Additional information: A 32-bit process can not access modules in a 64-bit process.
This error should be caused by the following line: memory.WriteUInt32(prestigeAddress, 1);
Does anyone know what I could do now or if there's a fix for this? Or do I need a whole new Memory Processing class?
I would appreciate any kind of help
UPDATE
The new error does look like the following:
An unhandled exception of type 'System.OverflowException' occurred in mscorlib.dll
Additional information: The arithmetic operation has caused an overflow.
Screenshot: https://gyazo.com/04107c28a4d7af0599f1dd59c72b6020
Full log:
System.OverflowException was unhandled
HResult=-2146233066
Message=The arithmetic operation caused an overflow.
Source=mscorlib
StackTrace:
at System.IntPtr.ToInt32()
at Infinite_Trainer___Cod_IW.CheatEngine.Memory.GetAddress(String moduleName, IntPtr baseAddress, Int32[] offsets) in C:\Users\d4ne\documents\visual studio 2015\Projects\Infinite Trainer - Cod IW\Infinite Trainer - Cod IW\CheatEngine\Memory.cs:Line 109.
at Infinite_Trainer___Cod_IW.Form1.bunifuFlatButton1_Click(Object sender, EventArgs e) in C:\Users\d4ne\documents\visual studio 2015\Projects\Infinite Trainer - Cod IW\Infinite Trainer - Cod IW\Form1.cs:Line 40.
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Label.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at Infinite_Trainer___Cod_IW.Program.Main() in C:\Users\d4ne\documents\visual studio 2015\Projects\Infinite Trainer - Cod IW\Infinite Trainer - Cod IW\Program.cs:Line 19.
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
Can you check out this32 bit process
Could be your project is targeting 32 instead of 64 bit?
I have a class which contains multiple functions to read and write memory. But it's missing the readByte function and writeByte function.
I've tried to create those in the following format:
public byte readByte(IntPtr address)
{
byte[] buffer = new byte[1];
ReadMemory(address, buffer, 1);
//return BitConverter.ToUInt32(buffer, 0);
}
But couldn't think of a BitConverter return type. Does anyone have a idea how I could create those functions? Since the returned value is only a byte as you can see in this screenshot:
Some functions from the class:
/// <summary>
/// Reads 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public int ReadInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToInt32(buffer, 0);
}
/// <summary>
/// Reads 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public uint ReadUInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToUInt32(buffer, 0);
}
/// <summary>
/// Reads single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public float ReadFloat(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToSingle(buffer, 0);
}
/// <summary>
/// Reads double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public double ReadDouble(IntPtr address)
{
byte[] buffer = new byte[8];
ReadMemory(address, buffer, 8);
return BitConverter.ToDouble(buffer, 0);
}
/// <summary>
/// Writes 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteUInt32(IntPtr address, uint value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteInt32(IntPtr address, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteFloat(IntPtr address, float value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteDouble(IntPtr address, double value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 8);
}
Because currently I'm calling the class in the following way:
public void updatePlayerStatistic(int prestigeLevel)
{
Process[] processes = Process.GetProcessesByName("iw7_ship");
if (processes.Length > 0)
{
using (CheatEngine.Memory memory = new CheatEngine.Memory(processes[0]))
{
// Prestige code
IntPtr prestigeAddress = memory.GetAddress("iw7_ship.exe", (IntPtr)0x04105320, new int[] { 0x6E5 });
//memory.WriteUInt32(prestigeAddress, uint.Parse(prestigeLevel.ToString()));
MessageBox.Show(memory.ReadUInt32(prestigeAddress).ToString());
}
}
}
Which would always return 0. As you can see in the following screenshot:
But clearly the value is 8 which is shown in CheatEngine.
Whole class:
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace InfiniteTrainer.CheatEngine
{
/// <summary>
/// Represents an access to a remote process memory
/// </summary>
public class Memory : IDisposable
{
private Process process;
private IntPtr processHandle;
private bool isDisposed;
public const string OffsetPattern = "(\\+|\\-){0,1}(0x){0,1}[a-fA-F0-9]{1,}";
/// <summary>
/// Initializes a new instance of the Memory
/// </summary>
/// <param name="process">Remote process</param>
public Memory(Process process)
{
if (process == null)
throw new ArgumentNullException("process");
this.process = process;
processHandle = Win32.OpenProcess(
Win32.ProcessAccessType.PROCESS_VM_READ | Win32.ProcessAccessType.PROCESS_VM_WRITE |
Win32.ProcessAccessType.PROCESS_VM_OPERATION, true, (uint)process.Id);
if (processHandle == IntPtr.Zero)
throw new InvalidOperationException("Could not open the process");
}
#region IDisposable
~Memory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (isDisposed)
return;
Win32.CloseHandle(processHandle);
process = null;
processHandle = IntPtr.Zero;
isDisposed = true;
}
#endregion
#region Properties
/// <summary>
/// Gets the process to which this memory is attached to
/// </summary>
public Process Process
{
get
{
return process;
}
}
#endregion
/// <summary>
/// Finds module with the given name
/// </summary>
/// <param name="name">Module name</param>
/// <returns></returns>
protected ProcessModule FindModule(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
foreach (ProcessModule module in process.Modules)
{
if (module.ModuleName.ToLower() == name.ToLower())
return module;
}
return null;
}
/// <summary>
/// Gets module based address
/// </summary>
/// <param name="moduleName">Module name</param>
/// <param name="baseAddress">Base address</param>
/// <param name="offsets">Collection of offsets</param>
/// <returns></returns>
public IntPtr GetAddress(string moduleName, IntPtr baseAddress, int[] offsets)
{
if (string.IsNullOrEmpty(moduleName))
throw new ArgumentNullException("moduleName");
ProcessModule module = FindModule(moduleName);
if (module == null)
return IntPtr.Zero;
else
{
//int address = module.BaseAddress.ToInt32() + baseAddress.ToInt32();
long address = module.BaseAddress.ToInt64() + baseAddress.ToInt64();
return GetAddress((IntPtr)address, offsets);
}
}
/// <summary>
/// Gets address
/// </summary>
/// <param name="baseAddress">Base address</param>
/// <param name="offsets">Collection of offsets</param>
/// <returns></returns>
public IntPtr GetAddress(IntPtr baseAddress, int[] offsets)
{
if (baseAddress == IntPtr.Zero)
throw new ArgumentException("Invalid base address");
//int address = baseAddress.ToInt32();
long address = baseAddress.ToInt64();
if (offsets != null && offsets.Length > 0)
{
byte[] buffer = new byte[4];
foreach (int offset in offsets)
address = ReadInt32((IntPtr)address) + offset;
}
return (IntPtr)address;
}
/// <summary>
/// Gets address pointer
/// </summary>
/// <param name="address">Address</param>
/// <returns></returns>
public IntPtr GetAddress(string address)
{
if (string.IsNullOrEmpty(address))
throw new ArgumentNullException("address");
string moduleName = null;
int index = address.IndexOf('"');
if (index != -1)
{
// Module name at the beginning
int endIndex = address.IndexOf('"', index + 1);
if (endIndex == -1)
throw new ArgumentException("Invalid module name. Could not find matching \"");
moduleName = address.Substring(index + 1, endIndex - 1);
address = address.Substring(endIndex + 1);
}
int[] offsets = GetAddressOffsets(address);
int[] _offsets = null;
IntPtr baseAddress = offsets != null && offsets.Length > 0 ?
(IntPtr)offsets[0] : IntPtr.Zero;
if (offsets != null && offsets.Length > 1)
{
_offsets = new int[offsets.Length - 1];
for (int i = 0; i < offsets.Length - 1; i++)
_offsets[i] = offsets[i + 1];
}
if (moduleName != null)
return GetAddress(moduleName, baseAddress, _offsets);
else
return GetAddress(baseAddress, _offsets);
}
/// <summary>
/// Gets address offsets
/// </summary>
/// <param name="address">Address</param>
/// <returns></returns>
protected static int[] GetAddressOffsets(string address)
{
if (string.IsNullOrEmpty(address))
return new int[0];
else
{
MatchCollection matches = Regex.Matches(address, OffsetPattern);
int[] offsets = new int[matches.Count];
string value;
char ch;
for (int i = 0; i < matches.Count; i++)
{
ch = matches[i].Value[0];
if (ch == '+' || ch == '-')
value = matches[i].Value.Substring(1);
else
value = matches[i].Value;
offsets[i] = Convert.ToInt32(value, 16);
if (ch == '-')
offsets[i] = -offsets[i];
}
return offsets;
}
}
/// <summary>
/// Reads memory at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="buffer">Buffer</param>
/// <param name="size">Size in bytes</param>
public void ReadMemory(IntPtr address, byte[] buffer, int size)
{
if (isDisposed)
throw new ObjectDisposedException("Memory");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (size <= 0)
throw new ArgumentException("Size must be greater than zero");
if (address == IntPtr.Zero)
throw new ArgumentException("Invalid address");
uint read = 0;
if (!Win32.ReadProcessMemory(processHandle, address, buffer, (uint)size, ref read) ||
read != size)
throw new AccessViolationException();
}
/// <summary>
/// Writes memory at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="buffer">Buffer</param>
/// <param name="size">Size in bytes</param>
public void WriteMemory(IntPtr address, byte[] buffer, int size)
{
if (isDisposed)
throw new ObjectDisposedException("Memory");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (size <= 0)
throw new ArgumentException("Size must be greater than zero");
if (address == IntPtr.Zero)
throw new ArgumentException("Invalid address");
uint write = 0;
if (!Win32.WriteProcessMemory(processHandle, address, buffer, (uint)size, ref write) ||
write != size)
throw new AccessViolationException();
}
/// <summary>
/// Reads 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public int ReadInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToInt32(buffer, 0);
}
/// <summary>
/// Reads 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public uint ReadUInt32(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToUInt32(buffer, 0);
}
/// <summary>
/// Reads single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public float ReadFloat(IntPtr address)
{
byte[] buffer = new byte[4];
ReadMemory(address, buffer, 4);
return BitConverter.ToSingle(buffer, 0);
}
/// <summary>
/// Reads double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <returns></returns>
public double ReadDouble(IntPtr address)
{
byte[] buffer = new byte[8];
ReadMemory(address, buffer, 8);
return BitConverter.ToDouble(buffer, 0);
}
/// <summary>
/// Writes 32 bit unsigned integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteUInt32(IntPtr address, uint value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes 32 bit signed integer at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteInt32(IntPtr address, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes single precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteFloat(IntPtr address, float value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 4);
}
/// <summary>
/// Writes double precision value at the address
/// </summary>
/// <param name="address">Memory address</param>
/// <param name="value">Value</param>
/// <returns></returns>
public void WriteDouble(IntPtr address, double value)
{
byte[] buffer = BitConverter.GetBytes(value);
WriteMemory(address, buffer, 8);
}
}
}
If you carefully read the code you've written, you'd see that no conversion is necessary, hence your difficulty finding this (unnecessary) BitConverter function.
Simply return the only byte you've read:
public byte readByte(IntPtr address)
{
byte[] buffer = new byte[1];
ReadMemory(address, buffer, 1);
return buffer[0];
}
You don't need a BitConverter. You already have a byte, which you can return:
public byte readByte(IntPtr address)
{
byte[] buffer = new byte[1];
ReadMemory(address, buffer, 1);
return buffer[0];
}
Some background information: BitConverter is there to convert raw binary data (represented as byte[]) to specific types. It exists that you don't have to worry about the internal structure of the respective types (especially endianness can be a bummer). That howewer is completely unnecessary for a byte, since you already have the byte in your array.
I have a need for a circular buffer (or other data structure), on which I can do a "ToArray" or similar call to get the current state of the buffer and use whilst the buffer carries on possibly overwriting the values held.
The reason for this use case is that the returned data is passed to a worker thread to process and the idea is to ensure that as little data is overwritten as possible in between the calls. The choice of a circular data structure was ostensibly to reduce the memory usage of the buffer to a fixed value.
I built the data structure below, which up until yesterday was sufficient for my needs. The relevant calls are TakeSnapshot, TakeAll and TakeWeakArraySnapshot which are all variations on the same theme.
The call is made quite frequently when about a 1000 samples of reference types are available.
All the calls result in an out of memory exception at some point. The exception is the TakeWeakArraySnapshot which basically reverts to null midway through using the array (something i guess is to do with the fact that the gc handle is weak?)
Is there a more suitable memory efficient data structure for this use or something I am doing wrong? Would changing the gc handle type to normal help. Is it possible to create a output type that wraps the references (like i attempted to with the weakreference) which would be easily be reclaimed by the garbage collector?
Thanks for reading.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Runtime.InteropServices;
public class WeakArray<T>
{
private GCHandle[] fArray;
public WeakArray(int length)
{
fArray = new GCHandle[length];
for (int i = 0; i < length; i++)
fArray[i] = GCHandle.Alloc(null, GCHandleType.Weak);
}
~WeakArray()
{
if (fArray == null)
return;
int count = fArray.Length;
for (int i = 0; i < count; i++)
{
GCHandle gcHandle = fArray[i];
if (!gcHandle.IsAllocated)
break;
gcHandle.Free();
}
}
public int Length
{
get
{
return fArray.Length;
}
}
public T this[int index]
{
get
{
return (T) fArray[index].Target;
}
set
{
fArray[index].Target = value;
}
}
}
public class OutputData<TDataType>:IEnumerable<TDataType>,IDisposable
{
private TDataType[] Buffer { get; set; }
private readonly object _lock = new object();
public OutputData(ref TDataType[] buffer,long length)
{
Buffer = buffer;
Length = length;
}
public long Length { get; private set; }
/// <summary>
/// Gets the <see cref="`0"/> at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue
/// </summary>
/// <value>
/// The <see cref="`0"/>.
/// </value>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
public TDataType this[int i]
{
get
{
lock (_lock)
{
return Length > 0 ? Buffer[i] : default(TDataType);
}
}
}
public IEnumerator<TDataType> GetEnumerator()
{
for (int i = 0; i < Length; i++)
{
yield return Buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
Array.Clear(Buffer,0,Buffer.Length);
}
public void SetLength(int count)
{
Length = count;
}
}
/// <summary>
/// Implements a CircularBuffer that behaves as a queue
/// </summary>
/// <typeparam name="T"></typeparam>
public class FixedCapacityQueue<T>
{
private T[] _buffer;
private T[] _output;
private OutputData<T> _outputData;
/// <summary>
/// Gets the dropped count. This is the number of samples that have been dropped to
/// maintain the fixed size of the datastructure.
/// </summary>
/// <value>
/// The dropped count.
/// </value>
public int DroppedCount { get; protected set; }
/// <summary>
/// The default number of dropped items required to generate a report
/// </summary>
protected const int DroppedFramesBetweenReports = 1000;
/// <summary>
/// The _start. Index of the first element in buffer.
/// </summary>
private int _start;
/// <summary>
/// The _end. Index after the last element in the buffer.
/// </summary>
private int _end;
/// <summary>
/// The _size. Buffer size.
/// </summary>
private int _numberOfItemsInBuffer;
private readonly object _lock = new object();
/// <summary>
/// Gets or sets the name of the buffer.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; protected set; }
private readonly T _default = default(T);
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The name of the queue.</param>
/// <param name="bufferSize">Size of the buffer.</param>
public FixedCapacityQueue(string name, int bufferSize)
{
Contract.Requires(bufferSize > 0);
Contract.Requires(!String.IsNullOrEmpty(name));
_buffer = new T[bufferSize];
_output = new T[bufferSize];
_outputData = new OutputData<T>(ref _output, 0);
_start = 0;
_end = _numberOfItemsInBuffer == bufferSize ? 0 : _numberOfItemsInBuffer;
Name = String.Format("FixedCapacityQueue for {0}", name);
}
/// <summary>
/// Initializes a new instance of the <see cref="FixedCapacityQueue{T}" /> class.
/// </summary>
/// <param name="name">The nameof the buffer.</param>
/// <param name="bufferSize">Size of the buffer.</param>
/// <param name="data">The data to be added to the queue.</param>
/// <exception cref="System.ArgumentException"></exception>
public FixedCapacityQueue(string name, int bufferSize, ICollection<T> data)
: this(name, bufferSize)
{
Contract.Requires(data != null);
Contract.Requires(bufferSize > 0);
Contract.Requires(data.Count < bufferSize);
foreach (var dataItem in data)
{
Enqueue(dataItem);
}
}
/// <summary>
/// Gets a value indicating whether the queue [is empty].
/// </summary>
/// <value>
/// <c>true</c> if [is empty]; otherwise, <c>false</c>.
/// </value>
public bool IsEmpty
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer == 0;
}
}
}
/// <summary>
/// Gets a value indicating whether the queue [is full].
/// </summary>
/// <value>
/// <c>true</c> if [is full]; otherwise, <c>false</c>.
/// </value>
public bool IsFull
{
get
{
lock (_lock)
{
return Count == Size;
}
}
}
/// <summary>
/// Gets the number of items currently present in the queue.
/// </summary>
/// <value>
/// The count.
/// </value>
public int Count
{
get
{
lock (_lock)
{
return _numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Gets the declared size of the queue.
/// </summary>
/// <value>
/// The size.
/// </value>
public int Size
{
get
{
lock (_lock)
{
return _buffer.Length;
}
}
}
/// <summary>
/// Dequeues an item from the queue. The expected behaviour is that if a Dequeue operation is
/// requested whilst a queue is empty, the default type of the generic queue is returned.
/// </summary>
/// <returns></returns>
public T Dequeue()
{
lock (_lock)
{
if (IsEmpty) return _default;
var item = _buffer[_start];
_buffer[_start] = _default;
Increment(ref _start);
--_numberOfItemsInBuffer;
return item;
}
}
/// <summary>
/// Enqueues the specified item to the queue.
/// </summary>
/// <param name="toAdd">To add.</param>
public void Enqueue(T toAdd)
{
lock (_lock)
{
if (IsFull)
{
_buffer[_end] = toAdd;
Increment(ref _end);
_start = _end;
DroppedCount++;
//report drops
if (DroppedCount >= DroppedFramesBetweenReports)
{
//EventAndErrorPump.Instance.WriteOnce(1000, ReportLevelEnum.Warning,
// String.Format("{0} FixedQueue Dropped Items: {1} ", Name, DroppedCount));
DroppedCount = 0;
}
}
else
{
_buffer[_end] = toAdd;
Increment(ref _end);
++_numberOfItemsInBuffer;
}
}
}
/// <summary>
/// Increments the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Increment(ref int index)
{
if (++index == Size)
{
index = 0;
}
}
/// <summary>
/// Decrements the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Decrement(ref int index)
{
if (index == 0)
{
index = Size;
}
index--;
}
public void Enqueue(IEnumerable<T> toAdd)
{
lock (_lock)
{
foreach (var dataItem in toAdd)
{
Enqueue(dataItem);
}
}
}
public IEnumerator<T> GetEnumerator()
{
var segments = new ArraySegment<T>[2] {ArrayOne(), ArrayTwo()};
foreach (ArraySegment<T> segment in segments)
{
for (int i = 0; i < segment.Count; i++)
{
yield return segment.Array[segment.Offset + i];
}
}
}
/// <summary>
/// Gets the at the specified index. Throws IndexOutOfRange for an invalid index
/// or returns the default value of the generic type on an empty queue. The head/earliest item index can also be
/// null in the event of a dequeue. If the front of the queue is required,please use the front function instead
/// </summary>
/// <param name="i">The item at that index.</param>
/// <returns></returns>
/// <exception cref="System.IndexOutOfRangeException"></exception>
public T this[int index]
{
get
{
lock (_lock)
{
if (IsEmpty)
{
return _default;
}
if (index >= _numberOfItemsInBuffer)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}",
index, _numberOfItemsInBuffer));
}
int actualIndex = InternalIndex(index);
return _buffer[actualIndex];
}
}
}
/// <summary>
/// Converts the index in the argument to an index in <code>_buffer</code>
/// </summary>
/// <returns>
/// The transformed index.
/// </returns>
/// <param name='index'>
/// External index.
/// </param>
private int InternalIndex(int index)
{
return _start + (index < (Size - _start) ? index : index - Size);
}
/// <summary>
/// Clears this instance.
/// </summary>
public void Clear()
{
lock (_lock)
{
_numberOfItemsInBuffer = 0;
_start = 0;
_end = 0;
}
}
/// <summary>
/// Takes a snapshot of the queue and returns it. If used in isolation .i.e not in a buffer implementation
/// it will return all the elements of the queue and leave the queue empty.
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData() instead")]
public T[] TakeSnapshot()
{
lock (_lock)
{
return TakeSnapshot(Count);
}
}
/// <summary>
/// Takes all data available in the queue.
/// </summary>
/// <returns></returns>
public OutputData<T> TakeAll()
{
var count = Count;
if (count <= 0) return null;
lock (_lock)
{
CopyInto(count, _output);
_outputData.SetLength(count);
return _outputData;
}
}
/// <summary>
/// Takes a snapshot of the queue using the count to limit the output to return. In the event that the specified
/// count is larger than the current size of the queue, it will throw an exception. A zero count value will immediately return
/// In a buffer implementation , since the buffer could be accepting data at the point of this call; it is a stale
/// snapshot of the queue when the call is made.
/// </summary>
/// <returns></returns>
[Obsolete("Use TakeAllData(int count) instead")]
public T[] TakeSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > _numberOfItemsInBuffer)
{
count = _numberOfItemsInBuffer;
//throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
CopyInto(count, output);
return output;
}
}
private void CopyInto(int count, T[] output)
{
var lastIndex = (_start + count) - 1;
if (lastIndex >= Size)
{
Array.Copy(_buffer, _start, output, 0, Size - _start);
Array.Copy(_buffer, 0, output, Size - _start, (lastIndex%Size) + 1);
}
else
{
Array.Copy(_buffer, _start, output, 0, count);
}
_start = (_start + count)%Size;
_numberOfItemsInBuffer = _numberOfItemsInBuffer - count;
}
public T[] PeekSnapshot()
{
lock (_lock)
{
var count = Count;
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
public T[] PeekSnapshot(int count)
{
if (count == 0) return null;
lock (_lock)
{
if (count > Size)
{
throw new ArgumentOutOfRangeException(String.Format("Queue size is {0}", Size));
}
var output = new T[count];
for (var i = 0; i < count; i++)
{
output[i] = this[i];
}
return output;
}
}
/// <summary>
/// Gets the front of the queue. The earliest item added to the queue.
/// Use this in lieu of checking this[0] as that could be null whilst items still
/// exist in the queue
/// </summary>
/// <value>
/// The front.
/// </value>
public T Front()
{
lock (_lock)
{
return IsEmpty ? _default : _buffer[_start];
}
}
/// <summary>
/// Gets the utilisation of the datastructure i.e % of the datastructure currently in use
/// </summary>
/// <value>
/// The utilisation.
/// </value>
public float Utilisation
{
get
{
lock (_lock)
{
return CalculateUtilisation();
}
}
}
private float CalculateUtilisation()
{
var util = 0f;
if (Size > 0)
{
util = (Count/(float) Size)*100;
}
return util;
}
/// <summary>
/// Returns the latest item in the queue.
/// </summary>
/// <returns></returns>
public T Back()
{
lock (_lock)
{
return Count > 0 ? _buffer[_end - 1] : _default;
;
}
}
public WeakArray<T> TakeWeakArraySnapshot()
{
lock (_lock)
{
var count = Count;
var arr = new WeakArray<T>(count);
for (var i = 0; i < count; i++)
{
arr[i] = Dequeue();
}
return arr;
}
}
/// <summary>
/// Gets the internal array. Use with EXTREME CAUTION! This is not a thread safe operation
/// and should ONLY be used in situations where any modification to the queue is not possible.
/// Modification operations include Enqueing or Dequeing
/// </summary>
/// <returns></returns>
public T[] GetInternalArray()
{
return _buffer;
}
// doing ArrayOne and ArrayTwo methods returning ArraySegment<T> as seen here:
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1957cccdcb0c4ef7d80a34a990065818d
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b
// should help a lot with the code.
#region Array items easy access.
// The array is composed by at most two non-contiguous segments,
// the next two methods allow easy access to those.
private ArraySegment<T> ArrayOne()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _start, _end - _start);
}
else
{
return new ArraySegment<T>(_buffer, _start, _buffer.Length - _start);
}
}
private ArraySegment<T> ArrayTwo()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _end, 0);
}
else
{
return new ArraySegment<T>(_buffer, 0, _end);
}
}
#endregion
}
Most data structure performance and efficiency comes down to the problem you are trying to solve.
I would recommend not to re-implement data structures yourself unless they don't exist already. Things like ngenerics exist exactly to solve those kind of problems.
This is just a sample project but I'm sure there is other types.
I'm working on .NET C# app that implements virtual files drag and drop from app to Windows Explorer. I got implementation idea and sample code from here. After file drop the app downloads a file and writes it to IStream wrapper. That works fine on Windows 7 x64, but that works very slow for files about 30 MB and larger and takes a lot of CPU resources and Memory on Windows 8.1.
Any ideas will be appreciated. Thanks in advance. The code is:
1) IStreamWrapper class
private class IStreamWrapper : Stream
{
/// <summary>
/// IStream instance being wrapped.
/// </summary>
private IStream _iStream;
/// <summary>
/// Initializes a new instance of the IStreamWrapper class.
/// </summary>
/// <param name="iStream">IStream instance to wrap.</param>
public IStreamWrapper(IStream iStream)
{
_iStream = iStream;
}
/// <summary>
/// Gets a value indicating whether the current stream supports reading.
/// </summary>
public override bool CanRead
{
get { return false; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking.
/// </summary>
public override bool CanSeek
{
get { return false; }
}
/// <summary>
/// Gets a value indicating whether the current stream supports writing.
/// </summary>
public override bool CanWrite
{
get { return true; }
}
/// <summary>
/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
public override void Flush()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the length in bytes of the stream.
/// </summary>
public override long Length
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// Gets or sets the position within the current stream.
/// </summary>
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
/// <summary>
/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
/// <summary>
/// Sets the position within the current stream.
/// </summary>
/// <param name="offset">A byte offset relative to the origin parameter.</param>
/// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
/// <summary>
/// Sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
public override void SetLength(long value)
{
throw new NotImplementedException();
}
/// <summary>
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
public override void Write(byte[] buffer, int offset, int count)
{
if (offset == 0)
{
// Optimize common case to avoid creating extra buffers
_iStream.Write(buffer, count, IntPtr.Zero);
}
else
{
// Easy way to provide the relevant byte[]
_iStream.Write(buffer.Skip(offset).ToArray(), count, IntPtr.Zero);
}
}
}
2) Creating IStream and writing to it:
public void SetData(short dataFormat, int index, Action<Stream> streamData)
{
...
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
if (streamData != null)
{
// Wrap in a .NET-friendly Stream and call provided code to fill it
using(var stream = new IStreamWrapper(iStream))
{
streamData(stream); // calls downloadFile(stream);
}
}
Marshal.ReleaseComObject(iStream);
...
}
// that will be called by streamData(stream)
private void downloadFile(Stream wStream) {
...
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode != HttpStatusCode.OK)
{
throw new WebException(String.Format("Server error (HTTP {0}: {1}).", response.StatusCode, response.StatusDescription));
}
using (Stream stream = response.GetResponseStream())
{
byte[] buffer = new byte[4096];
while ((bytesRead = stream.Read(buffer, 0, 4096)) != 0)
{
wStream.Write(buffer, 0, bytesRead);
}
}
...
}