2011-12-29 9 views
6

Sto creando una nuova istanza di Word utilizzando l'Ufficio di interoperabilità in questo modo:Ottenere specifici maniglia della finestra utilizzando Office interoperabilità

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

posso ottenere una maniglia finestra come questa:

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

Il problema è che il codice funziona supponendo che non ci sia altra istanza di Word in esecuzione. Se sono multipli, non può garantire che l'handle restituito sia per l'istanza che ho lanciato. Ho provato a utilizzare GetForegroundWindow dopo aver rilevato un evento WindowActivate dal mio oggetto, ma tutto questo è in esecuzione all'interno di un'applicazione WPF che è impostata per essere eseguita come finestra più in alto, quindi ho appena ottenuto l'handle della finestra di WPF. Ci sono altri modi per ottenere l'handle per la mia istanza di parole?

+0

Sì, non lo fanno. Qualunque cosa tu voglia fare con quella maniglia, sicuramente c'è un modo migliore. –

+0

Word 2013 e versioni successive ha una proprietà Application.Hwnd – Jbjstam

risposta

6

Non sono sicuro del motivo per cui è necessario l'handle per Word, ma in un modo che ho fatto prima è in realtà modificare la didascalia della finestra di Word e cercarla. L'ho fatto perché volevo ospitare l'applicazione Word all'interno di un controllo, ma questa è un'altra storia. :)

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 volta ottenuto il comando, è possibile ripristinare la didascalia, se lo si desidera.

+0

Sì, perché ho bisogno di farlo è un'altra storia. Ma penso che il tuo suggerimento non solo mi porti dove sto andando, ma in realtà rende l'intera esperienza che sto andando un po 'meglio con la didascalia personalizzata. – HotN

+1

D'accordo con Eddie Paz ma con una modifica: devi controllare se (p.MainWindowTitle.Contains ("My Word")) perché la parola aggiunge altre lettere all'inizio. –

1

Si sta già ottenendo un elenco di tutti i processi di Word. È possibile eseguire iterazioni in questo elenco, ottenere l'ID padre di ciascun processo e la corrispondenza è rispetto al processo corrente, vale a dire la propria applicazione che ha creato un'istanza di Word. Questo è più o meno quello che ho in 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 classe ProcessExtensions è disponibile in this excellent response ad un precedente post. Ho usato questa classe nel mio codice e non ho avuto lamentele.

+0

FYI la domanda collegata ha un'altra risposta che sembra essere un'opzione migliore di ProcessExtensions. – Chris

1

Lascerò la risposta che ho selezionato come corretta, poiché è stato quello che ho trovato a tornare indietro quando ho scritto questo. Da allora ho dovuto fare qualcosa di simile per un progetto diverso e ho scoperto che cercare di aggiornare la didascalia dell'applicazione sembrava essere meno affidabile (Office 2013 vs. 2010? Chissà ...). Ecco la nuova soluzione che ho trovato che lascia intatti i sottotitoli delle finestre.

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; 

Questo utilizza il seguente di confronto personalizzato per garantire la corrispondenza dei processi (che si trova 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

Questo answer spiega come ottenere l'oggetto Word.Application da un hwnd, che significa che possiamo scorrere tutti i processi attivi di Word e verificare se il loro Word.Application corrisponde il nostro oggetto Word.Application. In questo modo, non devi fare nulla con la didascalia della finestra.

Si prega di notare che è possibile ottenere solo il processo di una Word.Application che è visibile e ha uno o più documenti aperti (il codice apre un documento vuoto temporanea in quest'ultimo 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 utilizzare NetOffice in questo esempio, ma è possibile modificarlo facilmente per lavorare con le librerie di interoperabilità standard modificando l'istruzione using e facendo Marshal.ReleaseComObject() anziché Word.Application.Dispose().

0

altro metodo, sfruttando il fatto che iniettato macro è gestita direttamente all'interno del processo 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; 
     } 
    } 

} 
Problemi correlati