2011-12-29 12 views
6

estoy creando una nueva instancia de Word con la Oficina de interoperabilidad al hacer esto:Obtener identificador de ventana específica utiliza la interoperabilidad Oficina

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

yo puede conseguir a un identificador de ventana como esta:

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

El problema es que el código funciona bajo la suposición de que no hay otra instancia de Word en ejecución. Si hay varias, no puede garantizar que el identificador que devuelve sea para la instancia que inicié. Intenté usar GetForegroundWindow después de detectar un evento WindowActivate desde mi objeto, pero todo esto se está ejecutando dentro de una aplicación WPF que está configurada para ejecutarse como la ventana superior, por lo que solo obtengo el identificador de la ventana de WPF. ¿Hay alguna otra forma de obtener el control para mi instancia de palabra?

+0

Sí, no hacen eso. Lo que sea que quieras hacer con ese mango, seguramente hay una mejor manera. –

+0

Word 2013 y posterior tiene una propiedad Application.Hwnd – Jbjstam

Respuesta

6

No estoy seguro de por qué necesita el controlador para Word, pero una forma en que lo he hecho antes es cambiar realmente el título de la ventana de Word y buscarlo. Hice esto porque quería alojar la aplicación Word dentro de un control, pero esa es otra historia. :)

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()); 
    } 
    } 

Una vez que obtenga el mango, puede restaurar el título si lo desea.

+0

Sí, por qué tengo que hacer esto es otra historia también. Pero creo que tu sugerencia no solo me lleva a donde voy, sino que realmente hace que la experiencia completa sea un poco más agradable con el pie de foto personalizado. – HotN

+1

Esté de acuerdo con Eddie Paz, pero con un cambio: debe verificar si (p.MainWindowTitle.Contains ("My Word")) porque Word agrega algunas otras letras al comienzo de eso. –

1

Ya está obteniendo una lista de todos los procesos de Word. Puede repetir esta lista, obtener el ID padre de cada proceso y la coincidencia con el proceso actual, es decir, su propia aplicación que creó una instancia de Word. Esto es más o menos lo que tengo en mente:

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; 
} 

La clase ProcessExtensions está disponible en this excellent response a un post anterior. He usado esta clase en mi propio código y no he tenido quejas.

+0

FYI la pregunta vinculada tiene otra respuesta que parece ser una mejor opción que ProcessExtensions. – Chris

1

Voy a dejar la respuesta que he seleccionado como correcta, ya que fue lo que encontré para trabajar cuando escribí esto. Desde entonces, he tenido que hacer algo similar para un proyecto diferente y descubrí que tratar de actualizar la leyenda de la aplicación parecía ser menos confiable (Office 2013 vs. 2010? Quién sabe ...). Aquí está la nueva solución que he encontrado que deja intactos los títulos de las ventanas.

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; 

Este utiliza el siguiente comparador de encargo para asegurar el partido procesos (que se encuentra 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(); 
    } 
} 
0

Este answer explica cómo obtener el objeto Word.Application de un hwnd, lo que significa que podemos recorrer todos los procesos de palabra activa y comprobar si su Word.Application coincide con nuestro propio objeto Word.Application. De esta forma, no necesita hacer nada con el título de la ventana.

Tenga en cuenta que sólo se puede obtener el proceso de una Word.Application que es visible y tiene uno o más documentos abiertos (el código abre un documento vacío temporal en el último caso):

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 use NetOffice en este ejemplo, pero puede modificarlo fácilmente para que funcione con las bibliotecas de interoperabilidad estándar editando la instrucción using y haciendo Marshal.ReleaseComObject() en lugar de Word.Application.Dispose().

0

Otro método, haciendo uso del hecho de que las macros se ejecutan inyecta directamente en el proceso de 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; 
     } 
    } 

} 
Cuestiones relacionadas