2011-12-29 4 views
6

나는 사무실을 사용하여 Word의 새 인스턴스를 만드는거야를 사용하여 특정 창 핸들을 가져 오기 :이 작업을 수행하여 상호 운용성 오피스 상호 운용성

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

:

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

는이 같은 창 핸들을 얻을 수 있습니다 문제는 코드가 실행중인 다른 Word 인스턴스가 없다는 가정하에 작동한다는 것입니다. 배수가있는 경우 반환 한 핸들이 시작한 인스턴스에 대한 핸들임을 보장 할 수 없습니다. 나는 내 개체에서 WindowActivate 이벤트를 감지 한 후 GetForegroundWindow을 사용해 보았습니다.하지만 이것은 최상위 창으로 실행되도록 설정된 WPF 응용 프로그램 내에서 모두 실행되므로 WPF 창에 대한 핸들을 얻습니다. 내 말의 인과 관계를 처리 할 수있는 다른 방법이 있습니까?

+0

예는, 그렇게하지 않습니다. 그 핸들로 무엇을하고 싶든, 분명히 더 좋은 방법이 있습니다. –

+0

Word 2013 이상에는 Application.Hwnd 속성이 있습니다. – Jbjstam

답변

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

, 당신은 캡션을 복원 할 수 있습니다.

+0

그래, 왜이 작업을 수행해야하는지는 또 다른 이야기입니다. 그러나 당신의 제안은 내가가는 곳을 알 수있을뿐만 아니라 실제로 전체 커스텀 캡션을 조금 더 멋지게 만들 것입니다. – HotN

+1

Eddie Paz와 동의하지만 한 가지 변경 사항이 있음 : 단어가 다른 문자를 추가하기 때문에 (p.MainWindowTitle.Contains ("My Word"))를 확인하십시오. –

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에서 사용할 수 있습니다 : 이것은 내가 생각하고있는 것을 대략이다. 나는이 클래스를 내 자신의 코드에서 사용했으며 아무런 불만도 없었다.

+0

참고로 연결된 질문에는 ProcessExtensions보다 더 나은 옵션 인 것으로 보이는 또 다른 대답이 있습니다. – Chris

1

올바른 답으로 남겨 두었습니다. 필자가이 책을 쓸 때 돌아 왔기 때문입니다. 나는 다른 프로젝트와 비슷한 것을 할 필요가 있었고 응용 프로그램 캡션을 업데이트하려고 시도하는 것이 신뢰성이 떨어지는 것으로 나타났습니다 (Office 2013 vs. 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(); 
    } 
} 
0

answer는 Word.Application가 우리 자신의 Word.Application 개체를 일치하는 경우 우리는 모든 활성 워드 프로세스를 통해 루프를 확인하실 수 있습니다 의미하는 HWND에서 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; 
     } 
    } 
} 

I을 이 예제에서는 NetOffice를 사용하지만 using 문을 편집하고 Word.Application.Dispose() 대신 Marshal.ReleaseComObject()를 사용하여 표준 interop 라이브러리와 함께 작동하도록 쉽게 변경할 수 있습니다.

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

}