File Still Locked Despite putting StreamReader in Using - c#

My program goes through all files in a folder, reads them, and without altering their information moves them to another location under a different name. However I cannot use the File.Move method because I get the following IOException:
The process cannot access the file because it is being used by another
process.
This is how I am reading the file and adding all its lines to a List<string>:
List<string> lines = null;
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs, Encoding.Default))
{
lines = new List<string>();
while (!sr.EndOfStream)
lines.Add(sr.ReadLine());
}
And this is the function with which I move the file:
public static bool ArchiveFile(string filePath, string archiveFolderLocation)
{
if (!Directory.Exists(archiveFolderLocation))
Directory.CreateDirectory(archiveFolderLocation);
try
{
string timestamp = string.Format("{0:yyyy-MM-dd HHmmss}", DateTime.Now);
string newFileName = Path.GetFileNameWithoutExtension(filePath) + " " + timestamp;
string destination = string.Format("{0}\\{1}{2}", archiveFolderLocation, newFileName, Path.GetExtension(filePath));
File.Move(filePath, destination);
return true;
}
catch (Exception ex)
{
return false;
}
}
I thought using the using statement is supposed to garbage-collect and release the file after being used. How can I release the file so I can move it and why my file stays locked?
Solved:
Got it. Somewhere between these two calls I was opening a TextReaderobject without disposing it.

I thought using the using statement is supposed to garbage-collect and
release the file after being used. How can I release the file so I can
move it and why my file stays locked?
Not really. Using statement is nothing but :
try { var resource = new SomeResource(); }
finally { resource.Dispose(); // which is not GC.Collect(); }
It works fine so it looks like your file is opened from some other place in your code...
P.S.
By the way you can just do:
List<string> lines = File.ReadAllLines().ToList();

You could use:
string dpath = "D:\\Destination\\";
string spath = "D:\\Source";
string[] flist = Directory.GetFiles(spath);
foreach (string item in flist)
{
File.Move(item, dpath + new FileInfo(item).Name);
}
Replace D:\\Source & D:\\Destination\\ with the required source and destination paths, respectively.

Related

Unable to move a file once I have read and appended data to a new file c#

I have a foreach loop that is appending data from 1 file (source) to another (destination).
Once the file has been appended with all data I want to move the original source file. When debugging im getting the error "Exception thrown: 'System.IO.IOException' in System.IO.FileSystem.dll"
Im assuming this is because the file is locked. How can I dispose this so I can move the file.
var stringwrite = new[] { prefix, prefix2 };
foreach (string line in File.ReadLines(currentFile))
{
var lastOne = line.Split(';').Last();
if (!stringwrite.Any(stringwrite => lastOne.Contains(stringwrite)))
continue;
//write lines found to new file
File.AppendAllText(todestination, line + Environment.NewLine);
}
//move original file to new directory
try
{
File.Move(currentFile, completeddestination);
break;
}
catch (Exception ex)
{
MessageBox.Show("Issue Moving File");
}
As you are reading the source file to the end and writing to the destination depending on a condition, it would make sense to keep both input and output streams open until the end of read/write operations. Note, that File.ReadLines will open the source file, read the contents, and then close it. Also, File.AppendAllText will open the destination file, append the contents, and then close the file. Such an approach is inefficient. I think, your task could be properly implemented using file streams. Please find the example below:
static void Main()
{
var sourcePath = "C:\\PathToSource";
var destinationPath = "C:\\PathToDestination";
var completedDestinationPath = "C:\\PathToCompletedDestination";
var prefixes = new[] { "some-prefix", "some-other-prefix" };
foreach (var source in EnumerateDataFiles(sourcePath))
{
// This assumes that files in source and destination and completed
// dectination directories have the same file name but different paths.
// If you use another convention for your data, please adjust it here
var destination = GetDestinationFilePath(source, destinationPath);
var completedDestination = GetDestinationFilePath(source, completedDestinationPath);
try
{
AppendData(
source,
destination,
line =>
{
var lastEntry = line.Split(';').Last();
return prefixes.Any(prefix => lastEntry.Contains(prefix));
});
File.Move(source, completedDestination);
}
catch (Exception ex)
{
Console.WriteLine($"Issue Moving File: {ex.Message}");
}
}
}
static IEnumerable<string> EnumerateDataFiles(string path)
{
// Change *.dat to the extension (e.g., *.txt) you use for your data files,
// or to *.* to include all files from the directory
return Directory.EnumerateFiles(path, "*.dat", SearchOption.AllDirectories);
}
static string GetDestinationFilePath(string sourceFileName, string destinationPath)
{
// This will return a file path to the file with the same name as the source
// but located in the destination directory
return Path.Combine(destinationPath, Path.GetFileName(sourceFileName));
}
static void AppendData(string source, string destination, Func<string, bool> linePredicate)
{
using (var inputStream = new FileStream(source, FileMode.Open, FileAccess.Read))
using (var inputReader = new StreamReader(inputStream))
using (var outputStream = new FileStream(destination, FileMode.OpenOrCreate, FileAccess.Write))
using (var outputWriter = new StreamWriter(outputStream))
{
while (inputReader.ReadLine() is string inputLine)
{
if (!linePredicate(inputLine))
continue;
outputWriter.WriteLine(inputLine);
}
}
}
In the example provided, I assumed that you have the same file name but different paths for source, destination, and completed destination file paths. If you use a different naming mechanism, please follow comments to adjust it accordingly.

Can't rename folder after creation a new file inside [duplicate]

This is what I did to create and write on my file:
Create_Directory = #"" + path;
Create_Name = file_name;
private void Create_File(string Create_Directory, string Create_Name )
{
string pathString = Create_Directory;
if (!System.IO.Directory.Exists(pathString)) { System.IO.Directory.CreateDirectory(pathString); }
string fileName = Create_Name + ".txt";
pathString = System.IO.Path.Combine(pathString, fileName);
if (!System.IO.File.Exists(pathString)) { System.IO.File.Create(pathString); }
///ERROR BE HERE:
System.IO.StreamWriter file = new System.IO.StreamWriter(pathString);
file.WriteLine(Some_Method(MP.Mwidth, MP.Mheight, MP.Mtype, "" ));
file.Close();
}
The problem here, which I have battled the entire day, is writing the file after I create it. So, my program creates a file just fine, then gives out an error before writing:
"An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll"
"Additional information: The process cannot access the file 'D:\Projects\Project 15\Project 15\world\world maps\A.txt' because it is being used by another process."
Funny thing though, when I run the program again and try to create an already existing file, as you can see, it skips file creating, goes to writing and works fine, and I would really want my program to create the file and write without having to rerun it... What am I not seeing here?? :S
The problem is that File.Create returns an opened Stream, and you're never closing it. The file is "in use" (by you) at the point in time when you create your StreamWriter.
That being said, you don't need to "create" the file. StreamWriter will automatically do it for you. Just remove this line:
if (!System.IO.File.Exists(pathString)) { System.IO.File.Create(pathString); }
And everything should work as written.
Note that I would rewrite this slightly, however, to make it safer:
private void Create_File(string directory, string filenameWithoutExtension )
{
// You can just call it - it won't matter if it exists
System.IO.Directory.CreateDirectory(directory);
string fileName = filenameWithoutExtension + ".txt";
string pathString = System.IO.Path.Combine(directory, fileName);
using(System.IO.StreamWriter file = new System.IO.StreamWriter(pathString))
{
file.WriteLine(Some_Method(MP.Mwidth, MP.Mheight, MP.Mtype, "" )); 
}
}
You can also just use File.WriteAllText or similar methods to avoid creating the file this way. Using the using block guarantees the file will be closed, even if Some_Method raises an exception.
You can use the File class as it wraps up a lot of the work for you
Example:
private void Create_File(string Create_Directory, string Create_Name)
{
string pathString = Create_Directory;
if (!System.IO.Directory.Exists(pathString)) { System.IO.Directory.CreateDirectory(pathString); }
pathString = System.IO.Path.Combine(pathString, Create_Name + ".txt");
File.WriteAllText(fileName, Some_Method(MP.Mwidth, MP.Mheight, MP.Mtype, ""));
}
static void Main(string[] args)
{
FileStream fs = new FileStream("D:\\niit\\deep.docx", FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
sr.BaseStream.Seek(0, SeekOrigin.Begin);
string str = sr.ReadLine();
Console.WriteLine(str);
Console.ReadLine();
}

How to start process A after completing process B in c#?

I have written a windows service to move a file from one location to another and,
then read the file in the new location and write the data to the database.
In the middle of the execution there is an error message:
IOException: The process cannot access the file 'file path' because it is being used by another process.
My code:
File move class
public void mysql()
{
string fileName = "Bargstedt.csv";
string sourcePath = #"\\192.168.1.2\Data\Company Files\";
string targetPath = #"C:\Users\source";
string sourceFile = System.IO.Path.Combine(sourcePath, fileName);
string destFile = System.IO.Path.Combine(targetPath, fileName);
if (!System.IO.Directory.Exists(targetPath))
{
System.IO.Directory.CreateDirectory(targetPath);
}
System.IO.File.Copy(sourceFile, destFile, true);
if (System.IO.Directory.Exists(sourcePath))
{
string[] files = System.IO.Directory.GetFiles(sourcePath);
fileName = System.IO.Path.GetFileName(fileName);
destFile = System.IO.Path.Combine(targetPath, fileName);
FileInfo info = new FileInfo(destFile);
bool exists = info.Exists;
if (exists == true)
{
try
{
int delay = 400;
File.Delete(#"C:\Users\Isuruh\source\Bargstedt.csv");
Thread.Sleep(delay);
System.IO.File.Copy(sourceFile, destFile, true);
Debug.WriteLine("file moved");
}
catch (IOException ex) { }
}
}
else
{
Console.WriteLine("Source path does not exist!");
}
Console.WriteLine("Press any key to exit.");
}
Insert Class
public void Insert()
{
if (this.OpenConnection() == true)
{
using(var reader = new StreamReader(#"C:\Users\source\Bargstedt.csv"))
//using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
List<string> listA = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
string querynew = "INSERT INTO new_jobs"
+ "(board_code,status,code,no1,no2,thickness,dimension,material,root,variable,number,stable,constant)"
+ "VALUES (?jobNo, ?strClientName, ?strClientReference, ?strJobCategory, ?datCommisioned, ?datPromisedDelivery, ?division, ?date_assigned, ?root, ?variable, ?number, ?stable, ?constant)";
I did not mention the whole code for the service since it's too lengthy.
As given in Solution 01 I tried to use,
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
line instead of
using(var reader = new StreamReader(#"C:\Users\source\Bargstedt.csv"))
But that triggers errors in the below keywords I have used.
I am not sure is it possible to use a file open instead of a reader class?
Basically, what I need to do is start the Insert process after completing the file move process.
Any suggestions on how to do that?
PS: This is possibly a duplicate question of the question I have mentioned above. I tried my very best to make this question unique. Appreciate if not marked as duplicate. Thank you!

Data from newly created .text file is not readable to third party application

I have developed a windows application, which will read updated data from .jrn files(In an ATM Machine) and will be copy the text to a temporary text file "tempfile.txt".
There is another third party application called "POS Text Sender", which reads "tempfile.txt" and display the contents of it in a CCTV Camera.
The problem is that if I type directly something in the tempfile, the POS application will read it, but if my application writes text to "tempfile", I can see the same content as in the .jrn file in tempfile, but it is not reflected in the POS application when ever data is copied from newly generated file to tempfile.if restart the POS Text Sender after the first data copied to tempfile from newly generated file,POS Text sender will display the content til content from newly created file is written to tempfile
My application code is reading .jrn file using StreamReader and assigning it to a string variable and then writing it to a tempfile using StreamWriter. What is the difference between manually typing text on a file and .NET StreamWriter writing text to a file?
CODE:
DateTime LastChecked = DateTime.Now;
try
{
string[] files = System.IO.Directory.GetFiles(#"C:\Test", "*.jrn", System.IO.SearchOption.AllDirectories);
foreach (string file in files)
{
if (!fileList.Contains(file))
{
currentfilename = file;
fileList.Add(file);
copywarehouse(file);
//do_some_processing();
try
{
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(file))
{
currentcontent=sr.ReadToEnd();
// Read and display lines from the file until the end of
//// the file is reached.
//while ((currentcontent = sr.ReadLine()) != null)
//{
//}
sr.Close();
//sr.Dispose();
}
}
catch (Exception)
{
// Let the user know what went wrong.
}
}
}
//checking
try
{
using (StreamReader sr = new StreamReader(currentfilename))
{
string currentfilecontent = sr.ReadToEnd();
sr.Close();
//sr.Dispose();
if (currentfilecontent!=currentcontent)
{
if (currentfilecontent.Contains(currentcontent))
{
string originalcontent = currentfilecontent.Substring(currentcontent.Length);
System.IO.StreamWriter filenew = new System.IO.StreamWriter(#"C:\Test\tempfile.txt");
filenew.WriteLine(originalcontent);
filenew.Close();
currentcontent = currentfilecontent;
}
}
}
}
catch (Exception)
{
// Let the user know what went wrong.
}
copywarehouse method:
private void copywarehouse(string filename)
{
string sourcePath = #"C:\Test";
string targetPath = #"C:\Test";
try
{
string sourceFile = System.IO.Path.Combine(sourcePath, filename);
string destFile = System.IO.Path.Combine(targetPath, "tempfile.txt");
System.IO.File.Copy(sourceFile, destFile, true);
}
catch (Exception)
{
}
}
Can you check the following:
Is the generated file encoding same as the manually created file? (i.e. UTF-8/ANSI).
Are your constantly flushing the streamWriter's buffer? Or set the StreamWriter's AutoFlush property to true.
Is the StreamWriter opened with a WriteLock with no read allowed? In this case the other application may not be able to open your tempfile for read access.
EDIT:
Also, in the code you posted, you are comparing the tempFile data to current data, and if tempFile data is newer than current data, you are appending the temp file, which I think should be vice versa.
Main change:
using (StreamWriter filenew = new StreamWriter(fileDetail.TempFileName, true, Encoding.ASCII))
{
filenew.WriteLine(newContent);
}
To know the correct encoding, just create a new tempFile, write something in the editor and save it. Open the file in notepad and do a "save as". This will display the current encoding in the bottom. Set that encoding in .NET code.
If this does not work try (As recommended by shr):
using (StreamWriter filenew = new StreamWriter(fileDetail.TempFileName, true, Encoding.ASCII))
{
filenew.Write(newContent + "\r\n");
}
Long Version: (It may be a bit different than your code):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DateTime LastChecked = DateTime.Now;
IDictionary<string, FileDetails> fileDetails = new Dictionary<string, FileDetails>(StringComparer.OrdinalIgnoreCase);
IList<string> tempFileList = new List<string>();
try
{
string[] files = System.IO.Directory.GetFiles(#"C:\Test", "*.jrn", System.IO.SearchOption.AllDirectories);
foreach (string file in files)
{
string currentfilename = file;
string currentcontent = string.Empty;
if (!fileDetails.Keys.Contains(file))
{
fileDetails[file] = new FileDetails(copywarehouse(file));
//do_some_processing();
}
try
{
using (StreamReader sr = new StreamReader(file))
{
currentcontent = sr.ReadToEnd();
}
}
catch (Exception)
{
// Let the user know what went wrong.
}
fileDetails[file].AddContent(currentcontent);
}
//TODO: Check using the file modified time. Avoids unnecessary reading of file.
foreach (var fileDetail in fileDetails.Values)
{
//checking
try
{
string tempFileContent = string.Empty;
string currentcontent = fileDetail.GetContent();
using (StreamReader sr = new StreamReader(fileDetail.TempFileName))
{
tempFileContent = sr.ReadToEnd();
sr.Close();
}
if (!(0 == string.Compare(tempFileContent, currentcontent)))
{
if (currentcontent.Contains(tempFileContent))
{
string newContent = tempFileContent.Substring(currentcontent.Length);
using (StreamWriter filenew = new StreamWriter(fileDetail.TempFileName, true, Encoding.ASCII))
{
filenew.WriteLine(newContent);
}
}
}
}
catch (Exception)
{
// Let the user know what went wrong.
}
}
}
catch (Exception)
{
}
}
private static string copywarehouse(string filename)
{
string sourcePath = #"C:\Test";
string targetPath = #"C:\Test";
string sourceFile = System.IO.Path.Combine(sourcePath, filename);
string destFile = System.IO.Path.Combine(targetPath, filename+ "tempfile.txt");
try
{
System.IO.File.Copy(sourceFile, destFile, true);
}
catch (Exception)
{
}
return destFile;
}
internal class FileDetails
{
public string TempFileName { get; private set; }
private StringBuilder _content;
public FileDetails(string tempFileName)
{
TempFileName = tempFileName;
_content = new StringBuilder();
}
public void AddContent(string content)
{
_content.Append(content);
}
public string GetContent()
{
return _content.ToString();
}
}
}
}
EDIT 2:
Can you change the copywarehouse to this and see it the problem persists:
private void copywarehouse(string filename)
{
const string sourcePath = #"C:\Test";
const string targetPath = #"C:\Test";
try
{
string sourceFile = Path.Combine(sourcePath, filename);
string destFile = Path.Combine(targetPath, "tempfile.txt");
string currentcontent;
using (var sr = new StreamReader(sourceFile))
{
currentcontent = sr.ReadToEnd();
}
using (var wr = new StreamWriter(destFile, false, Encoding.ASCII))
{
wr.WriteLine(currentcontent);
}
}
catch (Exception)
{
}
}
Most likely this is a CR+LF issue.
The POS expects the file to have line endings with CR+LF (Carriage Return (0x0D) + New line (0x0A)) combination.
The filenew.WriteLine(originalcontent) appends only the new line character. When you type, I think, you editor must be creating the CR+LF combination for all line endings.
I suggest you try filenew.Write( originalcontent + "\r\n");
One difference is that your application does not write to tempfile.txt directly but to another file and then copies that file to tempfile.txt.

File cannot be accessed because it is being used by another program

I am trying to remove the space at the end of line and then that line will be written in another file.
But when the program reaches to FileWriter then it gives me the following error
Process can't be accessed because it is being used by another process.
The Code is as below.
private void FrmCounter_Load(object sender, EventArgs e)
{
string[] filePaths = Directory.GetFiles(#"D:\abc", "*.txt", SearchOption.AllDirectories);
string activeDir = #"D:\dest";
System.IO.StreamWriter fw;
string result;
foreach (string file in filePaths)
{
result = Path.GetFileName(file);
System.IO.StreamReader f = new StreamReader(file);
string newFileName = result;
// Combine the new file name with the path
string newPath = System.IO.Path.Combine(activeDir, newFileName);
File.Create(newPath);
fw = new StreamWriter(newPath);
int counter = 0;
int spaceAtEnd = 0;
string line;
// Read the file and display it line by line.
while ((line = f.ReadLine()) != null)
{
if (line.EndsWith(" "))
{
spaceAtEnd++;
line = line.Substring(0, line.Length - 1);
}
fw.WriteLine(line);
fw.Flush();
counter++;
}
MessageBox.Show("File Name : " + result);
MessageBox.Show("Total Space at end : " + spaceAtEnd.ToString());
f.Close();
fw.Close();
}
}
File.Create itself returns a stream.
Use that stream to write file. Reason you are receiving this error is because Stream returned by File.Create is open and you are trying to open that file again for write.
Either close the stream returned by File.Create or better use that stream for file write or use
Stream newFile = File.Create(newPath);
fw = new StreamWriter(newFile);
Even though you solved your initial problem, if you want to write everything into a new file in the original location, you can try to read all of the data into an array and close the original StreamReader. Performance note: If your file is sufficiently large though, this option is not going to be the best for performance.
And you don't need File.Create as the StreamWriter will create a file if it doesn't exist, or overwrite it by default or if you specify the append parameter as false.
result = Path.GetFileName(file);
String[] f = File.ReadAllLines(file); // major change here...
// now f is an array containing all lines
// instead of a stream reader
using(var fw = new StreamWriter(result, false))
{
int counter = f.Length; // you aren't using counter anywhere, so I don't know if
// it is needed, but now you can just access the `Length`
// property of the array and get the length without a
// counter
int spaceAtEnd = 0;
// Read the file and display it line by line.
foreach (var item in f)
{
var line = item;
if (line.EndsWith(" "))
{
spaceAtEnd++;
line = line.Substring(0, line.Length - 1);
}
fw.WriteLine(line);
fw.Flush();
}
}
MessageBox.Show("File Name : " + result);
MessageBox.Show("Total Space at end : " + spaceAtEnd.ToString());
Also, you will not remove multiple spaces from the end of the line using this method. If you need to do that, consider replacing line = line.Substring(0, line.Length - 1); with line = line.TrimEnd(' ');
You have to close any files you are reading before you attempt to write to them in your case.
Write stream in using statement like:
using (System.IO.StreamReader f = new StreamReader(file))
{
//your code goes here
}
EDIT:
Zafar is correct, however, maybe this will clear things up.
Because File.Create returns a stream.. that stream has opened your destination file. This will make things clearer:
File.Create(newPath).Close();
Using the above line, makes it work, however, I would suggest re-writing that properly. This is just for illustrative purposes.

Categories