6

我正在通过执行以下操作使用 Office 互操作创建 Word 的新实例:

var word = Microsoft.Office.Interop.Word.Application();
word.Visible = true;
word.Activate;

我可以得到这样的窗口句柄:

var wordHandle = Process.GetProcessesByName("winword")[0].MainWindowHandle;

问题是代码的工作假设没有其他 Word 实例正在运行。如果有多个,它不能保证它返回的句柄是针对我已经启动的实例的。我GetForegroundWindow在检测WindowActivate到来自我的对象的事件后尝试使用,但这一切都在设置为最顶层窗口运行的 WPF 应用程序中运行,所以我只是获取 WPF 窗口的句柄。还有其他方法可以获取我的单词实例的句柄吗?

4

6 回答 6

6

不知道为什么需要 Word 句柄,但我之前做过的一种方法是实际更改 Word 窗口标题并搜索它。我这样做是因为我想在控件中托管 Word 应用程序,但那是另一回事了。:)

  var word = new Microsoft.Office.Interop.Word.Application(); 
  word.Visible = true; 
  word.Activate();
  word.Application.Caption = "My Word";

  foreach( Process p in Process.GetProcessesByName( "winword" ) )
  {
    if( p.MainWindowTitle == "My Word" )
    {
      Debug.WriteLine( p.Handle.ToString() );
    }
  }

获得句柄后,您可以根据需要恢复标题。

于 2011-12-29T21:56:12.287 回答
3

我会将我选择的答案保留为正确的,因为这是我在写这篇文章时发现的。从那以后,我需要为不同的项目做类似的事情,并发现尝试更新应用程序标题似乎不太可靠(Office 2013 vs. 2010?谁知道......)。这是我提出的新解决方案,它使窗口标题保持不变。

var startingProcesses = Process.GetProcessesByName("winword").ToList();

var word = new Microsoft.Office.Interop.Word.Application(); 

var allProcesses = Process.GetProcessesByName("winword").ToList();

var processDiff = allProcesses.Except(startingProcesses, new ProcessComparer());

var handle = processDiff.First().MainWindowHandle;

这使用以下自定义比较器来确保进程匹配(在此处找到)。

class ProcessComparer : IEqualityComparer<Process>
{
    public bool Equals(Process x, Process y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x == null || y == null)
        {
            return false;
        }

        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(Process obj)
    {
        return obj.Id.GetHashCode();
    }
}
于 2014-05-08T21:39:24.713 回答
1

您已经获得了所有 Word 进程的列表。您可以遍历此列表,获取每个进程的父 ID 并匹配当前进程,即您自己的创建 Word 实例的应用程序。这大致是我的想法:

IntPtr getChildProcess(string childProcessName)
{
    var currentProcess = Process.GetCurrentProcess();

    var wordProcesses = Process.GetProcessesByName(childProcessName);
    foreach (var childProcess in wordProcesses)
    {
        var parentProcess = ProcessExtensions.Parent(childProcess);
        if (currentProcess.Id == parentProcess.Id)
            return currentProcess.Handle;
    }

    return IntPtr.Zero;
}

ProcessExtensions 类可在此对较早帖子的出色响应中获得。我在自己的代码中使用了这个类并且没有任何抱怨。

于 2011-12-29T22:33:20.420 回答
1

这个答案解释了如何从 hwnd 获取 Word.Application 对象,这意味着我们可以遍历所有活动的 Word 进程并检查它们的 Word.Application 是否与我们自己的 Word.Application 对象匹配。这样,您无需对窗口标题进行任何操作。

请注意,您只能获取可见且打开一个或多个文档的 Word.Application 的进程(代码在后一种情况下打开一个临时的空文档):

using System;
using System.Linq;
using System.Text;
using Word = NetOffice.WordApi;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;

namespace WordHwnd
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var app = new Word.Application() { Visible = true })
            {
                Console.WriteLine(WordGetter.GetProcess(app).MainWindowHandle);
            }

            Console.ReadLine();
        }
    }

    class WordGetter
    {
        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
        private interface IDispatch
        {
        }

        private const uint OBJID_NATIVEOM = 0xFFFFFFF0;
        private static Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");

        [DllImport("Oleacc.dll")]
        private static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out IDispatch ptr);

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

        [DllImport("User32.dll")]
        private static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);

        [DllImport("User32.dll")]
        private static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

        private static bool Find_WwG(int hwndChild, ref int lParam)
        {
            if (GetClassName(hwndChild) == "_WwG")
            {
                lParam = hwndChild;
                return false;
            }
            return true;
        }

        private static string GetClassName(int hwndChild)
        {
            var buf = new StringBuilder(128);
            GetClassName(hwndChild, buf, 128);
            return buf.ToString();
        }

        public static Process GetProcess(Word.Application app)
        {
            Word.Document tempDoc = null;

            //This only works if there is a document open
            if (app.Documents.Count == 0)
                tempDoc = app.Documents.Add();

            var processes = Process.GetProcessesByName("WINWORD");

            var appsAndProcesses = processes
                .Select(p => new { Process = p, App = WordGetter.GetWordApp(p) })
                .Where(x => !Equals(x.App, null));

            Process process = null;

            foreach (var appAndProcess in appsAndProcesses)
            {
                if (appAndProcess.App == app)
                {
                    process = appAndProcess.Process;
                    break;
                }
                else
                {
                    appAndProcess.App.Dispose();
                }
            }

            tempDoc?.Close(false);

            return process;
        }

        public static Word.Application GetWordApp(Process process)
        {
            return GetWordApp(process.MainWindowHandle);
        }

        public static Word.Application GetWordApp(IntPtr hwnd)
        {
            return GetWordApp((int)hwnd);
        }

        public static Word.Application GetWordApp(int hwnd)
        {
            var wwG_Hwnd = 0;

            var callback = new EnumChildCallback(Find_WwG);

            EnumChildWindows(hwnd, callback, ref wwG_Hwnd);

            if (wwG_Hwnd != 0)
            {
                IDispatch iDispatch;

                var result = AccessibleObjectFromWindow(wwG_Hwnd, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out iDispatch);

                if (result >= 0)
                {
                    var obj = iDispatch.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, iDispatch, null);

                    return new Word.Application(null, obj);
                }

                return null;
            }

            return null;
        }
    }
}

我在此示例中使用 NetOffice,但您可以通过编辑 using 语句并执行 Marshal.ReleaseComObject() 而不是 Word.Application.Dispose() 轻松地将其更改为与标准互操作库一起使用。

于 2017-01-02T23:31:42.040 回答
1

从 2013 年开始,您可以使用HwndWindowApplication

var windowHandle = wordApplication.ActiveWindow.Hwnd;

Hwnd 返回一个指示指定窗口的窗口句柄的 Integer。有了这个int,您可以使用NativeWindow提供窗口句柄的低级封装。

var nativeWindow = new NativeWindow();
nativeWindow.AssignHandle(new IntPtr(windowHandle));
于 2019-12-02T20:24:55.970 回答
0

另一种方法,利用注入的宏直接在 WINWORD 进程中运行这一事实:

using System;
using Word = NetOffice.WordApi;
using System.Diagnostics;

namespace WordHwnd
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var app = new Word.Application() { Visible = true })
            {
                var process = GetProcess(app);
                Console.WriteLine(process.MainWindowHandle);

                app.Quit();

            }

            Console.ReadLine();
        }

        private static Process GetProcess(Word.Application app)
        {
            var tempDocument = app.Documents.Add();
            var project = tempDocument.VBProject;
            var component = project.VBComponents.Add(NetOffice.VBIDEApi.Enums.vbext_ComponentType.vbext_ct_StdModule);
            var codeModule = component.CodeModule;
            codeModule.AddFromString("#If Win64 Then\r\n   Declare PtrSafe Function GetCurrentProcessId Lib \"kernel32\" () As Long\r\n#Else\r\n   Declare Function GetCurrentProcessId Lib \"kernel32\" () As Long\r\n#End If");

            var result = app.Run("GetCurrentProcessId");

            var process = Process.GetProcessById((int)result);

            tempDocument.Close(false);

            return process;
        }
    }

}
于 2017-01-04T11:24:33.423 回答