Load Excel process and close the entire process when closing a document - c#

In my C# application, I can Open a new Excel process by clicking a button.
Then, it waits for input idle and load a new Excel file. I have to accomplish it because I want all macros and tools to be loaded before loading my document (otherwise, there is a rendering bug).
The Process Id is saved in a var, and when I click again on that button, with the PID, if the process already exists, the Process has the focus, otherwise, a new process is created :
Process processExcel = null;
if (pId_m.HasValue)
{
try
{
processExcel = Process.GetProcessById(pId_m.Value);
}
catch (Exception ex)
{
Log.MonitoringLogger.Error("Unable to find Pid : " + pId_m,ex);
}
}
if (processExcel != null && (processExcel.HasExited == false))
{
AutomationElement element = AutomationElement.FromHandle(processExcel.MainWindowHandle);
if (element != null)
{
element.SetFocus();
}
}
else
{
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "Excel.exe";
info.Arguments = " /e ";
processExcel_l = Process.Start(info);
pId_m = processExcel_l.Id;
processExcel_l.WaitForInputIdle();
processExcel_l.StartInfo = new ProcessStartInfo(path);
processExcel_l.Start();
}
The problem is the following : when I close the Excel document window (and not Excel window), and I click the button, the focus is set to the Excel process, but without any document...
This behavior is logic, but not working for what I want...
I have seen a software that load a new process and a new document inside, but when clicking on the document close button, the entire process was exited...
How to reproduce the same?
Edit : Ok,
Finally instead of setting the focus on the process, I launched a file on this process (which set focus if the file is already open).
It's not what I really wanted to do, but it solve my problem...

I would suggest utilizing the Excel COM model rather than running the process by hand. You can subscribe to events of the Worksheet and close the application.
These MSDN documents might be helpful:
http://msdn.microsoft.com/en-us/library/wss56bz7(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.worksheet_members.aspx

Wait for Excel process ending and then close your app:
processExcel_l.WaitForExit();
Application.Exit();
To handle closing only Excel document (not Excel process) you probably need to reference to Excel API.

Related

Closing a particular excel file when multiple excel files are opened

I am trying to close an excel file named TestReport.xlsx using the below code. It is working when only one excel process is running but when I have multiple excel windows open, the MainWindowTitle changes and the code is not killing the desired excel process.
Process[] plist = Process.GetProcessesByName("Excel",".");
foreach(Process p in plist)
{
if (p.MainWindowTitle.Contains("TestReport.xlsx") && p.ProcessName == "EXCEL")
{
p.Kill();
}
}
The explanation you are giving in your question is not accurate enough. You must distinguish two cases:
Case 1: You have more than one Excel process each with one workbook. That means that e.g. all your excel workbooks each run in an own hosting excel window. You achieve this when e.g right-clicking on the excel icon, getting an empty excel workbook and loading the workbook you want into that process. In this case your approach works, as every excel process has its own title naming the workbook file name. The requested process is killed when iterating through the excel processes.
Case 2, which is the more "normal" case when working with excel, and to which you probably refer to: You have one excel process hosting more than one workbook, each possibly with several worksheets. In that case Excel acts as you describe and changes its window title (Multiple document interface).
In case 2, when there is only one single excel process present, you can close the workbook in the following way, using COM Interop:
private void button1_Click(object sender, EventArgs e)
{
CloseExcelWorkbook("TestReport.xlsx");
}
//put the following abbreviation to the "using" block: using Excel = Microsoft.Office.Interop.Excel;
internal void CloseExcelWorkbook(string workbookName)
{
try
{
Process[] plist = Process.GetProcessesByName("Excel", ".");
if (plist.Length > 1)
throw new Exception("More than one Excel process running.");
else if (plist.Length == 0)
throw new Exception("No Excel process running.");
Object obj = Marshal.GetActiveObject("Excel.Application");
Excel.Application excelAppl = (Excel.Application)obj;
Excel.Workbooks workbooks = excelAppl.Workbooks;
foreach (Excel.Workbook wkbk in workbooks )
{
if (wkbk.Name == workbookName)
wkbk.Close();
}
//dispose
//workbooks.Close(); //this would close all workbooks
GC.Collect();
GC.WaitForPendingFinalizers();
if (workbooks != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbooks);
//excelAppl.Quit(); //would close the excel application
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelAppl);
GC.Collect();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This approach works in case you have only ONE Excel process running. When there are more than one present, the situation is more complicated as you must get access to all Excel processes. For a discussion for that case, see here.
Another point to observe is to release excel objects properly to avoid stale excel objects. See there.
If you omit the "dispose" activities, it may happen that on closing Excel and continuing to run your app the Excel process runs silently on, as an inspection in Task Manager shows.

check if a text file is opened in notepad

how to find whether specific .txt file is opened in notepad?
I have tried solutions mentioned here
Is there a way to check if a file is in use?
But they work fine for Word and pdf file but not working for txt file opened in Notepad.
here is code I have wrote.
public bool IsFileOpen(string strFileName)
{
bool retVal = false;
try
{
if (File.Exists(pstrFileName))
{
using (FileStream stream = File.OpenWrite(pstrFileName))
{
try
{
}
catch (IOException)
{
retVal = true;
}
finally
{
stream.Close();
stream.Dispose();
}
}
}
}
catch (IOException)
{ //file is opened at another location
retVal = true;
}
catch (UnauthorizedAccessException)
{ //Bypass this exception since this is due to the file is being set to read-only
}
return retVal;
}
am i missing somthing here.??
My requirement:
I have application which works similar to VSS. When user checks out specific file and opens ,and try to check in the same, while it has opened. Application is suppose to throw a warning message.For that i have used the above functionality.Its working fine for word and pdf.
To expand on my comment. A file is only locked if a handle is kept open by an application. Word for example will open the file, read in the stream and maintain the handle so that other applications cannot delete that file while the user is working on it.
Notepad, and other applications, just open the file, read in the entire stream and then close the file releasing the lock they have. This means that the file is no longer locked and can be edited by another application or even deleted and Notepad will not care as it has its own copy in memory.
You could try and hack around with getting instances of Notepad and checking if a file is open but this is ultimately not a great idea. If the file is not locked then you should be free to do what you want with it.
This is a hack solution I just came up with, but it should work for you. This makes use of System.Diagnostics.
Process[] processes = Process.GetProcessesByName("notepad");
for (int i = 0; i < processes.Length; i++)
{
Console.WriteLine(processes[i].MainWindowTitle);
if (processes[i].MainWindowTitle.Equals("myFile.txt - Notepad"))
{
Console.WriteLine("The file myFile is Open!");
}
}
Console.ReadLine();
Hopefully that should do the trick. My example looks to see if an instance of notepad is open with the window title "myFile.txt - Notepad". The window name is always "filename.extension - Notepad" so you can handle that however you might need to.
I suppose you could make a call to System.IO.File.GetLastAccessTime(filePath). You could then poll the file every so often and when the access time changes you know the file has been opened, you can then fire an event that the file has been opened. See Jeffs post here:
Detect File Read in C#
You could also do this using the following tactic: It seems that notepad does hold some kind of lock on the hosting folder (try to delete the folder and you'll see you can't).
you could use the following code Using C#, how does one figure out what process locked a file? to check list of processes that lock the folder.
one of the processes will be your notepad.
you could them compare by Title as another answers mentioned.
if you're issuing the open of the file - you could save the PID and comapre it with one of the processes that returned.

NullReferenceException only when opening images from database

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.

How to set the active (i.e. foreground) Word document using C#

I have looked and failed to find a solution to my problem. I have developed an Add-in ribbon for Word 2007 which provides an additional set of load and save functions, to allow users to load and save documents from a bespoke system.
I have most of it working - when a user requests a file to be opened, it is downloaded and saved to the AppData folder, and then opened. However, the problem I am having is that if the user for example opens Word and uses this new 'load' function, the blank Word document remains, and Word opens the new document quite happily, but it does not get the focus.
(I'm on Windows 7 and it creates a second 'W' icon in the task bar for the new document, but it doesn't switch to it in the same way that Word would if I'd used the normal 'Open' route.)
I have tried (as a result of suggestions found elsewhere on here) either setting the 'visible' attribute to true, and calling doc.Activate() but neither does what I need. What am I missing? The code I'm using to open the file is below:
private void OK_Click(object sender, EventArgs e)
{
this.Close();
FES.FESServices wService = new FES.FESServices();
int request_id = wService.SubmitRequestFromAddIn(username, password, "RETR", "", textBox1.Text, "", "");
FES.FileRequestResponse response = wService.GetFileMembersFromAddIn(username, password, request_id);
if (response.ResponseType == "RETR")
{
byte[] data = wService.GetBytesForFilename(response.ResponseValue);
//MessageBox.Show("Loaded data for file...");
//MessageBox.Show(Application.UserAppDataPath);
FileStream fs = new FileStream(Application.UserAppDataPath + "\\" + response.ResponseValue.Substring(6).Split('~')[0], FileMode.Create, FileAccess.Write);
fs.Write(data, 0, (int)data.Length);
fs.Close();
object oMissing = System.Reflection.Missing.Value;
Microsoft.Office.Interop.Word.Document doc = Globals.ThisAddIn.Application.Documents.Open(
Application.UserAppDataPath + "\\" + response.ResponseValue.Substring(6).Split('~')[0], Visible:true
);
doc.Activate();
}
}
(I have included this.Close() as the function loading the document is held within a modal dialog box, and without closing it first, Word throws an exception about switching documents with a dialog box open).
Any help gratefully received!
Running this code whilst the modal dialog is showing is interfering with window activation.
I'm not sure exactly what the mechanism is for this interference, but the fix is simple enough. Move the code outside the dialog. Execute this code immediately after the call to ShowDialog returns.

Killing all processes generated by a form in C#

I would like to build a list of all processes generated by events in a form and kill them all when the form closes. How do I code this?
When I worked with the Excel Interop we had several processes created by it, and we had problems if we had real Excel workbooks opened at the same time the program was running.
If you kill the process and your user has a workbook opened, you might close their Excel instead. I would suggest getting a list of the PIDs for the processes that have Excel in their name before opening the workbook in your program, then get the list again immediately after opening it, and by seeing what is new determine the PID of your process. When you're done with the workbook, kill the process with the PID you retrieved.
Edit: when you open the workbook in code, a new process is created. Before opening the workbook, you have a list of processes - let's say 10 (by usingpart of the code Harendra wrote). Then you open the workbook, and get the list again - and you'll have 11 processes (you might have more, but only one of those is Excel). By comparing the two lists, you get the ID of the new process, which is your opened workbook. You add to a list or processes opened by you, and when closing the program, kill all the processes from the list.
Try this...
Process[] procList = Process.GetProcesses();
for (int i = 0; i <= procList.Length - 1; i ++) {
string strProcName = procList[i].ProcessName;
string strProcTitle = procList[i].MainWindowTitle();
//check for your process name.. here i m checking excel process
if (strProcName.ToLower().Trim().Contains("excel")) {
procList[i].Kill();
}
}
In continuation with reply from Harendra, you can store all the process ID of process invoked by you ! and close them at the time of closing.
This statement is valid only if you are using Process.Start to start processes

Categories