这个答案解释了如何从 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() 轻松地将其更改为与标准互操作库一起使用。