Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Using Process.Start on Excel

Using Process.Start on Excel

Scheduled Pinned Locked Moved C#
helpcomdebuggingquestion
6 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mjackson11
    wrote on last edited by
    #1

    I am trying to use Process.Start to launch Excel. If I immediately grab the process returned by the Start method, any addins or extensions to Excel (xlls or xlam) will not work. If I wait until Excel finishes opening and loading all the addins, then grab the process, it works. Need them to work for this project. I mashed up a class from code I found around to either grab a reference to an existing instance of Excel or to start a new one if it isn't running. Unfortunately, it doesn't work. If the Process.Start method is called, Excel starts loading. The WaitForInputIdle method waits for the message loop to start. But when the message loop starts, Excel is still loading things and has not created any child windows. So the class tries to enumerate the child windows but there are none. And I discovered that once you call EnumChildWindows() it "freezes" things in that it will not refer to a child window created subsequently even if you call it again after the child window is created. So loops to wait are out. One solution is to recursively create new instances of the class to get around the EnumChildWindows problem, but I end up with 1000's of these instances. As a kludge, I have a long Thread.Sleep in the code to wait until everything is open. It works correctly but I would like to know if there is a better way to determine if a process has finished opening, especially given the bug/feature in EnumChildWindows.

    public class ExcelInteropService
    {
    	private const string EXCEL\_CLASS\_NAME = "EXCEL7";
    
    	private const uint DW\_OBJECTID = 0xFFFFFFF0;
    
    	private static Guid rrid = new Guid("{00020400-0000-0000-C000-000000000046}");
    
    	public delegate bool EnumChildCallback(int hwnd, ref int lParam);
    
    	\[DllImport("Oleacc.dll")\]
    	public static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte\[\] riid, ref Microsoft.Office.Interop.Excel.Window ptr);
    
    	\[DllImport("User32.dll")\]
    	public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);
    
    	\[DllImport("User32.dll")\]
    	public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);
    
    	public static Microsoft.Office.Interop.Excel.Application GetExcelInterop(int? processId = null)
    	{
    		var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
    		p.WaitForInputIdle();
    		System.Threading.Thread.Sleep(60000);
    		Debug.Assert(p != null, "p != null");
    		try {
    			return new ExcelInte
    
    Richard DeemingR B 2 Replies Last reply
    0
    • M mjackson11

      I am trying to use Process.Start to launch Excel. If I immediately grab the process returned by the Start method, any addins or extensions to Excel (xlls or xlam) will not work. If I wait until Excel finishes opening and loading all the addins, then grab the process, it works. Need them to work for this project. I mashed up a class from code I found around to either grab a reference to an existing instance of Excel or to start a new one if it isn't running. Unfortunately, it doesn't work. If the Process.Start method is called, Excel starts loading. The WaitForInputIdle method waits for the message loop to start. But when the message loop starts, Excel is still loading things and has not created any child windows. So the class tries to enumerate the child windows but there are none. And I discovered that once you call EnumChildWindows() it "freezes" things in that it will not refer to a child window created subsequently even if you call it again after the child window is created. So loops to wait are out. One solution is to recursively create new instances of the class to get around the EnumChildWindows problem, but I end up with 1000's of these instances. As a kludge, I have a long Thread.Sleep in the code to wait until everything is open. It works correctly but I would like to know if there is a better way to determine if a process has finished opening, especially given the bug/feature in EnumChildWindows.

      public class ExcelInteropService
      {
      	private const string EXCEL\_CLASS\_NAME = "EXCEL7";
      
      	private const uint DW\_OBJECTID = 0xFFFFFFF0;
      
      	private static Guid rrid = new Guid("{00020400-0000-0000-C000-000000000046}");
      
      	public delegate bool EnumChildCallback(int hwnd, ref int lParam);
      
      	\[DllImport("Oleacc.dll")\]
      	public static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte\[\] riid, ref Microsoft.Office.Interop.Excel.Window ptr);
      
      	\[DllImport("User32.dll")\]
      	public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);
      
      	\[DllImport("User32.dll")\]
      	public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);
      
      	public static Microsoft.Office.Interop.Excel.Application GetExcelInterop(int? processId = null)
      	{
      		var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
      		p.WaitForInputIdle();
      		System.Threading.Thread.Sleep(60000);
      		Debug.Assert(p != null, "p != null");
      		try {
      			return new ExcelInte
      
      Richard DeemingR Offline
      Richard DeemingR Offline
      Richard Deeming
      wrote on last edited by
      #2

      mjackson11 wrote:

      grab a reference to an existing instance of Excel or to start a new one if it isn't running

      I haven't done this for a while, but I think you can replace all of that code with:

      using System;
      using System.Runtime.InteropServices;
      using Excel = Microsoft.Office.Interop.Excel;

      public static class ExcelInteropService
      {
      public static Excel.Application FindOrStartExcel()
      {
      try
      {
      return (Excel.Application)Marshal.GetActiveObject("Excel.Application");
      }
      catch (COMException)
      {
      }

          // Excel is not in the ROT; start a new instance:
          return new Excel.Application();
      }
      

      }


      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

      M 1 Reply Last reply
      0
      • Richard DeemingR Richard Deeming

        mjackson11 wrote:

        grab a reference to an existing instance of Excel or to start a new one if it isn't running

        I haven't done this for a while, but I think you can replace all of that code with:

        using System;
        using System.Runtime.InteropServices;
        using Excel = Microsoft.Office.Interop.Excel;

        public static class ExcelInteropService
        {
        public static Excel.Application FindOrStartExcel()
        {
        try
        {
        return (Excel.Application)Marshal.GetActiveObject("Excel.Application");
        }
        catch (COMException)
        {
        }

            // Excel is not in the ROT; start a new instance:
            return new Excel.Application();
        }
        

        }


        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

        M Offline
        M Offline
        mjackson11
        wrote on last edited by
        #3

        Unfortunately, the new Excel.Application call is buggy. The Excel instance that is launched will show the addins as being loaded but the addins cannot be used from a vba macro.

        Richard DeemingR 1 Reply Last reply
        0
        • M mjackson11

          I am trying to use Process.Start to launch Excel. If I immediately grab the process returned by the Start method, any addins or extensions to Excel (xlls or xlam) will not work. If I wait until Excel finishes opening and loading all the addins, then grab the process, it works. Need them to work for this project. I mashed up a class from code I found around to either grab a reference to an existing instance of Excel or to start a new one if it isn't running. Unfortunately, it doesn't work. If the Process.Start method is called, Excel starts loading. The WaitForInputIdle method waits for the message loop to start. But when the message loop starts, Excel is still loading things and has not created any child windows. So the class tries to enumerate the child windows but there are none. And I discovered that once you call EnumChildWindows() it "freezes" things in that it will not refer to a child window created subsequently even if you call it again after the child window is created. So loops to wait are out. One solution is to recursively create new instances of the class to get around the EnumChildWindows problem, but I end up with 1000's of these instances. As a kludge, I have a long Thread.Sleep in the code to wait until everything is open. It works correctly but I would like to know if there is a better way to determine if a process has finished opening, especially given the bug/feature in EnumChildWindows.

          public class ExcelInteropService
          {
          	private const string EXCEL\_CLASS\_NAME = "EXCEL7";
          
          	private const uint DW\_OBJECTID = 0xFFFFFFF0;
          
          	private static Guid rrid = new Guid("{00020400-0000-0000-C000-000000000046}");
          
          	public delegate bool EnumChildCallback(int hwnd, ref int lParam);
          
          	\[DllImport("Oleacc.dll")\]
          	public static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte\[\] riid, ref Microsoft.Office.Interop.Excel.Window ptr);
          
          	\[DllImport("User32.dll")\]
          	public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);
          
          	\[DllImport("User32.dll")\]
          	public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);
          
          	public static Microsoft.Office.Interop.Excel.Application GetExcelInterop(int? processId = null)
          	{
          		var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
          		p.WaitForInputIdle();
          		System.Threading.Thread.Sleep(60000);
          		Debug.Assert(p != null, "p != null");
          		try {
          			return new ExcelInte
          
          B Offline
          B Offline
          Bernhard Hiller
          wrote on last edited by
          #4

          What do you actually want to achieve? To me, your approach looks line an "XY problem": you do not describe the problem proper, but your idea of a solution which failed somewhere, and now you ask us how to procede on your wrong way. Let's get a few steps back and find the correct way to cope with it.

          1 Reply Last reply
          0
          • M mjackson11

            Unfortunately, the new Excel.Application call is buggy. The Excel instance that is launched will show the addins as being loaded but the addins cannot be used from a vba macro.

            Richard DeemingR Offline
            Richard DeemingR Offline
            Richard Deeming
            wrote on last edited by
            #5

            According to this SO question[^], you need to loop through the installed add-ins, set the Installed property to false, and then back to true. Alternatively, this blog post[^] suggests that you should use the Application.AddIns.Add method to load the add-ins, and explains why they're not loaded when you use new Excel.Application().


            "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

            "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

            M 1 Reply Last reply
            0
            • Richard DeemingR Richard Deeming

              According to this SO question[^], you need to loop through the installed add-ins, set the Installed property to false, and then back to true. Alternatively, this blog post[^] suggests that you should use the Application.AddIns.Add method to load the add-ins, and explains why they're not loaded when you use new Excel.Application().


              "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

              M Offline
              M Offline
              mjackson11
              wrote on last edited by
              #6

              OMG, I spent two days on Google looking for this. Thank you thank you thank you. It was driving me nuts.

              Mark Jackson

              1 Reply Last reply
              0
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • World
              • Users
              • Groups