Ten answer wyjaśnia, w jaki sposób uzyskać obiektu Word.Application z HWND, co oznacza, że możemy pętli wszystkich aktywnych procesów Word i sprawdzić, czy ich Word.Application mecze własną obiektu Word.Application. W ten sposób nie trzeba nic robić z podpisem okna.
Należy pamiętać, że można tylko uzyskać proces w Word.Application, która jest widoczna i ma jeden lub więcej dokumentów otwartych (kod otwiera tymczasowy pusty dokument w drugim przypadku):
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;
}
}
}
I użyj NetOffice w tym przykładzie, ale możesz go łatwo zmienić, aby działał ze standardowymi bibliotekami interopów, edytując instrukcję using i wykonując Marshal.ReleaseComObject() zamiast Word.Application.Dispose().
Tak, nie rób tego. Cokolwiek chcesz zrobić z tym uchwytem, z pewnością jest lepszy sposób. –
Word 2013 i nowsze wersje mają właściwość Application.Hndnd. – Jbjstam