Adding attachments unlock file - c#

I need some help.
My application works with Outlook files.
For example, I have a MSG format file is stored at my PC.
Let's imagine that I haven't got any running Outlook processes on my PC.
var myProcess = new Process { ProcessInfo = new ProcessStartInfo {FileName = targetPath}}
where targetPath is full path to my file.
Then I start process:
myProcess.Start();
At this moment Outlook runs at OS. Looking into TaskManager I see this process, and it's unique PID.
And there is my first question: why this PID is different from myProcess.Id ?
Moving on. I need to check is my file still opening at Outlook. I resolve this issue by trying to open this file in my application for a certain time.
var iterations = 3600;
while (iterations > 0 ) {
Thread.Sleep(1000);
var fInfo = new FileInfo(targetPath);
FileStream stream = null;
try {
stream = fInfo.Open(FileMode.Open, FileAccess.Read | FileAccess.Write, FileShare.None);
} catch {
//I expect if exception occurs then file is not lock and can send it to server.
}
finally{
if(stream != null)
stream.Close();
}
iteration--;
}
I think, while my MSG file is using by Outlook, I my application cant open file. In that way I decide that cant save my file and send it to server. But! If I add attachment but NOT close this e-mail at Outlook, my application CAN open this file. And I don't understand why Outlook change Read/Write attribute of this file? And how I can solve this issue?
Unfortunately, I haven't any idea why it happens and how to make it work.
I was looking on any info at web that can help to solve my issue, but has no result :(
Thank you for your time.

Firstly, Outlook is a singleton - if you start a new instance of outlook.exe, it will simply switch control to the already running instance and exit.
Outlook internally caches / references open MSG files, there is really northing you can do about that.

Related

C# - Kill EXCEL.exe process referencing a particular file

I have a C# application that creates an excel file. But if the file is left open and the app is run the second time, it throws IO exception (since, in the code I am replacing the existing file with new one)
Now, I want to check if the file is open and kill the excel.exe process that is referencing this file.
Is there any way this can be achieved?
Note: There might be several excel files open. I want to close only the process that is using my file.
xlFilePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
File.Copy(xlFilePath + "\\file.xlsx", String.Format("{0}\\OutputFile{1}.{2}.xlsx", xlFilePath, DateTime.Now.Year, DateTime.Now.Month), true);
xlFilePath = Path.Combine(xlFilePath, String.Format("{0}\\OutputFile{1}.{2}.xlsx", xlFilePath, DateTime.Now.Year, DateTime.Now.Month));
appl = new Excel.Application(Visible = false);
wbookss = appl.Workbooks;
wbook = wbookss.Open(xlFilePath);
//Excel.Worksheets wsheetss = appl.Worksheets;
wsheet = wbook.Sheets["Sheet1"];
Application_GenerateReport(wsheet);
Clipboard.Clear();
wbook.Close(true);
wbookss.Close();
appl.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(wsheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wbook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wbookss);
System.Runtime.InteropServices.Marshal.ReleaseComObject(appl);
EDIT: The file remains open if there was an exception in the last run. If it is opened by a user, it can be killed by checking the MainWindowTitle property (As suggested by Yeldar).
Another solution could be:
System.Diagnostics.Process.Start("CMD.exe","taskkill /f /im excel.exe");
This line will call command prompt and running the command taskkill.
All opening excel programs will be killed in this way.
Updated: (For one file)
System.Diagnostics.Process.Start("CMD.exe","taskkill /FI "WindowTitle eq Microsoft Excel - filename.xls");
As Excel stores the name of the opened file in its title like this:
Microsoft Excel - filename.xls
You can iterate through the processes and kill the one with the given name:
string filename = "mylist.xls";
var excelProcesses = Process.GetProcessesByName("excel");
foreach (var process in excelProcesses)
{
if (process.MainWindowTitle == $"Microsoft Excel - {filename}") // String.Format for pre-C# 6.0
{
process.Kill();
}
}
Note that:
it will also close all opened files with the same name
it will abandon any changes that user could change
title of Excel window may (or not, not sure, need to check) vary from version to version
However, as I said in comments, it is better to fix causes, but not consequences - just make your program start new Excel application without errors.

Dispose slow on very big filestreams?

I've got this code
string archiveFileName = BuildArchiveFileName(i, null);
string tmpArchiveFileName = BuildArchiveFileName(i, "tmp");
try
{
using (FileStream tmpArchiveMemoryStream = new FileStream(tmpArchiveFileName, FileMode.Create))
{
using (BinaryWriter pakWriter = new BinaryWriter(tmpArchiveMemoryStream))
{
if (i == 0)
{
WriteHeader(pakWriter, pakInfo.Header);
WriteFileInfo(pakWriter, pakInfo.FileList);
uint remainingBytesToDataOffset = pakInfo.Header.DataSectionOffset - CalculateHeaderBlockSize(pakInfo.Header);
pakWriter.Write(Util.CreatePaddingByteArray((int)remainingBytesToDataOffset));
}
foreach (String file in pakInfo.FileList.Keys)
{
DosPak.Model.FileInfo info = pakInfo.FileList[file];
if (info.IndexArchiveFile == i)
{
//Console.WriteLine("Writing " + file);
byte[] fileData = GetFileAsStream(file, false);
int paddingSize = (int)CalculateFullByteBlockSize((uint)fileData.Length) - fileData.Length;
pakWriter.Write(fileData);
pakWriter.Write(Util.CreatePaddingByteArray(paddingSize));
}
}
}
}
}
finally
{
File.Delete(archiveFileName);
File.Move(tmpArchiveFileName, archiveFileName);
}
I've tested this with NUnit on small file sizes and it works perfectly. Then when I tried it on a real life example , that are files over 1 GB. I get in trouble on the delete. It states the file is still in use by another process. While it shouldn't that file should have been disposed of after exiting the using branch. So I'm wondering if the dispose of the filestream is slow to execute and that is the reason I'm getting in trouble. Small note in all my file handling I use a FileStream with the using keyword.
While it shouldn't that file should have been disposed of after exiting the using branch
That's not what it is complaining about, you can't delete archiveFileName. Some other process has the file opened, just as the exception message says. If you have no idea what process that might be then start killing them off one-by-one with Task Manager's Processes tab. That being your own process is not entirely unusual btw. Best way is with SysInternals' Handle utility, it can show you the process name.
Deleting files is in general a perilous adventure on a multi-tasking operating system, always non-zero odds that some other process is interested in the file as well. They ought to open the file with FileShare.Delete but that's often overlooked.
The safest way to do this is with File.Replace(). The 3rd argument, the backup filename, is crucial, it allows the file to be renamed and continue to exist so that other process can continue to use it. You should try to delete that backup file at the start of your code. If that doesn't succeed then File.Replace() cannot work either. But do check that it isn't a bug in your program first, run the Handle utility.

what process is preventing my file from getting deleted in C#

I have a C# single thread application that creates a file. Uses that file and then deletes it. Some times the app has trouble deleting that file. The error I get is:
"The process cannot access the file --file path and file name-- because it is being used by another process."
How can I find out what process has a hold on this file and how can I make that process to let go so that the file can be deleted.
This thing rocks for that very "gotcha".
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx
Process Monitor v3.05
It has a "Filter" submenu so you can fine tune it to the file that is locked.
You need to post the relevant code so we can see.
It is however always important to make sure that your app close the file that it has opened.
usually something like this will ensure that:
using(var f = File.OpenRead("myfile")) {
...
}
or the equivalent:
try {
var f = File.OpenRead("myfile");
} finally {
f.close()
}
Make sure that you are closing file before delete.
if you are using StreamWriter class make sure that you are closing with its variable
Ex. StreamWriter sw = new StreamWriter();
// some writing operation
sw.Close();

Deleting a temp file that is open c#

i have written some pdf files to a temp directory and these get displayed as a thumbnail that the user can view. when i close my form i clean up all of the files in the temp directory.
If however the user has one of the thumbnails open and then closes my application - it deletes the files and then throws an exception because the pdf is open in another process and cant be cleaned up.
I guess this is a shocking programming decision by me, but i am still a novice! How should i account for this in my code?
Thanks
You can detect if the file is in use by using code similar to below, then use that to warn the user that a file can't be deleted.
Unfortunately you can't delete a file that is in use.
public static bool IsFileInUse(string pathToFile)
{
if (!System.IO.File.Exists(pathToFile))
{
// File doesn't exist, so we know it's not in use.
return false;
}
bool inUse = false;
System.IO.FileStream fs;
try
{
fs = System.IO.File.Open(pathToFile, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Read, System.IO.FileShare.None);
fs.Close();
}
catch (System.IO.IOException ex)
{
string exMess = ex.Message;
inUse = true;
}
return inUse;
}
You should catch that exception (in catch block you can inform user to close that file or it will not be deleted), and if the temp directory is yours you can try to delete it when application starts (or when it ends again), if its windows temp directory, then it does not matter that much
Tools like File Unlocker can release a file. However I think this could make programs depending on the file crash...
Maybe you can look up how they unlock files or manage to execute the unlocker via Process.Start to unlock your file and delete it.
However if it's you blocking the file you should try and fix this in your programm. Maybe you should dispose all loaded files (filestreams etc) before trying to clean it up.

Set permissions on folder used by winform application

My winform application periodically pulls a flash file from an SQL database, writes it to a temporary folder, displays it in a webbroswer control, and then deletes it from the temporary folder. My application creates the temporary folder if it does not exist. The temporary folder is located in 'System.Environment.CurrentDirectory'.
My problem is the permissions of the temporary folder frequently become read only and then my application cannot delete the file. Sometimes the problem occurs immediately, and sometimes I can run the application several times before it occurs.
How do I insure that the file is deleted?
I added code to delete the temporary folder and then re-create it each time it writes to it, but this did not resolve my problem.
Only my application needs to access this folder, and the folder only holds these flash images.
I thought about using the generic 'temp' folder, but read somewhere that that could lead to problems.
Also, I got the same problem when I located the temporary folder at 'D:\'.
I'm using VS2008 on Windows XP. The application is to run on XP, Vista and 7.
Here is code.
DataSet dsFlashQuizRandom = new DataSet();
dsFlashQuizRandom = objUserDAO.GetFlashQuizRandom(intAge);
if (dsFlashQuizRandom.Tables[0].Rows[0]["large_image_blob"] != null && dsFlashQuizRandom.Tables[0].Rows[0]["file_name"].ToString().Trim() != string.Empty)
{
byte[] b = (byte[])dsFlashQuizRandom.Tables[0].Rows[0]["large_image_blob"];
if (b != null)
{
string flashFileName = dsFlashQuizRandom.Tables[0].Rows[0]["file_name"].ToString().Trim();
string targetPath = System.Environment.CurrentDirectory.ToString() + #"\images\";
string strFileName = targetPath + flashFileName;
//Delete the current version of the folder (if it exists); then create a new version of it.
if (System.IO.Directory.Exists(targetPath))
System.IO.Directory.Delete(targetPath, true);
if (!System.IO.Directory.Exists(targetPath))
System.IO.Directory.CreateDirectory(targetPath);
//Write the file to a FileStream, assign that stream to the webbrowser control.
FileStream fs = new FileStream(strFileName, FileMode.CreateNew, FileAccess.Write);
fs.Write(b, 0, b.Length);
fs.Close();
webBrowserQuizFlash.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowserQuizFlash_DocumentCompleted);
webBrowserQuizFlash.Url = new System.Uri(strFileName, System.UriKind.Absolute);
}
}
//Delete the Flash Webbrowser file once it has completed loading.
private void webBrowserQuizFlash_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
FileInfo fi = new FileInfo(strFileName);
try
{
fi.Delete();
}
catch (IOException ex)
{
MessageBox.Show("IOException = " + ex); //test code
}
}
Any suggestions or a point in the right direction would be appreciated.
Cheers,
Frederick
PS--When copying my code to this post I see the color of the text is all red after the #"\images\"; Is there a problem with this part of my code, or is this a display artifact? Should I use this instead: #"\images\\";
You could use System.IO.Path.GetTempPath() to get a temp folder to use.
I assume that you're having problems with the delete due to the file being locked by some process. You might be able to get around that by using the MoveFileEx function to delete it at next reboot.
I think the accessing problem comes from another application that locks the file. One common application group that does such things would be the on access scanner from your anti-virus program.
To get a deeper look into, who accesses your file you should take a deeper look with Process Monitor to find out who will block your file.
Also you can maybe make a little change to your code:
//Write the file to a FileStream, assign that stream to the webbrowser control.
using(FileStream fs = new FileStream(strFileName, FileMode.CreateNew, FileAccess.Write))
{
fs.Write(b, 0, b.Length);
}

Categories

Resources