2

Using this,

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern IntPtr GetForegroundWindow();

I retrieve handle for a process then,

IntPtr hwnd = GetForegroundWindow();
int pid = APIFuncs.GetWindowProcessID(hwnd);
Process p = Process.GetProcessById(pid);
string appName = p.ProcessName;

So whenever I find appName = "WINWORD", I would like to retrieve Word application object using hwnd

NOTE: I don't want to create new instance of word, just get running one.

Aniket Bhansali
  • 630
  • 12
  • 33

1 Answers1

5

Given a window handle, you get an automatable Word application instance using the AccessibleObjectFromWindow function from oleacc.dll.

Here is a sample program showing how to use it (add a reference to Microsoft.Office.Interop.Word.dll):

using Microsoft.Office.Interop.Word;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace WordLateBindingSample
{
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
    public interface IDispatch
    {
    }

    class Program
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("Oleacc.dll")]
        static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out IDispatch 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() == "_WwG")
            {
                lParam = hwndChild;
                return false;
            }
            return true;
        }

        static void Main(string[] args)
        {
            // Use the window class name ("OpusApp") to retrieve a handle to Word's main window.
            // Alternatively you can get the window handle via the process id:
            // int hwnd = (int)Process.GetProcessById(wordPid).MainWindowHandle;
            //
            int hwnd = (int)FindWindow("OpusApp", null);

            if (hwnd != 0)
            {
                int hwndChild = 0;

                // Search the accessible child window (it has class name "_WwG") 
                // as described in http://msdn.microsoft.com/en-us/library/dd317978%28VS.85%29.aspx
                //
                EnumChildCallback cb = new EnumChildCallback(EnumChildProc);
                EnumChildWindows(hwnd, cb, ref hwndChild);

                if (hwndChild != 0)
                {
                    // We call AccessibleObjectFromWindow, passing the constant OBJID_NATIVEOM (defined in winuser.h) 
                    // and IID_IDispatch - we want an IDispatch pointer into the native object model.
                    //
                    const uint OBJID_NATIVEOM = 0xFFFFFFF0;
                    Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
                    IDispatch ptr;

                    int hr = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out ptr);

                    if (hr >= 0)
                    {
                        var wordApp = (Application)ptr.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ptr, null);

                        var version = wordApp.Version;
                        Console.WriteLine(string.Format("Word version is: {0}", version));
                    }
                }
            }
        }
    }
}
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • Really successful in case of Word, but unfortunately I am unable to retrieve Outlook/Excel/Powerpoint using same code. Do we need to change Guid IID_IDispatch? – Aniket Bhansali Jul 26 '17 at 10:42
  • 2
    @AniketBhansali: An example for Excel is available [here](https://stackoverflow.com/a/779710/40347), an example for PowerPoint at the end of this artice: [Launching Office Apps Programmatically](https://blogs.msdn.microsoft.com/andreww/2008/11/30/launching-office-apps-programmatically/) – Dirk Vollmar Jul 26 '17 at 11:40
  • Thanks, got solution in case of Excel, but not getting Accessible doucment window class name for Powerpoint and Outlook, I tried using **paneClassDC** for Powerpoint but not working. – Aniket Bhansali Jul 28 '17 at 05:58
  • Not yet, went through link posted by @DirkVollmar but unfortunately, unable to crack it for Outlook & Powerpoint. – Aniket Bhansali Aug 17 '17 at 07:06
  • 2
    Outlook is not mentioned in the docs at https://msdn.microsoft.com/en-us/library/windows/desktop/dd317978(v=vs.85).aspx so I highly doubt that it is supported. Only Word, Excel and PowerPoint are supported – Dirk Vollmar Aug 17 '17 at 07:47
  • 1
    For additional Info, You may use above logic for Word, Excel or Access but not for Powerpoint or Outlook because their COM servers are Multiuse (Single Instance) unlike Single Use (Multiple Instances) of Word/Excel/Access, Refer https://support.microsoft.com/en-us/help/316126/how-to-use-visual-c-to-automate-a-running-instance-of-an-office-progra – Aniket Bhansali Apr 06 '18 at 07:22