I am currently having issues with a piece of my code. This code works perfectly fine (even without some of the extra if statements) on my machine when I run it both through Visual Studio 2013 and when I publish using ClickOnce. On the client's machine, the event handler for the process exiting catches, the process ID is correct, and the process.hasExited returns true all while the process is still open. The process opens a pdf for them to sign and waits for them to save it and close it before proceeding. It uses Adobe Acrobat Reader DC.
The error message received is from the catch block of the event handler method:
System.IO.IOException: The process cannot access the file because it is being used by another process.
This occurs at the call for getReaders_NewHire(emp).
This is the method that opens the process:
private void pdfAndEmail(Employee employee, Requirements requirements)
{
try
{
PDFUtility pdfu = new PDFUtility();
pdfu.createPDFMG_NewHire(employee, requirements);
emp = employee;
process = new System.Diagnostics.Process();
string path = System.IO.Path.GetTempPath() + "\\ITResources\\Manager\\NewHire_" + employee.m_name.Replace(" ", "") + ".pdf";
Uri pdf = new Uri(path, UriKind.RelativeOrAbsolute);
process.StartInfo.FileName = pdf.LocalPath;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(process_Exited);
process.Start();
pid = process.Id;
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
and this is the event handling method:
private void process_Exited(object sender, EventArgs e)
{
try
{
if (process.Id == pid)
{
if (process.HasExited)
{
PDFUtility pdfu = new PDFUtility();
pdfu.getReaders_NewHire(emp);
Emailer send = new Emailer();
send.SendAfterMG_NewHire();
}
else
MessageBox.Show("Process has not exited.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
The getReaders method in the event handler needs to read the file that the client has to sign so it must be closed beforehand. I cannot force a close because I will never know how long it will take them to sign and I can't have the email being sent out before they've signed and saved the pdf.
I've already tried using the WaitForExit() method and it skips completely (only on my client's computer). I am running Windows 10, but the client is running Windows 7. I have not been able to find any documentation about these methods not working on Win7.
Note: I understand that the if statements in the event handler method are a bit redundant, but I was desperate to find where it was catching.
Any help would be greatly appreciated.
FIXED: I ended up have a Message Box pop up after my PDF was signed to assist the WaitForExit() in the Background Worker thread. After this message box's OK button is pressed (because they pause the application and wait for a response), it then reads the pdf files.
It uses Adobe Acrobat Reader DC.
This is your advantage I think, and my approach assumes this. Consider the following:
private static void Main()
{
var process = Process.GetProcessesByName("AcroRd32").FirstOrDefault();
if (process != null)
{
Process.Start(#"C:\mvvm.pdf");
}
else
{
process = Process.Start(#"C:\mvvm.pdf");
}
process.EnableRaisingEvents = true;
process.Exited += (sender, args) =>
{
Console.WriteLine("Exited");
};
Console.ReadLine();
}
First we check if Adobe process is already running, if so we keep the reference and hook the event handler to that existing process. If not we do what you have already done. Some validation code is omitted.
I have tested with the process running and not running and it works.
NOTICE: It will not work as expected if the user has another application configured as a predefined PDF reader
the problem I have here might have a quick workaround but I've been stuck for a while.
I am writing a service that starts a process when a user logs in and stops it when the user logs out. In my previous tries I have tried using a loop which checked the presence of explorer.exe but it didn't work very well as the infinite loop meant the service was stuck at "starting". So I have looked into the C# on session change. Here is the code I currently have, creating text files to check if it actually works.
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
ServiceName = "Service1";
}
public void onDebug()
{
OnStart(null);
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
EventLog.WriteEntry("SimpleService.OnSessionChange", DateTime.Now.ToLongTimeString() +
" - Session change notice received: " +
changeDescription.Reason.ToString() + " Session ID: " +
changeDescription.SessionId.ToString());
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "sessionchange.txt");
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
string[] lines = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged in.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedin.txt", lines);
break;
case SessionChangeReason.SessionLogoff:
string[] lines2 = { DateTime.Now.ToShortTimeString() };
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "logged out.txt");
System.IO.File.WriteAllLines(#"C:\Users\Public\test\loggedout.txt", lines2);
break;
}
}
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "onstart.txt");
}
I expected this to work, but apparently not. The on start text file is being created, but out of the 4 logged on/off ones that should appear (somewhere) only loggedout.txt appears in the Public user directory after a log out. No matter how many times I shut down, log in or out the other text files for login are not being created. The time in the only loggedout.txt file create also won't update.
If it helps, I'm running this in a Virtual Machine. I have also read somewhere that SessionChange might only apply to remote log ins but I'm not sure. I'm not sure how to proceed, wether to keep trying or there is another way for services to "listen" to session changes?
Thanks for taking the time to read through.
Overview of Problem:
I need to connect to an IRC Server. Once connected, the program will send a message to the channel, and a response will occur over multiple lines back. I need to read these lines and store in a variable for later use. A special character at the end of the message (]) will define the end of the message over multiple lines. Once we have received this character, the IRC session should disconnect and processing should continue.
Situation:
I am using the Smartirc4net library. Calling irc.Disconnect() takes about 40 seconds to disconnect the session. Once we've received the ] character, the session should be disconnected, Listen() should not be blocking, and the rest of the program should continue to run.
Research:
I have found this: smartirc4net listens forever, can't exit thread, and I think it might be the same issue, however, I am unsure of what I need to do to resolve the problem.
Code:
public class IrcCommunicator
{
public IrcClient irc = new IrcClient();
string data;
public string Data { get { return data; } }
// this method we will use to analyse queries (also known as private messages)
public void OnQueryMessage(object sender, IrcEventArgs e)
{
data += e.Data.Message;
if (e.Data.Message.Contains("]"))
{
irc.Disconnect(); //THIS TAKES 40 SECONDS!!!
}
}
public void RunCommand()
{
irc.OnQueryMessage += new IrcEventHandler(OnQueryMessage);
string[] serverlist;
serverlist = new string[] { "127.0.0.1" };
int port = 6667;
string channel = "#test";
try
{
irc.Connect(serverlist, port);
}
catch (ConnectionException e)
{
// something went wrong, the reason will be shown
System.Console.WriteLine("couldn't connect! Reason: " + e.Message);
}
try
{
// here we logon and register our nickname and so on
irc.Login("test", "test");
// join the channel
irc.RfcJoin(channel);
irc.SendMessage(SendType.Message, "test", "!query");
// here we tell the IRC API to go into a receive mode, all events
// will be triggered by _this_ thread (main thread in this case)
// Listen() blocks by default, you can also use ListenOnce() if you
// need that does one IRC operation and then returns, so you need then
// an own loop
irc.Listen();
// when Listen() returns our IRC session is over, to be sure we call
// disconnect manually
irc.Disconnect();
}
catch (Exception e)
{
// this should not happen by just in case we handle it nicely
System.Console.WriteLine("Error occurred! Message: " + e.Message);
System.Console.WriteLine("Exception: " + e.StackTrace);
}
}
}
IrcBot bot = new IrcBot();
bot.RunCommand();
ViewBag.IRC = bot.Data;
As you can see, once this
Thank you for your time to look at this code and read my problem description. If you have any thoughts, or other suggestions, please let me know.
Mike
I was able to successfully disconnect straight away by calling RfcQuit() within OnQueryMessage(), before irc.Disconnect();
I have created a very simple console app which downloads lot of files from web and place them in folder structure following a separate mapping file. The requirement doesn't require that the file needs to downloaded asynchronously.
The program works, but the problem is if somebody chooses to cancel the app using ctrl+c or ctrl+break.
If that is done, the file in progress will become corrupted as the program exit instantly. So I wanted to delete the corrupted file before exiting. So I have written the following handler,
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Program interrupted..deleting corrupted file");
Console.ResetColor();
if (File.Exists(fileInProgress))
{
File.Delete(fileInProgress);
}
}
catch
{
Console.WriteLine("Error occured.");
}
}
fileinprogress is global variable which updated from the function which calls download file.
The problem with above code is if ctrl+c is press it execute the code but it never deletes the file as the file in use. So I followed https://stackoverflow.com/a/937558/714518 and trying to wait untill the program releases the file
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Program interrupted..deleting corrupted file");
Console.ResetColor();
if (File.Exists(fileInProgress))
{
while (IsFileLocked(fileInProgress))
{
System.Threading.Thread.Sleep(1000);
}
File.Delete(fileInProgress);
}
}
catch
{
Console.WriteLine("Error occured.");
}
}
Now I don't understand the behavior. Now the program waits few seconds if ctrl+c is pressed and then without deleting the file it continues to download the next file. Please help to get rid of this problem.
The actual app is quite larger, I just recreated the situation. Please see http://pastebin.com/TRBEAvwi for full code..
It sounds like you need a way to signal your download code to stop downloading. From looking at this sample I think the best location would probably be on entry to your Console_CancelKeyPress function. Otherwise your download code will never realize that it needs to release the file lock and stop downloading.
For instance:
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
try
{
Interlocked.Increment(ref globalStopFlag);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Program interrupted..deleting corrupted file");
Console.ResetColor();
if (File.Exists(fileInProgress))
{
while (IsFileLocked(fileInProgress))
{
System.Threading.Thread.Sleep(1000);
}
File.Delete(fileInProgress);
}
}
catch
{
Console.WriteLine("Error occured.");
}
}
void SomeDownloadFunction()
{
using (somefile)
{
while (!downloadFinished)
{
long doINeedToStop = Interlocked.Read(ref globalStopFlag)
if (doINeedToStop != 0)
return;
//Download the next set of packets and write them to somefile
}
}
}
When a file is created (FileSystemWatcher_Created) in one directory I copy it to another. But When I create a big (>10MB) file it fails to copy the file, because it starts copying already, when the file is not yet finished creating...
This causes Cannot copy the file, because it's used by another process to be raised. ;(
Any help?
class Program
{
static void Main(string[] args)
{
string path = #"D:\levan\FolderListenerTest\ListenedFolder";
FileSystemWatcher listener;
listener = new FileSystemWatcher(path);
listener.Created += new FileSystemEventHandler(listener_Created);
listener.EnableRaisingEvents = true;
while (Console.ReadLine() != "exit") ;
}
public static void listener_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine
(
"File Created:\n"
+ "ChangeType: " + e.ChangeType
+ "\nName: " + e.Name
+ "\nFullPath: " + e.FullPath
);
File.Copy(e.FullPath, #"D:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name);
Console.Read();
}
}
There is only workaround for the issue you are facing.
Check whether file id in process before starting the process of copy. You can call the following function until you get the False value.
1st Method, copied directly from this answer:
private bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
2nd Method:
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private bool IsFileLocked(string file)
{
//check that problem is not in destination file
if (File.Exists(file) == true)
{
FileStream stream = null;
try
{
stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (Exception ex2)
{
//_log.WriteLog(ex2, "Error in checking whether file is locked " + file);
int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1);
if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION))
{
return true;
}
}
finally
{
if (stream != null)
stream.Close();
}
}
return false;
}
From the documentation for FileSystemWatcher:
The OnCreated event is raised as soon as a file is created. If a file
is being copied or transferred into a watched directory, the
OnCreated event will be raised immediately, followed by one or more
OnChanged events.
So, if the copy fails, (catch the exception), add it to a list of files that still need to be moved, and attempt the copy during the OnChanged event. Eventually, it should work.
Something like (incomplete; catch specific exceptions, initialize variables, etc):
public static void listener_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine
(
"File Created:\n"
+ "ChangeType: " + e.ChangeType
+ "\nName: " + e.Name
+ "\nFullPath: " + e.FullPath
);
try {
File.Copy(e.FullPath, #"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name);
}
catch {
_waitingForClose.Add(e.FullPath);
}
Console.Read();
}
public static void listener_Changed(object sender, FileSystemEventArgs e)
{
if (_waitingForClose.Contains(e.FullPath))
{
try {
File.Copy(...);
_waitingForClose.Remove(e.FullPath);
}
catch {}
}
}
It's an old thread, but I'll add some info for other people.
I experienced a similar issue with a program that writes PDF files, sometimes they take 30 seconds to render.. which is the same period that my watcher_FileCreated class waits before copying the file.
The files were not locked.
In this case I checked the size of the PDF and then waited 2 seconds before comparing the new size, if they were unequal the thread would sleep for 30 seconds and try again.
You're actually in luck - the program writing the file locks it, so you can't open it. If it hadn't locked it, you would have copied a partial file, without having any idea there's a problem.
When you can't access a file, you can assume it's still in use (better yet - try to open it in exclusive mode, and see if someone else is currently opening it, instead of guessing from the failure of File.Copy). If the file is locked, you'll have to copy it at some other time. If it's not locked, you can copy it (there's slight potential for a race condition here).
When is that 'other time'? I don't rememeber when FileSystemWatcher sends multiple events per file - check it out, it might be enough for you to simply ignore the event and wait for another one. If not, you can always set up a time and recheck the file in 5 seconds.
Well you already give the answer yourself; you have to wait for the creation of the file to finish. One way to do this is via checking if the file is still in use. An example of this can be found here: Is there a way to check if a file is in use?
Note that you will have to modify this code for it to work in your situation. You might want to have something like (pseudocode):
public static void listener_Created()
{
while CheckFileInUse()
wait 1000 milliseconds
CopyFile()
}
Obviously you should protect yourself from an infinite while just in case the owner application never releases the lock. Also, it might be worth checking out the other events from FileSystemWatcher you can subscribe to. There might be an event which you can use to circumvent this whole problem.
When the file is writing in binary(byte by byte),create FileStream and above solutions Not working,because file is ready and wrotted in every bytes,so in this Situation you need other workaround like this:
Do this when file created or you want to start processing on file
long fileSize = 0;
currentFile = new FileInfo(path);
while (fileSize < currentFile.Length)//check size is stable or increased
{
fileSize = currentFile.Length;//get current size
System.Threading.Thread.Sleep(500);//wait a moment for processing copy
currentFile.Refresh();//refresh length value
}
//Now file is ready for any process!
So, having glanced quickly through some of these and other similar questions I went on a merry goose chase this afternoon trying to solve a problem with two separate programs using a file as a synchronization (and also file save) method. A bit of an unusual situation, but it definitely highlighted for me the problems with the 'check if the file is locked, then open it if it's not' approach.
The problem is this: the file can become locked between the time that you check it and the time you actually open the file. Its really hard to track down the sporadic Cannot copy the file, because it's used by another process error if you aren't looking for it too.
The basic resolution is to just try to open the file inside a catch block so that if its locked, you can try again. That way there is no elapsed time between the check and the opening, the OS does them at the same time.
The code here uses File.Copy, but it works just as well with any of the static methods of the File class: File.Open, File.ReadAllText, File.WriteAllText, etc.
/// <param name="timeout">how long to keep trying in milliseconds</param>
static void safeCopy(string src, string dst, int timeout)
{
while (timeout > 0)
{
try
{
File.Copy(src, dst);
//don't forget to either return from the function or break out fo the while loop
break;
}
catch (IOException)
{
//you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible
}
Thread.Sleep(100);
//if its a very long wait this will acumulate very small errors.
//For most things it's probably fine, but if you need precision over a long time span, consider
// using some sort of timer or DateTime.Now as a better alternative
timeout -= 100;
}
}
Another small note on parellelism:
This is a synchronous method, which will block its thread both while waiting and while working on the thread. This is the simplest approach, but if the file remains locked for a long time your program may become unresponsive. Parellelism is too big a topic to go into in depth here, (and the number of ways you could set up asynchronous read/write is kind of preposterous) but here is one way it could be parellelized.
public class FileEx
{
public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone)
{
while (timeout > 0)
{
try
{
File.Copy(src, dst);
doWhenDone();
break;
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
}
public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout)
{
while (timeout > 0)
{
try {
return File.ReadAllText(filePath);
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
return "";
}
public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout)
{
while (timeout > 0)
{
try
{
File.WriteAllText(filePath, contents);
return;
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
}
}
And here is how it could be used:
public static void Main()
{
test_FileEx();
Console.WriteLine("Me First!");
}
public static async void test_FileEx()
{
await Task.Delay(1);
//you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy
//As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously
//until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx
CopyWaitAsync("file1.txt", "file1.bat", 1000);
//this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes
await CopyWaitAsync("file1.txt", "file1.readme", 1000);
Console.WriteLine("file1.txt copied to file1.readme");
//The following line doesn't cause a compiler error, but it doesn't make any sense either.
ReadAllTextWaitAsync("file1.readme", 1000);
//To get the return value of the function, you have to use this function with the await keyword
string text = await ReadAllTextWaitAsync("file1.readme", 1000);
Console.WriteLine("file1.readme says: " + text);
}
//Output:
//Me First!
//file1.txt copied to file1.readme
//file1.readme says: Text to be duplicated!
You can use the following code to check if the file can be opened with exclusive access (that is, it is not opened by another application). If the file isn't closed, you could wait a few moments and check again until the file is closed and you can safely copy it.
You should still check if File.Copy fails, because another application may open the file between the moment you check the file and the moment you copy it.
public static bool IsFileClosed(string filename)
{
try
{
using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
{
return true;
}
}
catch (IOException)
{
return false;
}
}
I would like to add an answer here, because this worked for me. I used time delays, while loops, everything I could think of.
I had the Windows Explorer window of the output folder open. I closed it, and everything worked like a charm.
I hope this helps someone.