3

I can access an instance of Excel in memory using Marshal.GetActiveObject. But this always returns the oldest existing instance.

I would like to iterate though all instances and be able to choose the one to link to.

Can anyone help with this please.

ManInMoon
  • 6,795
  • 15
  • 70
  • 133

1 Answers1

4

Try this.

        List<Process> procs = new List<Process>();
        procs.AddRange(Process.GetProcessesByName("excel"));

Edit: There is an article that fully implements this at http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx. GetActiveObject will always return the first object from the table. This is because Office doesn't register new objects. You have to get the application from the child windows.

Edit: This is the code that worked for me.

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

namespace ConsoleApplication1
{        

class Program
{
    [DllImport("Oleacc.dll")]
    public static extern int AccessibleObjectFromWindow(
          int hwnd, uint dwObjectID, byte[] riid,
          ref Microsoft.Office.Interop.Excel.Window ptr);

    public delegate bool EnumChildCallback(int hwnd, ref int lParam);

    [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 bool EnumChildProc(int hwndChild, ref int lParam)
    {
        StringBuilder buf = new StringBuilder(128);
        GetClassName(hwndChild, buf, 128);
        if (buf.ToString() == "EXCEL7")
        {
            lParam = hwndChild;
            return false;
        }
        return true;
    }

    static void Main(string[] args)
    {
        Excel.Application app = new Excel.Application();
        EnumChildCallback cb;
        List<Process> procs = new List<Process>();
        procs.AddRange(Process.GetProcessesByName("excel"));

        foreach (Process p in procs)
        {
            if ((int)p.MainWindowHandle > 0)
            {
                int childWindow = 0;
                cb = new EnumChildCallback(EnumChildProc);
                EnumChildWindows((int)p.MainWindowHandle, cb, ref childWindow);

                if (childWindow > 0)
                {
                    const uint OBJID_NATIVEOM = 0xFFFFFFF0;
                    Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
                    Excel.Window window = null;
                    int res = AccessibleObjectFromWindow(childWindow, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ref window);
                    if (res >= 0)
                    {
                        app = window.Application;
                        Console.WriteLine(app.Name);
                    }
                }
            }
        }

    }
}
Jesse Seger
  • 951
  • 1
  • 13
  • 31
  • Jesse: Yes that's the sort of thing. – ManInMoon Oct 04 '11 at 11:33
  • Jesse: Yes that's the sort of thing. But what argument from procs do I give to Marshal.GetActiveObject()? – ManInMoon Oct 04 '11 at 11:33
  • You should be able to get the process ID from the List. Then do Marshal.GetActiveObject(procID). – Jesse Seger Oct 04 '11 at 11:37
  • I tried that - doesn't like it. I converted ProcID to string too - doesn't like that either. – ManInMoon Oct 04 '11 at 11:48
  • Hmmm... Try to get the MainWindowHandle of the process instead. Marshall.GetActiveObject(process.MainWindowHandle); – Jesse Seger Oct 04 '11 at 12:04
  • It expects a string - so I tried MainWindowHandle.ToString() but get same error: "Invalid class string (Exception from HRESULT: 0x800401F3 (CO_E_CLASSSTRING))"} – ManInMoon Oct 04 '11 at 12:12
  • Sorry... this is a little more involved. I was reading an article at http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx. After you get the main window handle, you need to use [DllImport("Oleacc.dll")] public static extern int AccessibleObjectFromWindow( int hwnd, uint dwObjectID, byte[] riid, ref Microsoft.Office.Interop.Excel.Window ptr); Give it a go. – Jesse Seger Oct 04 '11 at 12:15
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3997/discussion-between-jesse-seger-and-maninmoon) – Jesse Seger Oct 04 '11 at 12:16