4

I inherited some code that is now not working. It is supposed to use the Process class to start "Excel.exe". But if I break on the next line I can see the my process instance has the error: 'process.BasePriority' threw an exception of type 'System.InvalidOperationException'. The next line of code throws the following exception:

System.InvalidOperationException: Cannot process request because the process has exited.

Here is the code:

private Microsoft.Office.Interop.Excel.Application StartExcel()
{
    // Maximum number of attempts to look for started Excel Application
    const int maxAttempts = 3;
    // Number of milliseconds to wait between attempts to look for started Excel Application
    const int waitTimeMS = 200;

    Microsoft.Office.Interop.Excel.Application result = null;

    // Start Excel
    var process = Process.Start("Excel.exe");
    //process.WaitForInputIdle();

    // Try to find started Excel Application

    int currentAttempt = 1;

    while ((result == null) && (currentAttempt <= maxAttempts))
    {
        // Wait between attempts 
        if (currentAttempt != 1)
        {
            Thread.Sleep(waitTimeMS);
        }

        // List all running Excel automation objects and find the one with the same process id
        IRunningObjectTable lRunningObjectTable = null;
        IEnumMoniker lMonikerList = null;

        try
        {
            // Query Running Object Table 
            if (GetRunningObjectTable(0, out lRunningObjectTable) == 0 && lRunningObjectTable != null)
            {

                // List Monikers
                lRunningObjectTable.EnumRunning(out lMonikerList);

                // Start Enumeration
                lMonikerList.Reset();

                // Array used for enumerating Monikers
                IMoniker[] lMonikerContainer = new IMoniker[1];

                IntPtr lPointerFetchedMonikers = IntPtr.Zero;

                // foreach Moniker
                while (lMonikerList.Next(1, lMonikerContainer, lPointerFetchedMonikers) == 0)
                {
                    object lComObject;
                    lRunningObjectTable.GetObject(lMonikerContainer[0], out lComObject);

                    // Check the object is an Excel workbook
                    if (lComObject is Microsoft.Office.Interop.Excel.Workbook)
                    {
                        Microsoft.Office.Interop.Excel.Workbook lExcelWorkbook = (Microsoft.Office.Interop.Excel.Workbook)lComObject;

                        // Get the Process ID for the Window Handle 
                        uint processId;
                        GetWindowThreadProcessId(new IntPtr(lExcelWorkbook.Application.Hwnd), out processId);

                        if (processId == process.Id)
                        {
                            // Correct automation object found, return Application
                            result = lExcelWorkbook.Application;
                            break;
                        }
                    }
                }
            }
        }
        finally
        {
            // Release ressources
            if (lRunningObjectTable != null) Marshal.ReleaseComObject(lRunningObjectTable);
            if (lMonikerList != null) Marshal.ReleaseComObject(lMonikerList);
        }

        currentAttempt++;
    }
    return result;
}

At the end result = null. Also an excel workbook does appear on my screen but for some reason the process cannot find the Excel Workbooks process ID. I am guessing this is because the process has excited without closing Excel.exe.

It has been running fine for the past year, and just stopped working a couple of days ago. I do not have any experience with the Process class and am not sure what the issue might be as the errors seem very vague. Based on other SO threads it seems it may be exiting. I can not figure out why the process is immediately Exiting.

Edited for clarification.

Mwspencer
  • 1,142
  • 3
  • 18
  • 35
  • 1
    the `InvalidOperationException` is thrown by the `WaitForInputIdle` function, which waits for the message pump of the child process to enter and idle. maybe newer versions of Excel.exe are just a stub to launch Office365, and this does no longer apply. just a guess, therefore not an answer, but can you just drop the `WaitForInputIdle` call? – Cee McSharpface Dec 26 '19 at 21:29
  • @CeeMcSharpface WaitForInputIdle only prolongs the issue, but it still seems the process has exited. Thanks for the response. I have updated the question with the entire method that is being called. – Mwspencer Dec 26 '19 at 21:37
  • Can you not get the process you started from the `Process` object itself? – user1274820 Dec 26 '19 at 21:42
  • this code depends somewhat on implementation details of office. maybe it's broken due to some subtle change in how office instantiates. check if the process id right after process.start is the same one that task manager would give you in the PID column as soon as you get the actual excel window on your desktop. if it is not, then maybe the excel.exe of your installation of office is just a stub and you need to go by one of the alternative window class name approaches that exist. – Cee McSharpface Dec 26 '19 at 21:43
  • @user1274820 correct, the process instance is just an error message 'System.InvalidOperationException. – Mwspencer Dec 26 '19 at 21:44
  • I'm using excel 2016 or whatever and it shows the correct process ID by simply calling `process.Id`. You can also get all running excel instances using `Process[] excelz = Process.GetProcessesByName("excel");` – user1274820 Dec 26 '19 at 21:47
  • But if the goal is to return the excel object then why wouldn't you just open it using `Application excel = new Application();` instead of `Process.Start` at all? – user1274820 Dec 26 '19 at 21:51
  • Is there a reason you can't just delete the entire method and make it `return new Microsoft.Office.Interop.Excel.Application();` ? – user1274820 Dec 26 '19 at 21:54
  • @user1274820 That's a good question, and since I have no experience with the Process class I'm not sure why this choice was made. I can think of two reasons. 1. The person who wrote it over engineered it just try something new. 2. There is a valid reason and removing the "Process" will break other processes that run. – Mwspencer Dec 26 '19 at 21:54
  • @user1274820 That is plan. I just want to understand how to fix this "Process" incase I break other pieces. As I said above I am not sure if the design choice was for necessity or just over engineered. – Mwspencer Dec 26 '19 at 21:56
  • The `Exited` event is raised **because the process has exited**. That's what the event tells you. Generally, this happens with programs written to activate existing instances instead of letting new ones run. See duplicate. – Peter Duniho Jul 12 '21 at 16:24

1 Answers1

3

If I were you I would try replacing the entire method with:

private Microsoft.Office.Interop.Excel.Application StartExcel()
{
    return new Microsoft.Office.Interop.Excel.Application();
}

It's possible that interop will use the wrong version of excel if you have multiple versions installed on the same machine I suppose?

I have two versions and my interop is opening an instance of excel 2013 when Process.Start("excel") opens the newer version?

Maybe that's the reason for all the stuff?

user1274820
  • 7,786
  • 3
  • 37
  • 74
  • 1
    The application ran just fine with calling a Simple Interop.Excel.Application(). I think I'll stick to KISS and just remove this long StartExcel(). Thanks – Mwspencer Dec 26 '19 at 22:05