C# StreamReader Holding up the process even if I close - c#

I am currently getting these errors:
Error 18 Could not copy "obj\Debug\SoldierApp.exe" to "bin\Debug\SoldierApp.exe". Exceeded retry count of 10. Failed.
Error 19 Unable to copy file "obj\Debug\SoldierApp.exe" to "bin\Debug\SoldierApp.exe". The process cannot access the file 'bin\Debug\SoldierApp.exe' because it is being used by another process.
So I am guessing that my StreamReader is not closing appropriately somewhere. Though I know I've closed it. Here is my code.
public Form1()
{
InitializeComponent();
if(File.Exists("soldier.csv"))
{
sr = new StreamReader("soldier.csv");
string stream_line;
int intCommaIndex1 = 0;
int intCommaIndex2;
string strSubStringSect;
stream_line = sr.ReadLine();
while(stream_line != EOF) //EOF is a constant
{
intCommaIndex1 = stream_line.IndexOf(",", intCommaIndex1);
intCommaIndex2 = stream_line.IndexOf(",", intCommaIndex1);
strSubStringSect = stream_line.Substring(intCommaIndex1, intCommaIndex2);
richTextBox1.Text = richTextBox1.Text + " " + strSubStringSect;
intCommaIndex1 = intCommaIndex2;
stream_line = sr.ReadLine();
}
sr.Close(); //closed here!
}
}
Fairly new to C# File IO and to me it looks as though everything is right? Any suggestions as to why I am getting these errors?

I have faced this issue a quite few times that it says "The process cannot access the file 'bin\Debug\Some.exe' because it is being used by another process."
Normally you should Clean the Solution and Rebuild it again and it would fix the issue.
Build -> Clean Solution
Then
Build -> Rebuild Solution
If this does not fix the issue. Than You should manually delete the Soldier.exe from bin folder and build the Solution again

First of all, I would suggest to use using. This way the file will be closed (and the reader disposed) even if you end up getting an exception.
Other thing is, ReadLine returns null if it reached the end of the stream.
But there is a useful property in StreamReader: EndOfStream. Which is true when you reached the end of the stream.
So I would suggest a code like this:
public Form1()
{
if(File.Exists("soldier.csv"))
{
InitializeComponent();
using (StreamReader sr = new StreamReader("soldier.csv"))
{
string stream_line;
int intCommaIndex1 = 0;
int intCommaIndex2;
string strSubStringSect;
while (!sr.EndOfStream)
{
stream_line = sr.ReadLine();
intCommaIndex1 = stream_line.IndexOf(",", intCommaIndex1);
intCommaIndex2 = stream_line.IndexOf(",", intCommaIndex1);
strSubStringSect = stream_line.Substring(intCommaIndex1, intCommaIndex2);
richTextBox1.Text = richTextBox1.Text + " " + strSubStringSect;
intCommaIndex1 = intCommaIndex2;
}
}
}
}

This happens a lot when debugging on Visual Studio because of the "hosting process". Is vshost.exe on your running processes?
Close Visual Studio, re-open your solution and then try the following:
On the Project menu, click Properties.
Click the Debug tab.
Clear the Enable the Visual Studio hosting process check box.
This might solve your problem if vshost.exe is still holding on to your file.

Related

Accessing file with streamreader failed because it is being used by another process

I have a .NET Core application which is multithreaded. One aspect of the application is a health check which parses a log file for errors. This is the code used to access it:
using StreamReader reader = new StreamReader(GetLogFile);
I noticed that I occasionally get this error:
2021-01-12 11:15:14.890Z ERROR APP=2227 COMP=3789 [16] Health check Check logs for application issues threw an unhandled exception after 96.2407ms - Logger=Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService,Level=ERROR,ThreadId=16,,Exception="System.IO.IOException: The process cannot access the file 'c:\apps\Cb.Publisher\Logs\Cb.Publisher.log' because it is being used by another process.
I changed my code to this:
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
In testing it I haven't encountered the issue but it occurred so rarely that I am not 100% sure it's resolved. Is my change likely to resolve this issue or is there a better way to do it?
Additional Info
This is the entire function:
private int LogLine(Regex reg)
{
GetLogFile = DefaultLogFile.GetLogFileName();
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
string line;
int lineNo = 0;
int errorLine = 0;
while ((line = reader.ReadLine()) != null)
{
Match match = reg.Match(line);
if (match.Success)
{
errorLine = lineNumber;
}
lineNo++;
}
return errorLine;
}
If I set a breakpoint on the while line in Visual Studio and run the function, then try to edit the file in Notepad I fails with the error The process cannot access the file because it is being used by another process.
After some investigation I'm wondering if this line could actually be the cause of my problems:
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("file-target");
It's in DefaultLogFile.GetLogFileName:
public string GetLogFileName()
{
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("file-target");
var logEventInfo = new LogEventInfo();
string fileName = fileTarget.FileName.Render(logEventInfo);
if (!File.Exists(fileName))
{
throw new Exception("Log file does not exist.");
}
return fileName;
}
You currently suggested solution will likely be enough, yes:
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
However, the proper solution is for you to check that the file is not being locked from wherever else you are using the file. This also means your logging framework (be it Log4Net, NLog, Serilog etc.) should be properly configured to not take an exclusive lock on the log file. I believe logging frameworks usually do not lock it from read access by default, so unless you have customized the configuration the logging framework should not be a problem.

Capturing output from powershell script

I have been working on converting a GUI script from another language to C# in VS2017 for a customer. With help from the folks here I am 95% of the way there, but have run into a couple of snags; just not sure I am doing things in the best way. I'm including just the relevant portions of code below, please let me know if I am not providing enough:
The majority of the code is centered on the wpf form, which collects data for low level technicians to batch deploy a number of Virtual Machines into the VMware environment. This number could easily range into the dozens or even a hundred VMs at once. The information for each VM is specified in the form, then collected in a listview. Once the listview is fully populated it is exported to a csv. Up to this point everything works just fine.
I've next been working on actually launching the powershell/powerCLI script (also working) and capturing output. The log file is opened with a specific reader application the customer uses, which updates in real time, and the captured output is fed to the log. It is important for the technicians to see the output from the code line by line so they can react if there is an issue.
I started with something like this as a test:
string sPSScript = "C:\\Users\\Admin\\Desktop\\TestC#.ps1";
string logFile = "C:\\Users\\Admin\\Desktop\\My.log";
string logReader = "C:\\Users\\Admin\\Documents\\CMTrace.exe";
string standard_output;
System.Diagnostics.Process PSScript = new System.Diagnostics.Process();
PSScript.StartInfo.FileName =
Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) +
"\\WindowsPowerShell\\v1.0\\powershell.exe";
PSScript.StartInfo.Arguments = "-command . '" + sPSScript + "' " +
vCenter.Text;
PSScript.StartInfo.RedirectStandardOutput = true;
PSScript.StartInfo.UseShellExecute = false;
PSScript.Start();
System.Diagnostics.Process LogFile = new System.Diagnostics.Process();
LogFile.StartInfo.FileName = logReader;
LogFile.StartInfo.Arguments = logFile;
LogFile.Start(); while ((standard_output =
PSScript.StandardOutput.ReadLine()) != null)
{
if (standard_output != "")
{
using (StreamWriter file = new StreamWriter(logFile, append: true))
{
file.WriteLine(standard_output);
}
}
}
While this writes to the log file in real time as expected, it creates 100 instances of the logReader application. I understand why, since I am declaring a new StreamWriter object through every pass, but am unsure how better to go about this.
I tried creating the file outside the loop, like this:
StreamWriter file = new StreamWriter(logFile, append: true) { };
System.Diagnostics.Process LogFile = new System.Diagnostics.Process();
LogFile.StartInfo.FileName = logReader;
LogFile.StartInfo.Arguments = logFile;
System.Diagnostics.Process PSScript = new System.Diagnostics.Process();
PSScript.StartInfo.FileName = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) + "\\WindowsPowerShell\\v1.0\\powershell.exe";
PSScript.StartInfo.Arguments = "-command . '" + sPSScript + "' " + vCenter.Text;
PSScript.StartInfo.RedirectStandardOutput = true;
PSScript.StartInfo.UseShellExecute = false;
LogFile.Start();
PSScript.Start();
System.Threading.Thread.Sleep(1500);
while ((standard_output = PSScript.StandardOutput.ReadLine()) != null)
{
if (standard_output != "")
{
file.WriteLine(standard_output);
}
}
It doesn't create multiple instances, but it also does not update the log file in real time as the previous code does. It only updates once the script runs, and then only partially. The script produces ~1000 lines of output, and I consistently see only about 840 written to the log file.
I thought about doing something like this:
FileStream logFS;
logFS = new FileStream(logFile, FileMode.Append);
but it appears the only options available to me to write to the file are expecting a byte array.
I am sure that I am missing something stupid simple in this, but would appreciate any suggestions on the easiest way to create the log file, open it in the reader, and then update it with the standard output from the powershell script.
why did the previous code writes in real time?
because you are wrapping it with using. And at the end of using block its gonna call dispose which calls .Flush to write to disk
Your second code block calls WriteLine but never called Flush so it writes to the disk whenever the buffer is full. Just add a .Flush call after WriteLine and you will have real time logging

VisualStudio Express 2012: StreamReader gives [System.UnauthorizedAccessException] error

I have read a lot of answers on this issue, but none of them helps for me.
Now, it's been 5 years that I had C# and apperantly I've forgotten it all. But I like to get into the language again to use it for automation. So, here is the bit of code I already have:
{
string path = #"C:\Users\decraiec\Documents\Client Automated";
//In this folder I will find all my XML files that I just want to load in a textbox
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//create a way to read and write the files
//go get the files from my harddrive
StreamReader FileReader = new StreamReader(path);
//make something readable for what you have fetched
StreamWriter FileWriter = new StreamWriter(textBox1.ToString());
int c = 0;
while (c == FileReader.Read())
{
string load = FileReader.ReadToEnd();//read every xmlfile up to the end
string stream = FileWriter.ToString();//make something readable
}
try
{
textBox1.Text = FileWriter.ToString();//what you have made readable, show it in the textbox
FileWriter.Close();
}
finally
{
if (FileReader != null)
{ FileReader.Close(); }
}
if (FileWriter != null)
{ FileWriter.Close(); }
}
}
If I run this code like this I'll get:
An unhandled exception of type 'System.UnauthorizedAccessException' occurred in mscorlib.dll
Additional information: Access to the path 'C:\Users\decraiec\Documents\Atrias Automated' is denied.
While I was hoping to see all the XML files in the textbox listed and clickable ( - although I need to insert the clickable code yet )
I've been looking in my folder and subfolder and files and I do have admin rights on everything. About the [ mscorlib.dll ] I have no clue where to find this.
Now if I wrap the StreamReader in a use ( var....;) VS doesn't recognizes it (red lines under the words) saying that I'm missing an instance of an object or something else of issue (just trying to glue the things together).
Could someone try to get me in the right direction please?
I think your path is a directory, not a file. Almost the exact same issue was addressed here: Question: Using Windows 7, Unauthorized Access Exception when running my application
What you can do is create a DirectoryInfo object on the path and then call GetFiles on it. For example:
DirectoryInfo di = new DirectoryInfo(directoryPath);
Foreach(var file in di.GetFiles())
{
string pathToUseWithStreamReader = file.FullName;
}
You need to use Directory.GetFiles to get any files residing in your "Client Automated" folder, then loop through them and load every single file into the stream.
var files = Directory.GetFiles(path);
foreach (var file in files)
{
var content = File.ReadAllText(file);
}
You can read more on it here:
https://msdn.microsoft.com/en-us/library/07wt70x2(v=vs.110).aspx
Also - in general, when working with files or directories like this, it's a good idea to programmatically check if they exist before working with them. You can do it like so:
if (Directory.Exists(path))
{
...
}
Or with files:
if (File.Exists(path))
{
...
}

Tfs Check-in using PendAdd: The array must contain at least one element

So I'm having a problem with automating my code to check-in files to TFS, and it's been driving me up the wall! Here is my code:
string location = AppDomain.CurrentDomain.BaseDirectory;
TfsTeamProjectCollection baseUserTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection);
IIdentityManagementService ims = baseUserTpcConnection.GetService<IIdentityManagementService>();
TeamFoundationIdentity identity = ims.ReadIdentity(IdentitySearchFactor.AccountName, #"PROD1\JR", MembershipQuery.None, ReadIdentityOptions.None);
TfsTeamProjectCollection impersonatedTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection, identity.Descriptor);
VersionControlServer sourceControl = impersonatedTpcConnection.GetService<VersionControlServer>();
Workspace workspace = sourceControl.CreateWorkspace("MyTempWorkspace", sourceControl.AuthorizedUser);
String topDir = null;
try
{
Directory.CreateDirectory(location + "TFS");
String localDir = location + "TFS";
workspace.Map("$/Automation/", localDir);
workspace.Get();
destinationFile = Path.Combine(localDir, Name + ".xml");
string SeconddestinationFile = Path.Combine(localDir, Name + ".ial");
bool check = sourceControl.ServerItemExists(destinationFile, ItemType.Any);
PendingChange[] pendingChanges;
File.Move(sourceFile, destinationFile);
File.Copy(destinationFile, sourceFile, true);
File.Move(SecondsourceFile, SeconddestinationFile);
File.Copy(SeconddestinationFile, SecondsourceFile, true);
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
else
{
workspace.PendEdit(destinationFile);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
and the problem is that whenever it's NEW files (PendEdit works correctly when the files already exist in TFS) that my code is attempting to check in, and it runs through this code:
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
The files, instead of being in the included changes in pending changes, are instead in the excluded changes like so:
and when the line that actually does the check-in runs, I'll get a "The array must contain at least one element" error, and the only way to fix it is to manually add those detected changes, and promote them to included changes, and I simply can't for the life of me figure out how to do that programatically though C#. If anyone has any guidance on what direction I should take for this, I would really appreciate it! Thank you!
Edit: I've also discovered another way to solve this by reconciling the folder, which also promotes the detected changes, but again the problem is I can't seem to figure out how to program that to do it automatically.
I know that running the visual studio developer command prompt, redirecting to the folder that this mapping is in, and the running "tf reconcile /promote" is one way, but I can only automate that as far as the /promote part, because that brings up a toolbox that a user would have to input into, which defeats the purpose of the automation. I'm at a loss.
Next Edit in response to TToni:
Next Edit in response to TToni:
I'm not entirely sure if I did this CreateWorkspaceParameters correctly (see picture 1), but this time it gave the same error, but the files were not even in the excluded portions. They just didn't show up anywhere in the pending changes (see picture 2).
Check this blog:
The workspace has a method GetPendingChangesWithCandidates, which actually gets all the “Excluded” changes. Code snippet is as below:
private void PendChangesAndCheckIn(string pathToWorkspace)
{
//Get Version Control Server object
VersionControlServer vs = collection.GetService(typeof
(VersionControlServer)) as VersionControlServer;
Workspace ws = vs.TryGetWorkspace(pathToWorkspace);
//Do Delete and Copy Actions to local path
//Create a item spec from the server Path
PendingChange[] candidateChanges = null;
string serverPath = ws.GetServerItemForLocalItem(pathToWorkspace);
List<ItemSpec> its = new List<ItemSpec>();
its.Add(new ItemSpec(serverPath, RecursionType.Full));
//get all candidate changes and promote them to included changes
ws.GetPendingChangesWithCandidates(its.ToArray(), true,
out candidateChanges);
foreach (var change in candidateChanges)
{
if (change.IsAdd)
{
ws.PendAdd(change.LocalItem);
}
else if (change.IsDelete)
{
ws.PendDelete(change.LocalItem);
}
}
//Check In all pending changes
ws.CheckIn(ws.GetPendingChanges(), "This is a comment");
}

C# TargetInvocationException and FormatException

Ok, I have a really weird situation happening here. First I need to give some background. I'm creating AI agents for a game that was made on the XNA engine. The way things are set up, people are supposed to use the agent's framework to generate a .dll that the game then uses to load the agents when it runs.
I have access to the code of the game (so I can see what's happening) and at this point I'm using someone else's agents as a starting point for my own. Recently, there were a few changes to the game (and consequentially, the framework), mostly in names of classes and interfaces which means I have to bring the agents up to speed. So, after I made all the updates necessary to be able to compile the agents with the new version of the framework, I came up with a problem. This is the code for the game loading the .dll
// dynamically load assembly from file GeometryFriendsAgents.dll
Assembly agentsDLL = Assembly.LoadFile(path);
// get type of classes BallAgent and SquareAgent from just loaded Assembly
Type circleType = AgentsDLL.GetType("GeometryFriendsAgents.CircleAgent");
Type rectangleType = AgentsDLL.GetType("GeometryFriendsAgents.RectangleAgent");
try {
// create instances of classes BallAgent and SquareAgent
npcCircle = (ICircleAgent)Activator.CreateInstance(circleType);
npcRectangle = (IRectangleAgent)Activator.CreateInstance(rectangleType);
}catch(TargetInvocationException e){
throw e.InnerException;
}
I can confirm that the path is correct. The lines inside the try/catch will throw a TargetInvocationException when I try to run the game (Which will automatically load the agents). I added the try/catch to see the inner exception, which is a FormatException, and VisualStudio gives the aditional information that the input string was not in the correct format.
I don't know what part of the agents code would be relevant for this, but I have yet to get to the weird part. In the implementation I'm using, the agents make use of a LearningCenter class. This class essentially reads and writes the learning files of the agents. at the start of the class it stores the path for the learning files:
protected const string path = #"..\..\..\..\Agents\";
So here's where things get weird. This is the correct path for the learning files. When earlier I made a mistake, I had this path (which before was repeated many times throughout the code) as
protected const string path = #"..\..\..\..\Agents";
When I build the .dll with the incorrect path, I can sucessfully load the agents and it will run the game. The problem then is that the path is incorrect, and when the LearningCenter tries to write the learning file, it will evidently fail with a DirectoryNotFoundException. The method in question is:
public void EndGame(float knownStatesRatio) {
if (_toSave) {
FileStream fileStream = new FileStream(path + _learningFolder + "\\Ratios.csv", FileMode.Append);
StreamWriter sw = new StreamWriter(fileStream);
sw.WriteLine(knownStatesRatio);
sw.Close();
fileStream.Close();
fileStream = new FileStream(path + _learningFolder + "\\IntraPlatformLearning.csv", FileMode.Create);
DumpLearning(fileStream, _intraplatformPlayedStates);
fileStream.Close();
if (interPlatform) {
fileStream = new FileStream(path + _learningFolder + "\\InterPlatformLearning.csv", FileMode.Create);
DumpLearning(fileStream, _interplatformPlayedStates);
fileStream.Close();
}
}
}
The exception occurs immediatly when creating the new filestream. I've tried shifting the missing \ to the _learningFolder variable, but when I do it goes back to the first problem. So long as the path is incorrect, I can run the game...
I should also mention that before this I initially encountered another TargetInvocationException at the same location. At the time the problem was fixed by changing the visibility of the agent classes to public.
I realize that the thing with the path is probably hiding the actual problem, but I just don't know where to look next.
edit: Here's the stack trace for the first problem
GeometryFriends.exe!GeometryFriends.AI.AgentsManager.LoadAgents() Line 396
GeometryFriends.exe!GeometryFriends.Levels.SinglePlayerLevel.LoadLevelContent() Line 78
GeometryFriends.exe!GeometryFriends.Levels.Level.LoadContent() Line 262
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.LoadContent() Line 253
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.DrawableGameComponent.Initialize()
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.Initialize() Line 221
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Initialize()
GeometryFriends.exe!GeometryFriends.Engine.Initialize() Line 203
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.RunGame(bool useBlockingRun)
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Run()
GeometryFriends.exe!GeometryFriends.Program.Main(string[] args) Line 16
The agent that's failing first is the CircleAgent, here's the constructor:
public CircleAgent() {
//Change flag if agent is not to be used
SetImplementedAgent(true);
lastMoveTime = DateTime.Now;
lastRefreshTime = DateTime.Now;
currentAction = 0;
rnd = new Random(DateTime.Now.Millisecond);
model = new CircleWorldModel(this);
learningCenter = new CircleLearningCenter(model);
learningCenter.InitializeLearning();
startTime = DateTime.Now;
}
edit 2: Ok, I managed to zone in on the source of the FormatException.
The error occurs in this method of the CircleLearningCenter (the statement in the first if):
public override void addStateMovementValue(string[] lineSplit, string stateId, ref Dictionary<string, Dictionary<int, double>> lessons) {
if (!lineSplit[1].Equals("0")) {
lessons[stateId].Add(Moves.ROLL_LEFT, double.Parse(lineSplit[1]));
}
if (!lineSplit[2].Equals("0")) {
lessons[stateId].Add(Moves.ROLL_RIGHT, double.Parse(lineSplit[2]));
}
if (!lineSplit[3].Equals("0")) {
lessons[stateId].Add(Moves.JUMP, double.Parse(lineSplit[3]));
}
}
Which is called by this method in the LearningCenter:
private void createLearningFromFile(FileStream fileStream, ref Dictionary<string, Dictionary<int, double>> lessons) {
lessons = new Dictionary<string, Dictionary<int, double>>();
StreamReader sr = new StreamReader(fileStream);
string line;
while ((line = sr.ReadLine()) != null) {
string[] lineSplit = line.Split(',');
string stateId = lineSplit[0];
lessons.Add(stateId, new Dictionary<int, double>());
addStateMovementValue(lineSplit, stateId, ref lessons);
}
}
which in turn is called by this method (which it's called in the constructor of the circle):
public void InitializeLearning() {
if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "IntraPlatformLearning.csv"))) {
FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder),"IntraPlatformLearning.csv"), FileMode.Open);
createLearningFromFile(fileStream, ref _intraplatformLessonsLearnt);
fileStream.Close();
} else {
createEmptyLearning(ref _intraplatformLessonsLearnt);
}
if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"))) {
FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"), FileMode.Open);
createLearningFromFile(fileStream, ref _interplatformLessonsLearnt);
fileStream.Close();
} else {
createEmptyLearning(ref _interplatformLessonsLearnt);
}
}
In case it's not apparent, CircleLearningCenter is a subclass of LearningCenter. Also, sorry for the text wall, but I'm at my wits end.
Use System.IO.Path.Combine() to con-cat path parts. For example:
instead of :
FileStream(path + _learningFolder + "\\Ratios.csv")
use :
FileStream(Path.Combine(Path.Combine(path , _learningFolder) , "Ratios.csv"))
Just don't forget to remove \\ from each part.
And do the same for other FileStream paths.

Categories