I am trying to save a backup of some data. If the backup exists then I want to delete it, give the most recent file the name of the backup file, and then save the new file as the most recent file.
The problem I am running into is that when I delete an existing file I still cannot save another file under the same name. The system still thinks that the file exists, and indeed the file isn't deleted at the point where the program throws the exception even though the two lines are right after each other. The only thing I can think of is that that the "delete" operation hasn't had time to complete by the time the move operation wants to execute. But I do not know how to fix it.
The code looks something like this.
File.Delete(filePath.Substring(filePath.Length - 4) + ".bak");
File.Move(filePath, filePath.Substring(0, filePath.Length - 4) + ".bak");
I thought of setting a timer in order to execute the move function later, but it seems to me that this would probably be a very sloppy and dangerous way of handling the problem. In addition, the amount of time that one needs to wait might have to vary from system to system. So I am thinking that there must be a better way.
The problem has probably to do with the fact that you use two different .Substring methods: String#Substring(int) and String#Substring(int,int). Indeed:
File.Delete(filePath.Substring(filePath.Length - 4) + ".bak");
// ^ with one int
File.Move(filePath, filePath.Substring(0, filePath.Length - 4) + ".bak");
// ^ with two ints
And unfortunately, the two are not semantically equivalent. The one with one int is the start index. So given that filePath is equal to test.txt, the file you aim to remove is .txt.bak and next you aim to move a file to test.bak. Or running this with Mono's C# emulator:
csharp> String filePath = "test.txt";
csharp> filePath.Substring(filePath.Length - 4) + ".bak"
".txt.bak"
csharp> filePath.Substring(0, filePath.Length - 4) + ".bak"
"test.bak"
Please update it with:
File.Delete(filePath.Substring(0,filePath.Length - 4) + ".bak");
// ^ added zero
File.Move(filePath, filePath.Substring(0, filePath.Length - 4) + ".bak");
Another more elegant and less error prone way is evidently to use a variable in between such that you are sure, you are talking about the same file:
String backupfile = filePath.Substring(0, filePath.Length - 4) + ".bak";
File.Delete(backupfile);
File.Move(filePath,backupfile);
Finally it's not recommended to do path processing yourself: use dedicated methods to join and manipulate file paths.
I tried your code with a little change and it worked fine. I only changed how you defined the backup file name, because the file extension can be more or less than 3 chars:
string backupPath = Path.GetFileNameWithoutExtension(filePath) + ".bak";
File.Delete(backupPath);
File.Move(filePath, backupPath);
But if it does not work for you, try to rename the backup to lets say "*.bak2" and then delete that one.
Hope that helps...
Related
This is the first question I asked on stackoverflow, very exciting. So sorry about my grammar and other type mistakes, I would appreciate if you correct them. I want to write a program that reads a csv file if it exists in the specific folder first, stores it in List variable, add some new lines and writes it to the same file. This process will be repeated continuously in a while block.
While the file is being read and written by the program, if it is opened with Notepad, it doesn't give an error and the program could access the file in parallel. But if it is opened with Office Excel, program gives error that says "file access is denied because it is used by another process..". I want to ask you that:
1) Is it possible to give priority to the program, so program can still access the file but user cannot? Or is it possible both program and user can access the file?
2) If the solution is opening the file with Notepad, is there a way to set default program for this file as Notepad? Or how can I change default program for csv files as Notepad from C#?
Reading and writing parts of the code is as bellows:
List<string> csvLines = new List<string>();
string address = folderpath + #"\PLC_LOGLARI";
if (!Directory.Exists(address))
Directory.CreateDirectory(address);
string fileAddress = address + #"\" + fileName + "_" + machineNo + "_1.csv";
if (Directory.EnumerateFiles(address).Any(f => f.Contains(fileName + "_" + machineNo)))
{
string[] addressArray = Directory.GetFiles(address, fileName + "_" + machineNo + "*.*");
FileInfo fileInformation = new FileInfo(addressArray[addressArray.Length - 1]);
long fileSize = fileInformation.Length;
if (fileSize > 5000000)
{
int fileNo = int.Parse(addressArray[addressArray.Length - 1].Substring(addressArray[addressArray.Length - 1].LastIndexOf('_') + 1, 1)) + 1;
fileAddress = addressArray[addressArray.Length - 1].Substring(0, addressArray[addressArray.Length - 1].LastIndexOf('_') + 1) + fileNo.ToString() + ".csv";
}
else
{
fileAddress = addressArray[addressArray.Length - 1];
csvLines = File.ReadAllLines(address).ToList();
}
}
else
{
string[] headings = makeHeadings();
csvLines.Add(headings[0]);
csvLines.Add(headings[1]);
}
string newLine = "";
// some operations and calculations for newLine
// some operations and calculations for newLine
// some operations and calculations for newLine
newLine = newLine.Substring(1, newLine.Length-1);
csvLines.Add(newLine);
File.WriteAllLines(fileAddress, csvLines.ToArray());
Thanks for your help.
For question 2: In order to change the default program, if you are using Windows, you could click start and find/type 'Default Programs'. Depending on the version of windows, basically find 'Associate a file type of protocol with a program' or 'choose a default application by file type' and adjust the csv file to use a new default program.
I would be very careful if both program and notepad are able accessing the file in the same time. The reason why it was blocked in the first place was to prevent overridding the content of the file, thus probably test it first if you open it in the program and notepad. Then the program make some update - and you also make a different update with the notepad. Would both changes be recorded? As I suspect the 'last save' win in which you may lost some of your changes.
For question 1: I was guessing that whoever open the file first got the lock of that file. As such, if Excel was opening the file - then the program would have that error. Vice versa, if the program had opened the file - then the Excel may have problem opening that file. I'm not sure how to give priority to the program.
Here's a GIF showing whats wrong
NOTE: It works perfectly fine with DownloadFile() it downloads under the right name and path (Even the mbox before it is correct) - But everything else after it fails to be the right name and path.
Need the .exe to test?
https://github.com/ImReallyShiny/SAIO/blob/master/Test.exe
(Scan it if needed, But it is 100% safe)
This is absolute wizardry that's going on.
Somehow when I run my "Auto-Update" code from the "Start" Button on Visual Studio, It outputs the correct file path and file name (with .exe)...
But when I open the Built .exe, It does some wizardry resulting in it slashing everything after (& Including) the "-" but keeping the .exe somehow.
The code is EXACTLY the same on BOTH .exe's the only thing I could see as the reason, Is some issue with Webclient Getting the version more than 1 time (Yet its tied to a string var so it shouldn't matter) or some kind of Glitch with my VisualStudio setup (Cache, Propogation, PC Restart Needed or something)
This is a hard question to explain, So I added some Inline Commenting to explain where the issue is and what it is.
Code:
//Using WebClient, get the Newest File Version;
using (System.Net.WebClient wc = new System.Net.WebClient())
{
//Latest Version; (This is the number after the - for the filename - Excluding .exe)
string LatestVersion = wc.DownloadString("https://raw.githubusercontent.com/ImReallyShiny/SAIO/master/version.txt");
//This is the Location itself e.g C:/Users/Shiny/Desktop/{appname}.exe
string ExecutableLocation = typeof(Program).Assembly.CodeBase.Replace("file:///", "");
//The build's File Version;
string CurrentVersion = FileVersionInfo.GetVersionInfo(ExecutableLocation).ProductVersion;
//Final Name String; As you can see it SHOULD output as: AppName-1.2.4.5.exe
string CurrentExecutableName = typeof(Program).Assembly.GetName().Name + "-" + LatestVersion + ".exe";
//If the Latest Version is Newer then the Current Version;
if (LatestVersion != CurrentVersion)
{
//Download the Latest Version of the EXE file; (Gets the name and path perfectly fine)
wc.DownloadFile("https://github.com/ImReallyShiny/SAIO/raw/master/SAIO.exe", CurrentExecutableName);
//Show a MessageBox asking to open Explorer to the file; - This should output "{Path}/AppName-1.2.4.5.exe" but it only does when opening from VS
DialogResult mb = MessageBox.Show("Continue usage on the new update. Open Explorer and go to the Directory containing the updated .exe located at: " + ExecutableLocation.Replace("SAIO.EXE", CurrentExecutableName + " ?\""), "New Update Downloaded!", MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
if (mb == DialogResult.Yes)
{
//Go to where SAIO is and select the New Update.exe; (This also fails to select the right .exe, It selects "SAIO.EXE" when it should be selecing the AppName-1.2.4.5.exe
Process.Start("explorer.exe", "/select,\"" + ExecutableLocation.Replace("/", "\\").Replace("SAIO.EXE", CurrentExecutableName) + "\"");
}
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
Get what im saying?
This is simply that the filename is (sometimes) SAIO.exe or some other case.
Seeing as Replace doesn't have an overload to allow you to specify InvariantCultureIgnoreCase (or somethingElseIgnoreCase), the best solution is to use Path.GetDirectoryName to get the full folder name you want, rather than just expecting there to be exactly one SAIO.EXE in the path.
E.g. your last line could be (untested):
Process.Start("explorer.exe", "/select,\"" +
Path.GetDirectoryName(ExecutableLocation.Replace("/", "\\"))
+ "\\" + CurrentExecutableName + "\"");
GetDirectoryName handles / separators fine, but you'll still want to adjust them sometime to ensure the explorer call to works properly.
Really that last line should be (again untested):
Process.Start("explorer.exe", "/select,\"" +
Path.Combine(Path.GetDirectoryName(ExecutableLocation.Replace("/", "\\")),
CurrentExecutableName) + "\"");
I'm making an Outlook plugin which will automatically save messages to a file server when received. This part of the code simply doesn't do what it's asked to do!
//RelevantDirectory[0] is the root folder where I want to save stuff, and sits on a mapped network drive where I have full admin permissions.
System.IO.Directory.CreateDirectory(RelevantDirectory[0] + "\\Email Correspondence\\");
System.IO.Directory.CreateDirectory(RelevantDirectory[0] + "\\Email Correspondence\\Outgoing");
mail.SaveAs(RelevantDirectory[0] + "\\Email Correspondence\\Outgoing\\" + mail.SenderName + " - " + string.Format("text-{0:yyyy-MM-dd_hh-mm-ss-tt}", mail.ReceivedTime) + ".msg");
System.Windows.Forms.MessageBox.Show(System.IO.File.Exists(RelevantDirectory[0] + "\\Email Correspondence\\Outgoing\\" + mail.SenderName + " - " + string.Format("text-{0:yyyy-MM-dd_hh-mm-ss-tt}", mail.ReceivedTime) + ".msg").ToString()); //returns True!
The MessageBox returns True, yet the file isn't actually there! The CreateDirectory code also doesn't create the directory - and I'm sure that the filepath in the program is correct. I understand from this link that this could have something to do with filesystem virtualisation. If so, how do I get around the problem?
One more point is that if I point mail.SaveAs to the Desktop folder, it saves it.
EDIT For some bizarre reason, using Path.Combine() worked. Credit to Sinatr. Thanks for your hints everyone.
The problem is in the line:
string.Format("text-{0:yyyy-MM-dd_hh-mm-ss-tt}", mail.ReceivedTime)
When converted to string it becomes somthing like : 1/1/2014 12:35:35 PM
when creating files a '/' and ':' are illegal characters , so for the minimal amount change I suggest you append a couple Replace functions at the end of your string.Format like this:
string.Format("text-{0:yyyy-MM-dd_hh-mm-ss-tt}", mail.ReceivedTime).Replace('/','-').Replace(':',' ')
I am taking a file with a name of sfile
In my method I am doing like this way
File.Copy(sSFile, Failure + "\\" + Path.GetFileName(sSFile)) ''''''''''FAILURE FOLDER"""""
fileCounter += 1
File.move(sSFile, BACKup + "\\" + Path.GetFileName(sSFile)) ''''''''''BACKup FOLDER"""""
fileCounter += 1
where failure and backup are different folders
But it iss throwing an error : This process is using by some other process
Look at MSDN example. Hope it'll help.
Get the Path First, and then Copy the File, and then do the Same then Move the File.
I am guessing your trying to get the Filename when your already holding on to it to Copy and or Move It.
Use
File.Copy(sSFile, Path.Combine(Failure, Path.GetFileName(sSFile)), bool_overrideflag);
We are using an application that reads the file and inserts into database. For some reason, we set the creation time to a particular time. and when we create the file the application picks up the file before we even change the creation time. Is there anyway that we can lock the file until we change its creation time.
The error i am getting is at File.SetCreationTime as the file is being used by other process or the file is archived.
//Copy signal file
s = filePath + "s";
newS = targetFullPath2 + "s";
File.Copy(s, newS, true);
//
//new creation ts
dtFileCreation = File.GetCreationTime(s);
//retain creation time
File.SetCreationTime(newS, dtFileCreation);
Please advice.
The common solution to this is to create the file (and set its timestamp) in a different directory or under a different name first, and then move or rename it when it’s ready so the other process can pick it up. The move/rename is an atomic operation on NTFS (unless of course you move files between separate partitions).
For example:
s = filePath + "s";
newS = targetFullPath2 + "s";
File.Copy(s, newS + "_", true); // add the _ so the other process
// doesn’t “see” the file yet
dtFileCreation = File.GetCreationTime(s);
File.SetCreationTime(newS + "_", dtFileCreation);
// We’re done with the file, now rename it to its intended final name
File.Move(newS + "_", newS);
That error indicates someone (it could be you or another program) is locking the file; therefore, I do not think locking the file will solve your problem. What kind of file is it? Is your code reading the file and forgetting to close the stream after it is done?