I'm writing a C# program for the company I work for that will launch a PHP script which creates a PDF, and then opens the PDF file. Right now, I have:
// launch the PHP page to generate my pdf report
Process.Start(phpFile);
// wait for the report to exist
Thread.Sleep(waitTime);
// open the report
Process.Start(filePath);
Now, I'm not a fan of the whole "Sleep() for a specified time in hopes of the file existing when it's done". So my question is, is it feasible/better to use a do loop and say:
do
{
// Do nothing until the file exists
} while (!File.Exists(filePath));
Why not use a FileSystemWatcher ?
Set the Path and Filter property and subscribe to the Created event.
The problem with using File.Exists is that the file may exist prior to it finishing being written by the first process. I would do something like this instead:
// launch the PHP page to generate my pdf report
Process generator = Process.Start(phpFile);
// wait for the report to exist
generator.WaitForExit();
// open the report
Process.Start(filePath);
Use a FileSystemWatcher and handle the created event. If you want it to happen synchronously use FileSystemWatcher.WaitForChanged with a timeout
I think is a better aproach to look if the file exists in any way. If you rely on a fixed amount of time it can fail or the process will be waiting more than it needs.
What about waiting for the end of the first process ?
Process p = Process.Start(phpFile);
p.WaitForExit();
Process.Start(filePath);
Related
I am working on a project that I need to do the following:
I need to rename an image file. (Open an image from folder, and give a name & save it in to same folder)
try
{
string oldFileName = #"path\to\person1.jpg";
string desFileName = #"path\to\person2.jpg";
File.Copy(oldFileName, desFileName, true);
if (File.Exists(oldFileName))
{
File.Delete(#oldFileName);
}
}
catch (Exception ex)
{
}
I did rename the file using above way.
This process copy the old file with new name, but couldn't remove old file
Exception message :
The process cannot access the file 'path\to\person1.jpg' because it is
being used by another process.
How to resolve this? Please suggest any way to detect copying process has complete or not.
Your copy process is definatly complete on if statement becouse your code is sync.
I bet you got this error becouse file is used by another proccess (not your programm). Maby you have paint open or something else.
You should find it out with process monitor or something else. Check this question.
I have a Windows Forms Application that uses 2 forms, with both writing to separate files (file paths given by inclusion of strings in textboxes on the form).
For form1, I have a number of functions that write data to the file on various different button clicks. This being the case, I used the StreamWriter consoleFile = new StreamWriter(File.OpenWrite(fileName)); method for the first writing to file and StreamWriter consoleFile = File.AppendText(fileName); for any subsequent ones. This has worked fine.
When it came to implementing the same feature for Form2, the main difference is that all the text is written at once (one function containing four sub-functions to try and keep the code tidy). I went about it like this...
public void writeChecklistToFile()
{
//open new file for writing
StreamWriter checklistFileStart = new StreamWriter(File.OpenWrite(getChecklistFile()));
checklistFileStart.WriteLine("Pre-Anaesthetic Checklist\n");
//sub-functions (one for each section of list)
//append tool used in separate functions
//StreamWriter checklistFile = File.AppendText(getChecklistFile());
writeAnimalDetails();
writeAnimalHistory();
writeAnimalExamination();
writeDrugsCheck();
}
Each of the sub-functions then contains the appendText variable shown above:
public void writeAnimalDetails()
{
StreamWriter checklistFile = File.AppendText(getChecklistFile());
//...
}
Whenever I click the button that calls the main function, it throws an exception on the first File.AppendText() method. It states that the destination file cannot be accessed because it is already being used in another process.
Presumably this has to be the OpenWrite() as it is not used anywhere before that, but I don't understand why this error would occur in my form2 when it doesn't in form1!
If anyone could help me get around this, or can point me in the direction of an easier way to do it, I'd really appreciate that.
Thanks
Mark
Read the error as "File cannot be accessed because [the file is still open for use by this] process".
The problem is that the file resource - from File.OpenWrite - is not being Disposed correctly and an unmanaged file handle, with an exclusive lock, is kept open. This in turn results in exceptions when trying to open the still-open file for writing. Use the using statement to help with lifetime management, as discussed here.
In this particular case I recommend supplying the StreamWriter - created once - as an argument to the functions that need to write to it and then Dispose the entire open file resource once at the end when complete. This ensures a more visible resource lifetime and avoids several open-close operations.
public void writeChecklistToFile()
{
// Open file for writing once..
using (var checklistWriter = new StreamWriter(File.OpenWrite(getChecklistFile())))
{
// .. write everything to it, using the same Stream
checklistWriter.WriteLine("Pre-Anaesthetic Checklist\n");
writeAnimalDetails(checklistWriter);
writeAnimalHistory(checklistWriter);
writeAnimalExamination(checklistWriter);
writeDrugsCheck(checklistWriter);
}
// And the file is really closed here, thanks to using/Dispose
}
Also see
File cannot be accessed because it is being used by another program
I think the reason it works in your first form is that you only ever have one StreamWriter existing at a time. You click a button, a StreamWriter is created, the function ends, and the StreamWriter is automatically closed before the next button click calls a function.
With your second form, however, you're calling your sub functions with their StreamWriters within a main function that also has a StreamWriter. What that amounts to is you have more than one StreamWriter trying to open the file at the same time, and thus the error.
To fix this, you can put after your call to WriteLine in your writeChecklistToFile function:
checklistFileStart.Close();
This will close your first FileStream, and allow your subsequent ones to open up the file.
I wanted to test if a particular file is already open before trying to launch it, so I came up with this:
public void LaunchErrorLog()
{
var logFile = ConfigurationManager.AppSettings["Log"];
if (IsLogOpen(logFile))
return; //figure out how to give focus to other app later
var psi = new ProcessStartInfo(logFile);
psi.WindowStyle = ProcessWindowStyle.Maximized;
Process.Start(psi);
}
private bool IsLogOpen(string p)
{
try
{
using (var s = new FileStream(p, FileMode.Open, FileAccess.Read)){}
}
catch (IOException)
{
return true;
}
return false;
}
I'm testing using a .log file (just a text file) that I've got open in Baretail. The method always returns false regardless of whether or not the file is open. I tried opening it in Notepad, and it still returns false.
Basically, the end objective is to give focus to the application that has the file open, or launch the application/file if it's not already open. But this is always false so it just goes on and launches a new instance of Baretail with the file open.
Also tried the top solution found here;
Is there a way to check if a file is in use?
Notepad is a bad test application because it does not hold a lock open on the file. It streams in the file and closes the lock. Use Word to do the test and you will see different results. A file is only locked if a handle is kept open by an application. Word will lock files. The same holds true for "Baretail".
In other words, if "Baretail" streams the file in and closes the lock then this test will not work. You could do something hacky such as sniff around Win32 objects...window handles and title bars to extract the information...but be warned this kind of UI hacking is tricky and I would not consider the information to be reliable. There's no stopping some other program from using similar text in their title bars per se.
I believe that that method is always returning false because you are opening for read. Even if the file is open for write elsewhere, you should be able to read that file (thus no exception is thrown).
Try using a different FileAccess opetion ie
FileAccess.ReadWrite
Or you can also try different combinations of FileMode and FileOption. (Sorry not in front of a dev machine at this point)
I have a table in my database that stores all kind of files.
File names are shown in a ListView and when an user clics on one of them then it's opened by the registered application based on file extension.
This is the code:
if (listViewArchivos.HasItems)
{
dynamic result = listViewArchivos.SelectedItem;
var nombre = Path.GetTempPath() + admin.buscarNombreArchivo((int)result.Id);
var bytes = admin.buscarArchivo((int)result.Id);
try
{
using (var writer = new BinaryWriter(File.Open(nombre, FileMode.Create)))
{
writer.Write(bytes);
}
var p = Process.Start(nombre);
p.WaitForExit();
}
catch (Exception exc)
{
InterfazUtil.error(exc.Message); // This shows a MessageBox
}
finally
{
File.Delete(nombre);
}
}
It's working fine for docx, pdf, txt, etc. But when I try to open an image the file is successfully opened by Window Photo Viewer (Windows 7) but a NullReferenceException is thrown.
If I close WPV first and then the MessageBox the file is deleted from temp folder.
If I close the MessageBox first then the image disappears from WPV and after I close WPV the file is not deleted from temp folder.
Now, if I remove the catch block then the file is successfully opened by WPV and after closing it the file is not deleted from temp folder. Obviously the application crashes because the exception isn't managed.
Looks like the problem is WPV.
Any idea of what is wrong?
TIA
EDIT:
The exception is thrown at
p.WaitForExit();
When you close the MessageBox first the temp file is not deleted because WPV uses it and doesn't allow it.
According to this MSDN: http://msdn.microsoft.com/en-us/library/53ezey2s.aspx
...you will not get back a Process object when the process is already running.
I found this on a forum relating to the nature of WindowsPhotoViewer:
Actually, the Windows Photo Viewer is part of Windows Explorer, and
generally runs in the Explorer.exe process. In fact, what you're
calling the Photo Viewer is really just the "preview" verb for images.
It isn't a standalone application, and opening it without an image or
images doesn't really make any sense.
Thus, you are not getting back a Process object because it is already running by virtue of the fact that explorer.exe is already running.
In the end, I think it means that if your images open in WindowsPhotoViewer, you will not be able to make WaitForExit() work because the owner process will never exit.
My application use "FileSystemWatcher()" to raise an event when a TXT file is created by an "X" application and then read its content.
the "X" application create a file (my application detect it successfully) but it take some time to fill the data on it, so the this txt file cannot be read at the creation time, so im
looking for something to wait until the txt file come available to reading. not a static delay but something related to that file.
any help ? thx
Create the file like this:
myfile.tmp
Then when it's finished, rename it to
myfile.txt
and have your filewatcher watch for the .txt extension
The only way I have found to do this is to put the attempt to read the file in a loop, and exit the loop when I don't get an exception. Hopefully someone else will come up with a better way...
bool FileRead = false;
while (!FileRead)
{
try
{
// code to read file, which you already know
FileRead = true;
}
catch(Exception)
{
// do nothing or optionally cause the code to sleep for a second or two
}
}
You could track the file's Changed event, and see if it's available for opening on change. If the file is still locked, just watch for the next change event.
You can open and read a locked file like this
using (var stream = new FileStream(#"c:\temp\file.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (var file = new StreamReader(stream)) {
while (!file.EndOfStream) {
var line = file.ReadLine();
Console.WriteLine(line);
}
}
}
However, make sure your file writer flushes otherwise you may not see any changes.
The application X should lock the file until it closes it. Is application X also a .NET application and can you modify it? In that case you can simply use the FileInfo class with the proper value for FileShare (in this case FileShare.Read).
If you have no control over application X, the situation becomes a little more complex. But then you can always attempt to open the file exclusively via the same FileInfo.Open method. Provide FileShare.None in that case. It will attempt to open the file exclusively and will fail if the file is still in use. You can perform this action inside a loop until the file is closed by application X and ready to be read.
We have a virtual printer for creating pdf documents, and I do something like this to access that document after it's sent to the printer:
using (FileSystemWatcher watcher = new FileSystemWatcher(folder))
{
if(!File.Exists(docname))
for (int i = 0; i < 3; i++)
watcher.WaitForChanged(WatcherChangeTypes.Created, i * 1000);
}
So I wait for a total of 6 seconds (some documents can take a while to print but most come very fast, hence the increasing wait time) before deciding that something has gone awry.
After this, I also read in a for loop, in just the same way that I wait for it to be created. I do this just in case the document has been created, but not released by the printer yet, which happens nearly every time.
You can use the same class to be notified when file changes.
The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, or security permissions of a file or directory in the directory being monitored.
So I think you can use that event to check if file is readable and open it if it is.
If you have a DB at your disposal I would recommend using a DB table as a queue with the file names and then monitor that instead. nice and transactional.
You can check if file's size has changed. Although this will require you to poll it's value with some frequency.
Also, if you want to get the data faster, you can .Flush() while writing, and make sure to .Close() stream as soon as you will finish writing to it.