I’m trying to use the File class to work with a text file in a console and winforms desktop app and getting the following exception:
The type initializer for '_Library.Logging' threw an exception
From what I’ve read here this error is typically caused by a problem in App.config for Winfoms apps but the Exception details seem to point elsewhere:
System.ArgumentNullException: Value cannot be null. Parameter name: path
at System.IO.File.AppendAllText(String path, String contents)
The MSDN examples for file manipulation all hard code the path parameter without any reference to using an App.confiig file so my presumption was it is possible to do this without involving ConfigurationManager.
This is the code I'm trying to use
// in calling method
class Program_Console
{
private static StringBuilder SB4log = new StringBuilder();
public static void Main(string[] tsArgs)
{
// Conditionals dealing with argumentts from Task Scheduler
Save2Log("Calling _UI.exe via \"Process.Start()\"", true);
// try-catch-finally for Process.Start
}
private static void Save2Log(string msgTxt, bool noTS)
{
SB4log.AppendLine($"{msgTxt}");
if (noTS) Logging.SaveLog(SB4log);
else Logging.SaveLog_TimeStamp(SB4log);
SB4log.Clear();
}
}
// saving app progression messages to a single log txt file
public static class Logging
{
private static String filePath = Connections.LogPath();
private static StringBuilder SB4log = new StringBuilder();
public static void SaveLog(StringBuilder logTxt)
{
File.AppendAllText(filePath, logTxt.ToString());
logTxt.Clear();
}
}
// class for DB connection and file paths
public static class Connections
{
private static StringBuilder SB4log = new StringBuilder();
public static string AppPath()
{
string appRoot;
try
{
string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
SaveLog($"->App Path: {appDir}", true); // culprit
int loc = appDir.IndexOf("BURS");
appRoot = appDir.Substring(0, loc + 5);
}
catch (Exception ex)
{
// Exception response and MessageBox
}
return appRoot;
}
public static string ConsolePath() {}
public static string UIPath() {}
public static string LogPath()
{
string appRoot = AppPath();
string wrkDir = #"_Library\Data\BURS_Log.Txt";
string fullDir = $"{appRoot}{wrkDir}";
SaveLog($"->Log Path: {fullDir}\n", true); // culprit
return fullDir;
}
}
In stepping through the code the code the variable containing the path -- filePath -- has the expected value: "D:\BURS_Library\Data\BURS_Log.Txt" (quotes used to show there re no unintended spaces needing to be trimmed). Acordinng to MSDN if it's a malformed path an exception will be thrown but the path looks valid to me.
Why isn’t the Path variable utilized?
Any help will be appreciated.
Edit: expanded code to show start-to-finish flow since the original abridged version seemed to be confusing. Have added the text "//culprit" to the two lines which caused the error as pointed out by the responders.
It's not clear what Connections is, but given Connections.LogPath(); it seems that you're calling LogPath(); to set the value for filePath which is a problem because that calls AppPath which has the following statement SaveLog($"->App Path: {appDir}", true);.
You haven't included a version of SaveLog that has 2 parameters, but assuming it's similar to the one you've posted, you're attempting to use filePath when the value hasn't been set yet - which causes an issue.
Related
I have the following class which writes new line to a text file.
using System;
using System.Text;
using System.IO;
namespace TextStreamer
{
class TextWriter
{
private string _FilePath;
private string _TextToAdd;
// Constructor will assign file Path variable and check if file is valid
public TextWriter(string filePath)
{
this._FilePath = filePath;
ValidateFile();
}
// Validate if the text file exist
private void ValidateFile()
{
// If file does not exist show error message
// and create new text file
if(!File.Exists(_FilePath))
{
Console.WriteLine("File not found");
File.Create(_FilePath);
Console.WriteLine("Created new file {0}", _FilePath);
}
}
// Write new line to the text file
public void WriteNewLine(string text)
{
this._TextToAdd = text + Environment.NewLine;
File.AppendAllText(_FilePath, _TextToAdd);
}
}
}
Right now if the file does not exist it will write a message to the console and then it will create the text file, but what if i used say WPF application, in this case i prefer showing a message box with the same message, how can i achieve that.
I tried throwing exception FileNotFoundException but that just crashes the program and exit.
A simple way to achieve this is by using a public variable to change the option to console log or show message.
Add the namespace for the generic message box:
using System.Windows;
And add a public variable in your class that will let you programmatically change the logging method, such as:
public bool UseMsgBox = false;
You could improve this by using things like using an int or enum to have more logging methods, though a bool is fine for only 2 options.
Add a logging method such as:
private void LogMsg(string msg)
{
if(UseNsgBox) MessageBox.Show(msg);
else Console.WriteLine(msg);
}
And replace your Console.WriteLine's with LogMsg instead.
Make sure to change the option when you create your class:
TextWriter textWriter = new TextWriter("SomeFile.txt");
textWriter.UseMsgBox = true; // or false
Actually, this might not work as you instantly call the LogMsg when your class is created, so perhaps add it as an initialization parameter as well:
public TextWriter(string filePath, bool useMsgBox = false)
{
UseMsgBox = useMsgBox;
// ...
}
Given the below console application, I have a problem understanding the .net Framework behavior.
I do have an absolute path to a folder (Windows), that I want to create. To avoid a PathTooLongException during Directory.CreateDirectory() I check for that in advance by doing a Path.GetFullPath() for the path. However, this is not working for a reason I don't understand.
My questions are:
Why is this happening?
How can I (really, as in reliably) check if the path is to long before creating the directory
Notes:
- Simply catching the Exception during creation is not a solution for me, because the real world application does the PathTooLong check and the actual createion this on different places where a lot of other path related stuff is happening in between. So it would simply be to late to check that.
Edit: I checked that Path.GetFullPath() does not modify the path, but left that out in my example for brevity.
Here is my example code:
using System;
using System.IO;
namespace PathTooLongExperiments
{
class Program
{
static void Main(string[] args)
{
string pathThatFailsCreate = #"C:\Users\qs\1234567\vkT7eYDrFL0lZzEVBwx3O-8GE632bW64IvUiCqjOHv00661Kh,lVminnGrM4Y82EKD6\qozVNx8NoSDOhGoTV1f4syjtciBfv0fLCN7iSaRBuiHtIfgHNGJDbKQ28G4uqIumKa-\DtfhThPUI7J4hGxkPUem11PZBofq1uqn-7xw9YjBODLRouNCKo7T7-ODTc,Qjed01R0\8GfPtnmuUANti7sN55aq27cW";
TryCreateFolder(pathThatFailsCreate);
string pathThatWorks = #"C:\Users\qs\1234567\vkT7eYDrFL0lZzEVBwx3O-8GE632bW64IvUiCqjOHv00661Kh,lVminnGrM4Y82EKD6\qozVNx8NoSDOhGoTV1f4syjtciBfv0fLCN7iSaRBuiHtIfgHNGJDbKQ28G4uqIumKa-\DtfhThPUI7J4hGxkPUem11PZBofq1uqn-7xw9YjBODLRouNCKo7T7-ODTc,Qjed01R0\8GfPtnmuUANti7sN55aq27c";
TryCreateFolder(pathThatWorks);
Console.WriteLine("Done. Press any key");
Console.ReadKey();
}
private static void TryCreateFolder(string path)
{
Console.WriteLine($"Attempting to create folder for path: {path}");
string checkedPath;
try
{
checkedPath = Path.GetFullPath(path);
}
catch (PathTooLongException)
{
Console.WriteLine("PathToLong check failed!");
Console.WriteLine($"Path length: {path.Length}");
Console.WriteLine($"Path: {path}");
Console.ReadKey();
return;
}
try
{
Directory.CreateDirectory(checkedPath);
}
catch (PathTooLongException)
{
// Why is this possible? We've checked for path too long by Path.GetFullPath, didn't we?
Console.WriteLine("Could not create directory because the path was to long");
}
}
}
}
This question already has answers here:
How can I save application settings in a Windows Forms application?
(14 answers)
Closed 7 years ago.
I would like to ask you if anyone of you knows how can I save changes in the progrem so when It will be restarted, the changes would remain?
for example, I have a boolean variable which his defualt value is "false".
I want after the inital start, change the value to "true" so when I'll close and start the program, the boolean variable value would be true.
That's what we have databases for...
or config files
or file systems
You need to retain data, which cant be in memory it has to be on disk
Read about various data persisting strategies try out things and let us know if you are stuck
Choose format you want to use for persisting you data. On Application starts - read file and deserialize it to your model. On Close - serialize it and save to file. Take a look at next question: Best practice to save application settings in a Windows Forms Application
Next sample was taken from the above link:
using System;
using System.IO;
using System.Web.Script.Serialization;
namespace MiscConsole
{
class Program
{
static void Main(string[] args)
{
MySettings settings = MySettings.Load();
Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
Console.WriteLine("Incrementing 'myInteger'...");
settings.myInteger++;
Console.WriteLine("Saving settings...");
settings.Save();
Console.WriteLine("Done.");
Console.ReadKey();
}
class MySettings : AppSettings<MySettings>
{
public string myString = "Hello World";
public int myInteger = 1;
}
}
public class AppSettings<T> where T : new()
{
private const string DEFAULT_FILENAME = "settings.jsn";
public void Save(string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
}
public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
}
public static T Load(string fileName = DEFAULT_FILENAME)
{
T t = new T();
if(File.Exists(fileName))
t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
return t;
}
}
}
I have the following code (sample1.evol - file attached to my unit test project):
[Test]
public void LexicalTest1()
{
var codePath = Path.GetFullPath(#"\EvolutionSamples\sample1.evol");
//.....
}
I found that the working directory of test execution is not the assembly directory: (in my case codepath variable assigned to d:\EvolutionSamples\sample1.evol).
So, how can I change the execution working directory (without hardcode)? What will be the best practice to load any files attached to test case?
You can use following to get the directory of assembly running the code something like
var AssemblyDirectory = TestContext.CurrentContext.TestDirectory
I use this for integration tests that need to access data files.
On any machine the test needs to run create a system environment variable named TestDataDirectory that points to the root of where your test data is.
Then have a static method that gets the file path for you..
public static class TestHelper
{
const string EnvironmentVariable = "TestDataDirectory";
static string testDataDir = Environment.GetEnvironmentVariable(EnvironmentVariable);
public static string GetTestFile(string partialPath)
{
return Path.Combine(testDataDir, partialPath);
}
}
...
[Test]
public void LexicalTest1()
{
var codePath = TestHelper.GetTestFile(#"\EvolutionSamples\sample1.evol");
//.....
}
I am using this code:
var str = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
if (str.StartsWith(#"file:\")){
str = str.Substring(6);
}
Getting in str variable the assembly directory.
We were having a problem where tests run using ReSharper and NCrunch would work, but the native VS Test Runner would not be able to find the files, when given just a relative file path for the test to use. I solved it by creating a function that you pass the relative test file path into, and it will give you the absolute file path.
private static string _basePath = Path.GetDirectoryName(typeof(NameOfYourTestClassGoesHere).Assembly.Location);
private string GetAbsoluteTestFilePath(string relativePath) => Path.Combine(_basePath, relativePath);
You would then use the function like so:
var input = File.ReadAllLines(GetAbsoluteTestFilePath(#"TestData/YourTestDataFile.txt"));
I am looking for an OFX file parser library in C#. I have search the web but there seems to be none. Does anyone know of any good quality C# OFX file parser. I need to process some bank statements files which are in OFX format.
Update
I have managed to find a C# library for parsing OFX parser.
Here is the link ofx sharp. This codebase seems to be the best case to startup my solution.
I tried to use the ofx sharp library, but realised it doesn't work is the file is not valid XML ... it seems to parse but has empty values ...
I made a change in the OFXDocumentParser.cs where I first fix the file to become valid XML and then let the parser continue. Not sure if you experienced the same issue?
Inside of the method:
private string SGMLToXML(string file)
I added a few lines first to take file to newfile and then let the SqmlReader process that after the following code:
string newfile = ParseHeader(file);
newfile = SGMLToXMLFixer.Fix_SONRS(newfile);
newfile = SGMLToXMLFixer.Fix_STMTTRNRS(newfile);
newfile = SGMLToXMLFixer.Fix_CCSTMTTRNRS(newfile);
//reader.InputStream = new StringReader(ParseHeader(file));
reader.InputStream = new StringReader(newfile);
SGMLToXMLFixer is new class I added into the OFXSharp library. It basically scans all the tags that open and verifies it has a closing tag too.
namespace OFXSharp
{
public static class SGMLToXMLFixer
{
public static string Fix_SONRS(string original)
{ .... }
public static string Fix_STMTTRNRS(string original)
{ .... }
public static string Fix_CCSTMTTRNRS(string original)
{ .... }
private static string Fix_Transactions(string file, string transactionTag, int lastIdx, out int lastIdx_new)
{ .... }
private static string Fix_Transactions_Recursive(string file_modified, int lastIdx, out int lastIdx_new)
{ .... }
}
}
Try http://www.codeproject.com/KB/aspnet/Ofx_to_DataSet.aspx. The code uses Framework 3.5 and transforms an ofx into a dataset, this may help with what you're trying to do.