Is there a way to share non-serializable control object between two different applications.
I've used below code for sharing data among two applications, it is working fine.
My problem is that i need to share non-serializable objects between these applications.
Application One
using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
{
bool mutexCreated;
Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
string path = #"Second Application's path";
//Run second application
Process pr = new Process();
ProcessStartInfo prs = new ProcessStartInfo();
prs.FileName = path;
pr.StartInfo = prs;
bool ret = pr.Start();
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
MessageBox.Show(String.Format("Process A says: {0}", reader.ReadBoolean()));
MessageBox.Show(String.Format("Process B says: {0}", reader.ReadBoolean()));
}
mutex.ReleaseMutex();
}
Applicatin Second
try
{
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(1, 0))
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(0);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
MessageBox.Show("Memory-mapped file does not exist. Run Process A first.");
}
Can any one help me with the solution.
To my knowledge, memory mapped files cannot directly contain live managed objects. There are two ways to use memory mapped files: using MemoryMappedViewStream (which our example is doing), or using MemoryMappedViewAccessor. The latter lets you treat it as a big buffer of memory, reading and writing it with calls like ReadDouble(offset) and Write(offset, doubleValue). There are also accessor methods for reading and writing arrays.
Related
I created a C# program which does some stuff. Let's call it Program B. I want to open Program B's executable in another program, Program A, I am creating also in C#. What I want to do is to get some information(actually a custom object) from Program B on a button click which also will close the program and add the information in a List in Program A. I want to use Memory Mapped Files for this, but somehow I didn't manage to do this until now. I have created a class:
public class MemoryMappedFileCommunicator{
public MemoryMappedFileCommunicator()
{
MemoryMappedFile = MemoryMappedFile.CreateOrOpen(MMF_FILE_NAME, MMF_MAX_SIZE);
// Create a stream which allows it to write data from offset 0 to 1024 (whole memory)
MemoryMappedViewStream = MemoryMappedFile.CreateViewStream(0, MMF_VIEW_SIZE);
_commandToSend = new Command();
//callback = new SendOrPostCallback(OnDataReceivedInternal);
//operation = AsyncOperationManager.CreateOperation(null);
}
}
I tried using SendOrPostCallback seen in a tutorial, but it didn't make sense to me. I also tried using an event handler, but my event did not fire.
What I actually want to do is to fire an event every time I finished writing in the file, so Program A could read the information and put it into a List.
Write function looks like this:
public void WriteData(MyCustomObject obj)
{
FileStream writer = new FileStream(MMF_FILE_NAME, FileMode.Append);
DataContractSerializer ser = new(typeof(MyCustomObject ));
ser.WriteObject(writer, obj);
writer.Close();
DataWrittenEvent();
}
My delegate is void and has no parameters
Read function looks like this:
public MyCustomObject ReadData()
{
FileStream reader = new FileStream(MMF_FILE_NAME, FileMode.Open);
XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(reader, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(MyCustomObject ));
MyCustomObject deserializedObj = (MyCustomObject )ser.ReadObject(xmlDictionaryReader, true);
xmlDictionaryReader.Close();
reader.Close();
return deserializedObj;
}
In every program I create an instance of MemoryMappedFileCommunicator, but only in Program A's constructor I attach the DataWrittenEvent event, but it is null all the time.
What am I doing wrong? How could I manage to do this?
I expected to make a callback from Program B to Program A like this:
Program B: get information -> serialize information -> write serialized information to MMF (this should rise an event to Program A)
Program A: read from MMF -> deserialize information -> add to list
So I figured it out finally. The way I work with the 2 processes is the following:
I have process A from which I will open process B. Process B will
calculate some stuff and on the button click "Add" it will add the
information into a MemoryMappedFile (created in Process A before
opening Process B). It is important to give it a name and set the
HandleInheritability to HandleInheritability.Inheritable. Also an EventWaitHandle is created in Process A in order to wait for Process B to do its job. I am sending objects between the two processes, so I
serialize the object and after that I write the information in the
MMF. The same at reading, I deserialize the object after reading it
from the MMF.
The solution looks like this:
Process A:
EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset, "ObjectAddedEvent");
MyObject obj= new();
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen("ObjectMMF", 1024, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, HandleInheritability.Inheritable))
{
Process myProcess = Process.Start(psi);
ewh.WaitOne(); // Here we wait for Process B to do its job
// This will be executed AFTER Process B finishes its task (after ewh.Set())
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
string receivedObjectString = reader.ReadString();
if (!string.IsNullOrEmpty(receivedObjectString ))
obj= JsonSerializer.Deserialize<MyObject>(receivedObjectString);
}
// AddReceivedObjectToList(obj); -- this is a function I need in my program. It adds the object received from Process B into a list in Process A, after it is read by Process A from the MMF
ewh.Dispose();
myProcess.Kill(); // I will kill Process B manually after Process A read everything from the MMF
Process B:
MyObject obj= GetObject();
string serializedObject = JsonSerializer.Serialize(obj);
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("ObjectMMF", MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable))
{
bool ewhExists = EventWaitHandle.TryOpenExisting("ObjectAddedEvent", out EventWaitHandle ewh);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(serializedObject);
}
ewh.Set(); // This gives Process A a signal that it finished its job and Process A will continue to read the data from the MMF
}
I'm using the following lines of code in order to write credentials of users to a text file. It's supposed to create the directory inside AppData (which it does) but it doesn't write the credentials to the text file, it leaves it blank!
public void RegisterUserCreds()
{
string[] creds = { Username.Text, Password.Text };
string roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(roaming + "/Launcher"))
Directory.CreateDirectory(roaming + "/Launcher");
string specificFolder = roaming + "/Launcher/user_info.txt";
var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite);
var sw = new StreamWriter(fs);
sw.WriteLine(Username.Text);
fs.Close();
}
What's the problem? Thanks!
Just use the using statement when operating on streams:
public static void RegisterUserCreds()
{
string[] creds = { Username.Text, Password.Text };
string roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(roaming + "/Launcher")) Directory.CreateDirectory(roaming + "/Launcher");
string specificFolder = roaming + "/Launcher/user_info.txt";
using (var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(Username.Text);
}
}
}
In your code you were closing the file stream before the stream writer was able to flush the changes you want to write so the file was created empty.
You're closing the wrong stream. When you create new stream objects and pass an existing stream to the constructor, that new stream now "owns" the old stream. When you dispose of the newer stream it will automatically dispose of the older one.
In your situation, you're closing the "fs" stream, but the "sw" stream might not have actually written to it yet (it has it's own internal buffer). If you were to close the "sw" stream instead, it would flush it's buffer (into the "fs" stream), and then it would call fs.Dispose() for you to make sure it did the same thing.
There's a much better way, that would help you avoid doing things out-of-order like this, as well as make sure you're calling Dispose() even if exceptions get thrown (streams implement IDisposable, so you're supposed to always call their Dispose() method when you're done with them so they can internally "clean up"). The using statement is perfect for this, since it will call Dispose() even if an exception is thrown (it's a shortcut for wrapping the code with try/finally blocks):
using (var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(Username.Text);
}
}
This is the same as this:
try
{
var fs = File.Open(specificFolder, FileMode.OpenOrCreate, FileAccess.ReadWrite);
try
{
var sw = new StreamWriter(fs);
sw.WriteLine(Username.Text);
}
finally
{
sw.Dispose();
}
}
finally
{
fs.Dispose();
}
Even though sw.Dispose() will call fs.Dispose() for you, there is no harm in calling fs.Dispose() again. Why is it important to call Dispose()? Let's say an exception was thrown during sw.WriteLine() (e.g. out of disk space, I/O error, etc.)... the file would stay open until your app terminated. The using (or the try/catch version) would make sure the file was closed no matter what.
(side note: with streams, Dispose() and Close() do the same thing, you don't need to call both. Close() just calls Dispose() -- MS included a method called Close() because that was what people were used to with a file API, but the .NET IDisposable interface uses a method called Dispose())
(another side note: starting with .NET 4.5, many of the stream classes have an additional constructor that has a new "leaveOpen" parameter... passing true would tell that stream to NOT dispose of the original stream automatically)
When using a file stream, and setting FileShare to None, and say two users accessing the same function at the same time want to read/write to that file. Will FileShare.None make the second users request waiting or will the second user's request throw an exception?
//two users get to this this code at the same time
using (FileStream filestream = new FileStream(chosenFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
using (StreamReader sr = new StreamReader(filestream))
using (StreamWriter sw = new StreamWriter(filestream))
{
//reading and writing to file
}
Msdn says: None Declines sharing of the current file. Any request to open the file (by this process or another process) will fail until the file is closed.
But will requests keep trying until the filestream is closed?
When a process opean a file for Read/Write with FileShare.None any subsequent access by any process on this same file will result in Acess Denied Exception. To answer your question, Second user will get exception.
MSDN: FileShare.None - Declines sharing of the current file. Any request to open the
file (by this process or another process) will fail until the file is
closed.
There are many ways you can handle these kind of concurrent file access issues, Following code demonstrates a simple approach to tackle this situation.
//Retry 5 times when file access fails
int retryCounter = 5;
while (!isFileAccessSuccess && retryCounter > 0)
{
try
{
//Put file access logic here
//If the file has been accessed successfully set the flag to true
isFileAccessSuccess = true;
}
catch (Exception exception)
{
//Log exception
}
finally
{
//Decrease the retry count
--retryCounter;
}
if (!isFileAccessSuccess)
{
//Wait sometime until initiating next try
Thread.Sleep(10000);
}
}
No, IOException will be thrown an with HResult = -2147024864 and Message = The process cannot access the file 'path' because it is being used by another process.
if you want to synchronize access to a file you can use a named wait handle.
public class FileAcessSynchronizer
{
private readonly string _path;
private readonly EventWaitHandle _waitHandle;
public FileAcessSynch(string path)
{
_path = path;
_waitHandle = new EventWaitHandle(true, EventResetMode.AutoReset, "NameOfTheWaitHandle");
}
public void DoSomething()
{
try
{
_waitHandle.WaitOne();
using (FileStream filestream = new FileStream(chosenFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
using (StreamReader sr = new StreamReader(filestream))
using (StreamWriter sw = new StreamWriter(filestream))
{
//reading and writing to file
}
}
finally
{
_waitHandle.Set();
}
}
}
since named wait handle creates a critical section no two threads or processes of your application (that use same name as wait handle name) can execute the codes in it concurrently. So one thread or process enters the section, opens the file in the way that no one can access it (other applications), execute commands and at the end leaves the critical section to allow other threads or processes of your application enters the critical section.
I am using MemoryMappedFile for communication between 2 programs. Program "A" creates the mmf and reads it's contents on a timer. Program "B" writes xml data to the mmf on a timer. I have the memory map working but I run into an issue where the previous iteration of the XML data is longer than the current and old data gets carried over to the next round.
so for simplicity lets say program B writes
aaaa
Program A will read correctly,
Then the next write from program B is:
b
Program A reads
baaa
It seems like there should be some simple way to flush the contents of the memory mapped file but I can't seem to figure it out. It's very possible that I'm totally wrong in the way I'm going about this.
Here's what I'm currently doing.
Program A:
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap",MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
string outputtext;
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0,0))
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyObject));
TextReader textReader = new StreamReader(stream);
outputtext = textReader.ReadToEnd();
textReader.Close();
}
mutex.ReleaseMutex();
return outputtext; //ends up in a textbox for debugging
}
Program B
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap", MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 0))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
TextWriter textWriter = new StreamWriter(stream);
serializer.Serialize(textWriter, myObjectToExport);
textWriter.Flush();
}
mutex.ReleaseMutex();
}
Assuming length is reasonably small, you could really clear it out
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.BaseStream.Write(new byte[length], 0, length);
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
EDIT: I think I misunderstood the OP's question. The problem he was having was not with clearing the contents of the MMF, but with stream manipulation. This should fix the problem:
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.Write("");
textWriter.Flush();
That being said, you might want to do both.
I haven't really worked with MemoryMappedStreams much but this question seemed interesting so I took a crack at it. I wrote a really basic windows example with two buttons (read/write) and a single text box. I didn't pass in "0, 0" to the CreateViewStream calls and I created the file with a fixed length using a call to "CreateOrOpen" and everything worked well! The following are the key pieces of code that I wrote:
WRITE The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var writer = new StreamWriter(stream);
writer.WriteLine(txtResult.Text);
writer.Flush();
}
}
finally { mutex.ReleaseMutex(); }
}
READ The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var textReader = new StreamReader(stream);
txtResult.Text = textReader.ReadToEnd();
textReader.Close();
}
}
finally { mutex.ReleaseMutex(); }
}
Dispose the file (after finished)
if (sharedFile != null) sharedFile.Dispose();
For the full example, see here: https://github.com/goopyjava/memory-map-test. Hope that helps!
EDIT/NOTE - If you look at the example provided you can write to the file as many times as you want and any time you read you will read exactly/only what was written last. I believe this was the original goal of the question.
Is there a way to restart a windows application written in .NET using .NET code
I mean the application should exit and restart itself, on click of a button.
Application.Restart() is your method :)
Here is another StackOverflow answer that points out a couple of "watch-out-for's" with using this method.
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.restart.aspx
I had a similar problem, but mine was related to unmanageable memory leak that I couldn't find on an app that has to run 24/7. With the customer I agreed that safe time to restart the app was 03:00AM if the memory consumption was over the defined value.
I tried Application.Restart, but since it seems to use some mechanism that starts new instance while it is already running, I went for another scheme. I used the trick that file system handles persist until process that created them dies. So, from The Application, i dropped the file to the disk, and didn't Dispose() the handle. I used the file to send 'myself' executable and starting directory also (to add flexibility).
Code:
_restartInProgress = true;
string dropFilename = Path.Combine(Application.StartupPath, "restart.dat");
StreamWriter sw = new StreamWriter(new FileStream(dropFilename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite));
sw.WriteLine(Application.ExecutablePath);
sw.WriteLine(Application.StartupPath);
sw.Flush();
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Application.StartupPath, "VideoPhill.Restarter.exe"),
WorkingDirectory = Application.StartupPath,
Arguments = string.Format("\"{0}\"", dropFilename)
});
Close();
Close() at the end would initiate app shutdown, and file handle I used for StreamWriter here would be held open until process really dies. Then...
Restarter.exe comes into action. It TRIES to read the file in exclusive mode, preventing it to gain access until main app wasn't dead, then starts main app, deletes the file and exists. I guess that it can't be simpler:
static void Main(string[] args)
{
string filename = args[0];
DateTime start = DateTime.Now;
bool done = false;
while ((DateTime.Now - start).TotalSeconds < 30 && !done)
{
try
{
StreamReader sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
string[] runData = new string[2];
runData[0] = sr.ReadLine();
runData[1] = sr.ReadLine();
Thread.Sleep(1000);
Process.Start(new ProcessStartInfo { FileName = runData[0], WorkingDirectory = runData[1] });
sr.Dispose();
File.Delete(filename);
done = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Thread.Sleep(1000);
}
}