2011-12-29 12 views
6

Tworzę nową instancję programu Word przy użyciu pakietu Office współdziałanie w ten sposób:Szczegółowe uchwyt okna za pomocą pakietu Office współdziałanie

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

mogę uzyskać uchwyt okna tak:

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

Problem polega na tym, że kod działa przy założeniu, że nie działa żadne inne wystąpienie programu Word. Jeśli jest ich wiele, nie może zagwarantować, że uchwyt, który zwróci, dotyczy instancji, którą uruchomiłem. Próbowałem używać GetForegroundWindow po wykryciu zdarzenia WindowActivate z mojego obiektu, ale to wszystko działa w aplikacji WPF, która jest uruchamiana jako najwyższe okno, więc po prostu dostaję uchwyt do okna WPF. Czy są jakieś inne sposoby na uchwycenie mojej instancji słowa?

+0

Tak, nie rób tego. Cokolwiek chcesz zrobić z tym uchwytem, ​​z pewnością jest lepszy sposób. –

+0

Word 2013 i nowsze wersje mają właściwość Application.Hndnd. – Jbjstam

Odpowiedz

6

Nie wiem, dlaczego potrzebujesz uchwytu do Worda, ale jednym ze sposobów, w jaki to robiłem, jest zmiana podpisu okna programu Word i poszukiwanie go. Zrobiłem to, ponieważ chciałem hostować aplikację Word wewnątrz kontrolki, ale to już inna 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()); 
    } 
    } 

Po uzyskaniu klamki można przywrócić podpis, jeśli chcesz.

+0

Taa, dlaczego muszę to zrobić, to także inna historia. Ale myślę, że twoja sugestia nie tylko doprowadzi mnie do celu, ale sprawi, że całe to przeżycie będzie trochę przyjemniejsze dzięki niestandardowemu napisowi. – HotN

+1

Zgadzam się z Eddie Paz ale z jedną zmianą: należy sprawdzić, czy (p.MainWindowTitle.Contains ("Moje słowo")), ponieważ słowo dodaje kilka innych liter na początku tego. –

1

Otrzymujesz już listę wszystkich procesów programu Word. Możesz wykonać iterację na tej liście, uzyskać identyfikator nadrzędny każdego procesu i dopasowanie w odniesieniu do bieżącego procesu, tj. Własną aplikację, która utworzyła instancję programu Word. To jest mniej więcej to, co mam na myśli:

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

Klasa ProcessExtensions jest dostępny w this excellent response do wcześniejszego postu. Użyłem tej klasy w moim własnym kodzie i nie miałem żadnych skarg.

+0

FYI połączone pytanie ma inną odpowiedź, która wydaje się lepszą opcją niż ProcessExtensions. – Chris

1

Zostawię odpowiedź, którą wybrałem jako poprawną, ponieważ to było to, co znalazłem do pracy, kiedy to pisałem. Od tego czasu musiałem zrobić coś podobnego dla innego projektu i okazało się, że próba zaktualizowania podpisu aplikacji wydaje się mniej niezawodna (Office 2013 vs. 2010? Kto wie ...). Oto nowe rozwiązanie, które wymyśliłem, które pozostawia niezmienione podpisy pod Windowsem.

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; 

wykorzystuje następujące niestandardowy comparer zapewnienia rozpoczęcia sposobami (znaleziono 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

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().

0

Inną metodą, wykorzystując fakt, że wtryskiwanego makra są uruchamiane bezpośrednio w procesie 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; 
     } 
    } 

} 
Powiązane problemy