0

My situation is that I'm developing a C# application which is launching an instance of a Microsoft Office Excel Application.
I change some of the Form's functions so that the instantiated Excel Application is being killed and cleaned up from memory when my Form's being closed.

What I want to do is to perform the opposite. I'd like my instance of Excel to close my windows Form when it's being exited.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jsncrdnl
  • 3,005
  • 5
  • 28
  • 43
  • Poll running processes and simply exit when the Excel process ends? – CodingBarfield Jan 18 '12 at 09:58
  • 1
    If you're using Visual Studio 2008 or 2010, you could bypass the Form completely and create an Excel Addin - http://msdn.microsoft.com/en-us/library/cc668205.aspx – JMK Jan 18 '12 at 10:00
  • I know it by that's not the point of my application. I need to do it this way. What I don't know is how to set up a listener that's gonna close my C# application when the C#'s instantiated application is being exited... – Jsncrdnl Jan 18 '12 at 10:09

3 Answers3

1

Ok, I haven't even tried this so I'm not sure if it will work, but you could try something along these lines:

Microsoft.Office.Interop.Excel.Application excel = ...;

System.Diagnostics.Process excelProcess = null;
var processes = System.Diagnostics.Process.GetProcesses();

foreach (var process in processes)
{
        if (process.Handle.ToInt32() == excel.Hwnd)
            excelProcess = process;
}

excelProcess.Exited += new EventHandler(excelProcess_Exited);

UPDATE

Try the following code (its not pretty):

Microsoft.Office.Interop.Excel.Application xlsApp;
Process xlsProcess;
Timer timer;

public Form1()
{
    xlsApp = new Microsoft.Office.Interop.Excel.Application();
    xlsApp.Visible = true;

    foreach (var p in Process.GetProcesses()) //Get the relevant Excel process.
    {
        if (p.MainWindowHandle == new IntPtr(xlsApp.Hwnd))
        {
            xlsProcess = p;
            break;
        }
    }

    if (xlsProcess != null)
    {
        timer = new Timer();
        timer.Interval = 500;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Start();
    }
}

void timer_Tick(object sender, EventArgs e)
{
    if (xlsApp != null && !xlsApp.Visible)
    {
        //Clean up to make sure the background Excel process is terminated.
        xlsApp.Quit();
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsApp);
        xlsApp = null;
    }

    if (xlsProcess.HasExited)
    {
        //The event is not fired but the property is eventually set once the process is terminated
        this.timer.Dispose();
        this.Close();
    }
}

The problem when working with an Excel Application is that even if the user closes it's main window, the process will keep on running in the background. In order to release it completely you have to call Application.Quit but you should only do that when you detect the user closing the window which is exactly the problem you are trying to solve...you have a circular reference here :D

Latching to the relevant System.Diagnostics.Process doesn't seem to work either, as the Exited event never gets fired (not even if you terminate the background process from the task manager). My guess is that this event is fired only with processes launched through process = Process.Start(...).

So the only solution I can see is polling if Excel`s main window is visible. Once it is not, that probably means the user closed the main window and the Excel application should be terminated (if there is some scenario where Excel's main window's visibility can be false other than when the user wants to terminate the process then this solution is not valid). From there it is pretty straightforward.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • I agree with you. The only problem is that I ain't got admin rights at launch whose are needed to perform this action `process.Handle.ToInt32()` Isn't there any way to do it without needing admin rights ?? – Jsncrdnl Jan 18 '12 at 11:05
  • @Kuroicloud666: Check update. Its not pretty but it seems to work. – InBetween Jan 18 '12 at 12:22
1

This is what i did for the same scenario.

I created a process within the class

System.Diagnostics.Process myProcess = new System.Diagnostics.Process();

Then a delegate to method which closes the app

delegate void KillProcess();
KillProcess aKillProcess;

Setting the delegate to the app closing method

aKillProcess = CloseApp;

This below code is written inside the button click event This opens the excel. Here subscribe for the Exited event of the process

myProcess.StartInfo = new System.Diagnostics.ProcessStartInfo (@"D:\SomeExcel.xlsx");
myProcess.Start();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(ProcessKilled);

On clicking close button of the excel, this method will be called

private void ProcessKilled(object sender, EventArgs e)
{
    if (this.InvokeRequired)
        this.Invoke(aKillProcess);
    else
        this.Close();
}

void CloseApp()
{
   this.Close();
}

This will close the app.

Hope this helps

Manjunath K Mayya
  • 1,078
  • 1
  • 11
  • 20
  • i have added the delegate since on closing the form directly, "Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on." exception was thrown. – Manjunath K Mayya Jan 18 '12 at 10:25
  • The problem with your solutions seems is that I won't be able to use interaction between my application and the launched process because it's not a "Microsoft.Office.Interop.Excel.Application" that is launched but a simple process. I don't have any control on it at this point... – Jsncrdnl Jan 18 '12 at 11:00
0

Your first try would have worked, just needed to enavle raising of events for the process, this is what I did to catch a launched visio exit or crash.

                    foreach (var process in processes)
                    {
                        try
                        {
                            if (process.MainWindowHandle == new IntPtr(app.WindowHandle32))
                                visioProcess = process;
                        }
                        catch
                        {

                        }
                    }

                    visioProcess.EnableRaisingEvents = true;

                    visioProcess.Exited += new EventHandler(visioProcess_Exited);