In my Silverlight/XNA app/game I save and load a custom class Level.cs to Isolated Storage. I serialize and deserialize the level using SharpSerializer. The code below is combined from several tutorials on saving and serializing. The code works most of the time, but if I save and load a level repeatedly, usually 2 or 3 times, I will get an exception thrown in the Load() method. I have been unable to track down the cause of this exception. Right now I am handling it by restarting the level from the default XML file when this occurs.
My Questions:
1.) Do you know what is causing my exception, and how can I fix it?
2.) Are there any additional details in the Exception that I am catching that might help me track down what is causing this?
3.) Is there a better method for structuring this code? Most examples I have found use "using" statements. Is there an advantage to that method that might help me here?
Additional Details:
The strange part is that if I quit the app without saving and then run the app again, it will successfully load the Isolated Storage file that it just failed to load. This leads me to believe that the save file is not corrupted, but that I am likely not disposing of some resources properly each time I save/load and as those resources build up during an instance of running the app, it eventually causes this issue.
When the exception is thrown, it is always thrown by this line in the Load() method:
LoadStream = LoadStorage.OpenFile(loadName, FileMode.Open);
The exception description is {"Operation not permitted on IsolatedStorageFileStream."}. Are there any other relavant details in the exception that I should look for? The inner exception could not be read.
My method for saving:
public void Save()
{
IsolatedStorageFile SaveStorage = null;
SaveStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream SaveStream = null;
string saveLName = "levelSave_" + currentLevel.info.number + ".XML";
if (SaveStorage.FileExists(saveLName))
{
SaveStorage.DeleteFile(saveLName);
}
try
{
SaveStream = SaveStorage.CreateFile(saveLName);
var serializer = new SharpSerializer();
serializer.Serialize(currentLevel, SaveStream);
saveState = SaveState.Successful;
}
catch (Exception ex)
{
saveState = SaveState.Failed;
}
finally
{
if (SaveStream != null)
{
SaveStream.Close();
SaveStream.Dispose();
} if (SaveStorage != null)
{
SaveStorage.Dispose();
}
}
}
My method for loading:
public Level LoadLevel(int levelNumber)
{
IsolatedStorageFile LoadStorage;
LoadStorage = IsolatedStorageFile.GetUserStoreForApplication();
Level tmpLevel;
string loadName = "levelSave_" + levelNumber + ".XML";
if (LoadStorage.FileExists(loadName))
{
IsolatedStorageFileStream LoadStream = null;
try
{
LoadStream = LoadStorage.OpenFile(loadName, FileMode.Open);
var serializer = new SharpSerializer();
tmpLevel = (Level)serializer.Deserialize(LoadStream);
}
catch (Exception ex)
{
tmpLevel = LoadLevelXML(levelNumber);
// Level failed to load from save,
// restart unsaved level from beginning
}
finally
{
if (LoadStream != null)
{
LoadStream.Close();
LoadStream.Dispose();
}
if (LoadStorage != null)
{
LoadStorage.Dispose();
}
}
}
else
{
tmpLevel = LoadLevelXML(levelNumber);
// Level save file not found,
// restart unsaved level from beginning
}
return tmpLevel;
}
I believe this code is probably unnecessary in the Save() method. I just added it to make sure overwriting a file was not the problem.
if (SaveStorage.FileExists(saveLName))
{
SaveStorage.DeleteFile(saveLName);
}
I also believe that this code in both methods is unnecessary, but again, I added it to ensure that the "storage" floating around was not the issue.
if (LoadStorage != null)
{
LoadStorage.Dispose();
}
The exception was thrown before I added either of those segments of code.
I have a feeling that blocking IO operations may throw an IllegalOperationException if performed on the UI thread.
Have you tried performing your serialisation on a background thread (for example, using BackgroundWorker)?
Now, just for kicks, give this a spin:
lock (SaveStorage)
{
if (SaveStorage.FileExists(saveLName))
{
SaveStorage.DeleteFile(saveLName);
}
}
You should be using Mutex concept..refer these..
http://msdn.microsoft.com/en-us/library/ff402541(v=vs.92).aspx
http://forums.create.msdn.com/forums/p/94965/568077.aspx
Related
I'm writing a .NET 6 application for which users can create plugins. In some situations however, when a plugin throws an unhandled exception, my own application crashes as well. That should not happen, no matter what. The plugin may stop working, it may unload, it may whatever, just leave the parent app alive.
Loading happens like this:
public static ServiceInfo? LoadService(string relativePath)
{
var loadContext = new ServiceLoadContext(relativePath);
_alcs.Add(loadContext);
try
{
var assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(relativePath)));
var shouldLoadDll = false;
foreach (var type in assembly.GetTypes())
{
if (typeof(IMonitorableService).IsAssignableFrom(type))
{
var directoryName = new FileInfo(relativePath).Directory!.FullName;
if (Activator.CreateInstance(type, new object[] { directoryName }) is IMonitorableService result)
{
shouldLoadDll = true;
return new ServiceInfo
{
Category = Directory.GetParent(relativePath)!.Name,
Name = Path.GetFileNameWithoutExtension(relativePath),
AssemblyPath = relativePath,
Service = result!
};
}
}
}
if (!shouldLoadDll)
{
loadContext.Unload();
}
}
catch (Exception)
{
// This is handled, but this won't catch the exception in the plugin
}
return null;
}
I have my share of try/catch phrases, and since these IMonitorableServices are BackgroundServices, they're started like
public async Task StartAsync(CancellationToken cancellationToken)
{
foreach (var service in _options.Services)
{
try
{
await service.Service.StartAsync(cancellationToken);
}
catch (Exception ex)
{
// This is handled, but it won't catch the exception in the plugin
}
}
}
Now I doubt that it's really relevant to provide the specific error, but just in case: it's a
'System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute',
following an operation on event subscriptions. I know how to solve that in the plugin, but I could never trust my future plugin writers to always handle their exceptions (or prevent them from happening). I need some way to catch absolutely everything in my own application. I've been breaking my head over this and I can find many considerations on plugins loaded in AppDomains, but they're from the .NET Framework era...
Who has an idea how to solve this? I could hardly imagine this is something that has been overlooked in .NET Core/6 development.
Update: I find that other type of exceptions actually are caught within the StartAsync method. So it might have something to do with the exception being raised from an event in the plugin (don't want to put you on the wrong track though). I must add, the event is registered from within the StartAsync method, but it seems to bypass the regular catch.
I am developing in C# for the Motorola device "MC67" and I have having issues with initialising the scanner.
The code I am using seems to be generic as I have found similar examples all over the Internet; for reference here is the code that is causing me problems:
/// <summary>
/// Initialize the reader.
/// </summary>
///
public override bool InitReader()
{
Logger.Instance.Debug("InitReader");
bool result = false;
// Logger.Instance.AddToDebuggerLog("Symbol.InitReader");
// If reader is already present then fail initialize
if (this._MyReader != null)
{
return false;
}
try
{
// Create new reader, first available reader will be used.
this._MyReader = new Symbol.Barcode.Reader();
// Create reader data
this._MyReaderData = new Symbol.Barcode.ReaderData(
Symbol.Barcode.ReaderDataTypes.Text,
Symbol.Barcode.ReaderDataLengths.MaximumLabel);
// Enable reader, with wait cursor
this._MyReader.Actions.Enable();
if ((GetDeviceType() != DeviceTypes.SymbolMC3070) && (GetDeviceType() != DeviceTypes.SymbolMC3090BT))
{
this._MyReader.Parameters.Feedback.Success.BeepTime = 0;
}
else
{
this._MyReader.Parameters.Feedback.Success.BeepTime = 50;
}
SetScannerDecoderTypeToUseWithScanSys();
result = true;
}
catch (Exception ex)
{
// Something has gone wrong Initializing barcode reader etc
// Log Exception
Logger.Instance.Exception("InitReader", ex);
// Ensure reader is Disposed
if (_MyReader != null)
{
try
{
_MyReader.Dispose();
}
catch
{
// Just incase something goes wrong
Logger.Instance.Error("Error Disposing MyReader in InitReader Exception");
}
_MyReader = null;
}
// Ensure ReaderData is Disposed
if (_MyReaderData != null)
{
try
{
_MyReaderData.Dispose();
}
catch
{
// Just incase something goes wrong
Logger.Instance.Error("Error Disposing MyReaderData in InitReader Exception");
}
_MyReaderData = null;
}
// null the EventHandler
_MyEventHandler = null;
}
return result;
}
My problem is that when the above method is called, the following line produces an exception error:
this._MyReader.Actions.Enable();
The exception is "OperationFailureException" and the error message mentions "Get all supported attributes failed : E_SCN_INVALIDIOCTRL"
Now the strange thing is that I am able to actually use the scanner on the device correctly, so I can scan barcodes and read the data even with this exception but the fact that it is happening concerns me so I am trying to prevent it.
Does anyone have any idea why I am getting the exception or any suggestions of things I can try?
This is a "handled" exception in the Symbol library. Just turn off the breakpoint for thrown exception-- Ctrl-Alt-E, in the row "Common Language Runtime Exceptions" uncheck the box under "Thrown". Unfortunately if you're trying to debug an exception that isn't working correctly, you just gotta keep pressing play every time this exception comes up.
I haven't found a way to make it stop throwing the exception though... I'd really like to be able to turn off whatever feature is failing.
The error occurs randomly, my guess is when there is heavy traffic, but i'm having a hard time replicating it. This functionality runs everytime a business transaction is initiated.
Error: System.IO.IOException: The process cannot access the file ''
because it is being used by another process. at
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
private void writeToTrafficLogFile(string data, string filePath)
{
try
{
StreamWriter logWriter;
if (!File.Exists(filePath))
{
logWriter = new StreamWriter(filePath);
}
else
{
logWriter = File.AppendText(filePath);
}
logWriter.WriteLine(DateTime.Now);
logWriter.WriteLine(data);
logWriter.WriteLine();
logWriter.Close();
}
catch (Exception error) {
sendLoggingErrorEmail(error, "Error Writing writeToTrafficLogFile", "Error Writing writeToTrafficLogFile.\r\n");
}
}
#endregion
}
It might be easier and more bulletproof to switch to an existing, well tested logging solution. Several exist, have a look at dotnetlogging.com where dozens are listed. I can't recommend any, right now I am stuck with log4net but I can't recommend it.
You're probably calling that from multiple threads at the same time...
There are two possible solutions:
A: Create a single thread that writes to the log file from a string that other threads can write to. -- Clarification edit: Have a class with code like
public static List<string> logme = new List<string>();
// Launch LogLoop as a thread!
public static void logloop()
{
while (true)
{
while (logme.Count > 0)
{
File.AppendAllText("log.txt", logme[0] + "\r\n");
logme.RemoveAt(0);
}
Thread.Sleep(500);
}
}
// Oh, and whenever you want to log something, just do:
logme.add("Log this text!");
B: Use a Lock on the log writer.
How can I get my exception from my class to show as a windows error ?
This is my class :
public class Editcap
{
private string _newFileName;
public void convertFileToLibpcap(string filePath)
{
FileInfo fileInfo = new FileInfo(filePath);
_newFileName = fileInfo.FullName.Replace(fileInfo.Extension, "_new") + ".pcap";
invokeProcess(WiresharkProcesses.Editcap, string.Format("{2}{0}{2} -F libpcap {2}{1}{2}", fileInfo.FullName, _newFileName, "\""));
deleteFile(filePath);
}
private void deleteFile(string filePath)
{
try
{
File.Delete(filePath);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
public string getNewFileName()
{
return _newFileName;
}
}
From my form:
The function creates new wireshark file with different extension and should delete the old file. If it fails to delete, I want to show the message error in pop up window
Editcap editpcap = new Editcap();
editpcap.convertFileToLibpcap(file.FullName);
You don't need to do anything, the exception will bubble up all on it's own; it's how they work.
Just add a Try/Catch around your calling code:
try
{
Editcap editpcap = new Editcap();
editpcap.convertFileToLibpcap(file.FullName);
}
catch(Exception e)
{
MessageBox.Show("There was an error deleting the file.");
}
Note that there is no need (and in fact some harm) to catching and then re-throwing a new exception in your deleteFile method. You're changing the type of the exception from the more specific and informative IOException to the less expressive Exception, and you're also taking out a lot of the stack trace information. It would be better to just remove that try/catch entirely from deleteFile. It also means you can change the calling code to catch IOException instead of the global Exception. By doing this you can potentially have different catch blocks for different exceptions, allowing you to treat them differently (perhaps crash with one type, log an error for a different one, show a message to the user for something else, etc.).
When a file is being copied to the file watcher folder, how can I identify whether the file is completely copied and ready to use? Because I am getting multiple events during file copy. (The file is copied via another program using File.Copy.)
When I ran into this problem, the best solution I came up with was to continually try to get an exclusive lock on the file; while the file is being written, the locking attempt will fail, essentially the method in this answer. Once the file isn't being written to any more, the lock will succeed.
Unfortunately, the only way to do that is to wrap a try/catch around opening the file, which makes me cringe - having to use try/catch is always painful. There just doesn't seem to be any way around that, though, so it's what I ended up using.
Modifying the code in that answer does the trick, so I ended up using something like this:
private void WaitForFile(FileInfo file)
{
FileStream stream = null;
bool FileReady = false;
while(!FileReady)
{
try
{
using(stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
FileReady = true;
}
}
catch (IOException)
{
//File isn't ready yet, so we need to keep on waiting until it is.
}
//We'll want to wait a bit between polls, if the file isn't ready.
if(!FileReady) Thread.Sleep(1000);
}
}
Here is a method that will retry file access up to X number of times, with a Sleep between tries. If it never gets access, the application moves on:
private static bool GetIdleFile(string path)
{
var fileIdle = false;
const int MaximumAttemptsAllowed = 30;
var attemptsMade = 0;
while (!fileIdle && attemptsMade <= MaximumAttemptsAllowed)
{
try
{
using (File.Open(path, FileMode.Open, FileAccess.ReadWrite))
{
fileIdle = true;
}
}
catch
{
attemptsMade++;
Thread.Sleep(100);
}
}
return fileIdle;
}
It can be used like this:
private void WatcherOnCreated(object sender, FileSystemEventArgs e)
{
if (GetIdleFile(e.FullPath))
{
// Do something like...
foreach (var line in File.ReadAllLines(e.FullPath))
{
// Do more...
}
}
}
I had this problem when writing a file. I got events before the file was fully written and closed.
The solution is to use a temporary filename and rename the file once finished. Then watch for the file rename event instead of file creation or change event.
Note: this problem is not solvable in generic case. Without prior knowledge about file usage you can't know if other program(s) finished operation with the file.
In your particular case you should be able to figure out what operations File.Copy consist of.
Most likely destination file is locked during whole operation. In this case you should be able to simply try to open file and handle "sharing mode violation" exception.
You can also wait for some time... - very unreliable option, but if you know size range of files you may be able to have reasonable delay to let Copy to finish.
You can also "invent" some sort of transaction system - i.e. create another file like "destination_file_name.COPYLOCK" which program that copies file would create before copying "destination_file_name" and delete afterward.
private Stream ReadWhenAvailable(FileInfo finfo, TimeSpan? ts = null) => Task.Run(() =>
{
ts = ts == null ? new TimeSpan(long.MaxValue) : ts;
var start = DateTime.Now;
while (DateTime.Now - start < ts)
{
Thread.Sleep(200);
try
{
return new FileStream(finfo.FullName, FileMode.Open);
}
catch { }
}
return null;
})
.Result;
...of course, you can modify aspects of this to suit your needs.
One possible solution (It worked in my case) is to use the Change event. You can log in the create event the name of the file just created and then catch the change event and verify if the file was just created. When I manipulated the file in the change event it didn't throw me the error "File is in use"
If you are doing some sort of inter-process communication, as I do, you may want to consider this solution:
App A writes the file you are interested in, eg "Data.csv"
When done, app A writes a 2nd file, eg. "Data.confirmed"
In your C# app B make the FileWatcher listen to "*.confirmed" files. When you get this event you can safely read "Data.csv", as it is already completed by app A.
(Edit: inspired by commets) Delete the *.confirmed filed with app B when done processing the "Data.csv" file.
I have solved this issue with two features:
Implement the MemoryCache pattern seen in this question: A robust solution for FileSystemWatcher firing events multiple times
Implement a try\catch loop with a timeout for access
You need to collect average copy times in your environment and set the memory cache timeout to be at least as long as the shortest lock time on a new file. This eliminates duplicates in your processing directive and allows some time for the copy to finish. You will have much better success on first try, which means less time spent in the try\catch loop.
Here is an example of the try\catch loop:
public static IEnumerable<string> GetFileLines(string theFile)
{
DateTime startTime = DateTime.Now;
TimeSpan timeOut = TimeSpan.FromSeconds(TimeoutSeconds);
TimeSpan timePassed;
do
{
try
{
return File.ReadLines(theFile);
}
catch (FileNotFoundException ex)
{
EventLog.WriteEntry(ProgramName, "File not found: " + theFile, EventLogEntryType.Warning, ex.HResult);
return null;
}
catch (PathTooLongException ex)
{
EventLog.WriteEntry(ProgramName, "Path too long: " + theFile, EventLogEntryType.Warning, ex.HResult);
return null;
}
catch (DirectoryNotFoundException ex)
{
EventLog.WriteEntry(ProgramName, "Directory not found: " + theFile, EventLogEntryType.Warning, ex.HResult);
return null;
}
catch (Exception ex)
{
// We swallow all other exceptions here so we can try again
EventLog.WriteEntry(ProgramName, ex.Message, EventLogEntryType.Warning, ex.HResult);
}
Task.Delay(777).Wait();
timePassed = DateTime.Now.Subtract(startTime);
}
while (timePassed < timeOut);
EventLog.WriteEntry(ProgramName, "Timeout after waiting " + timePassed.ToString() + " seconds to read " + theFile, EventLogEntryType.Warning, 258);
return null;
}
Where TimeoutSeconds is a setting that you can put wherever you hold your settings. This can be tuned for your environment.