我正在使用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窗口的句柄。有没有其他方法来获取我的单词实例的句柄?
答案 0 :(得分: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() );
}
}
获得句柄后,如果愿意,可以恢复标题。
答案 1 :(得分: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类在this excellent response中可用于较早的帖子。我在自己的代码中使用过这个类,没有任何抱怨。
答案 2 :(得分:1)
我将选择的答案保留为正确,因为这是我在写这篇文章时发现的。我之后需要为不同的项目做类似的事情,并发现尝试更新应用程序标题似乎不太可靠(Office 2013与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;
这使用以下自定义比较器来确保进程匹配(找到here)。
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();
}
}
答案 3 :(得分:1)
这个answer解释了如何从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()来轻松改变它以使用标准的互操作库。
答案 4 :(得分: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;
}
}
}
答案 5 :(得分:0)
自2013年以来,您就可以使用Hwnd
公开的Window
的{{1}}属性
Application
Hwnd返回一个整数,该整数指示指定窗口的窗口句柄。
借助此var windowHandle = wordApplication.ActiveWindow.Hwnd;
,您可以使用int
来提供窗口句柄的低级封装。
NativeWindow