2013-07-17 3 views
0

UI 자동화 요소를 구현하지 않는 프로그램의 UI로 작업해야하는 응용 프로그램을 만들고 있습니다 (Inspect.Exe는 기본 창과 아이들 없음).스레드 입력을 대상 프로세스에 연결 한 후 SendInput이 작동하지 않습니다.

그래서 필요한 기능을 구현하는 가장 좋은 방법은 무엇인지 조사하고 sendInput()이 keybd_event()와 mouse_event()의 최신 버전임을 확인했습니다.

그러나 키보드 포커스가 필요하고 타겟 윈도우를 포어 그라운드로 설정할 여유가 없으므로 (실행하는 동안 사용자를 괴롭히지 않도록) 나는 this answer을 찾을 때까지 검색을 계속했습니다. 나는 Skurmedel이 말한 것을했고, 내 응용 프로그램의 스레드를 대상의 창 스레드에 연결했습니다. 그러나 이제 SetFocus()를 대상으로 보낸 다음 SendInput()을 실행하더라도 대상 창이 영향을받지 않습니다.

제 질문은 "왜이 기능이 작동하지 않습니까?"입니다. 또는 "? 내가 뭘 잘못하고있는 중이 야",하지만 난 코드 예제이 밖으로 정렬 도움이 될 것 같아요

ThreadHandler 클래스

class ThreadHandler 
{ 
    #region P/Invoking and constants definition 
    const uint WM_GETTEXT = 0x000D; 

    [DllImport("user32.dll")] 
    static extern IntPtr SetFocus(IntPtr hWnd); 

    [DllImport("user32.dll")] 
    private static extern int GetWindowThreadProcessId(IntPtr hWnd, uint lpdwProcessId = 0); 

    delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam); 
    [DllImport("user32.dll")] 
    static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, 
     IntPtr lParam); 

    [DllImport("user32.dll")] 
    static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach); 

    [DllImport("kernel32.dll")] 
    static extern int GetCurrentThreadId(); 

    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, StringBuilder lParam); 

    #endregion 

    public readonly string ProcessName, WindowName; 
    protected readonly int TargetThreadID, CurrentThreadID; 
    protected readonly IntPtr TargetWindowHandle; 

    public ThreadHandler(string processName, string windowName) 
    { 
     CurrentThreadID = GetCurrentThreadId(); 
     ProcessName = processName; 
     WindowName = windowName; 

     object[] objs = GetWindowThread(processName, windowName); 
     if (objs == null) 
     { 
      throw new ArgumentException("Could not find the specified process/window."); 
     } 

     TargetThreadID = (int)objs[0]; 
     TargetWindowHandle = (IntPtr)objs[1]; 
    } 

    public ThreadHandler(string processName) 
    { 
     CurrentThreadID = GetCurrentThreadId(); 
     ProcessName = processName; 

     var processes = Process.GetProcessesByName(ProcessName); 
     if (processes.Length == 0) 
     { 
      throw new ArgumentException("Could not find the specified process."); 
     } 
     var appProc = processes[0]; 

     WindowName = appProc.MainWindowTitle; 
     TargetThreadID = GetWindowThreadProcessId(appProc.MainWindowHandle); 
     TargetWindowHandle = appProc.MainWindowHandle; 
    } 

    public bool AttachThreadInput() 
    { 
     return AttachThreadInput(CurrentThreadID, TargetThreadID, true); 
    } 

    public bool DetachThreadInput() 
    { 
     return AttachThreadInput(CurrentThreadID, TargetThreadID, false); 
    } 

    public void SetFocus() 
    { 
     SetFocus(TargetWindowHandle); 
    } 

    static object[] GetWindowThread(string processName, string windowName) 
    { 
     var processes = Process.GetProcessesByName(processName); 
     if (processes.Length > 0) 
     { 
      //Fill a list of handles 
      var handles = new List<IntPtr>(); 
      foreach (ProcessThread thread in processes[0].Threads) 
       EnumThreadWindows(thread.Id, 
        (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero); 

      //Create a stringbuilder to function as storage unit 
      StringBuilder nameBuffer = new StringBuilder(64); 
      foreach (var hWnd in handles) 
      { 
       //And finally compare the caption of the window with the requested name 
       nameBuffer.Clear(); 
       SendMessage(hWnd, WM_GETTEXT, nameBuffer.Capacity, nameBuffer); 
       if (nameBuffer.ToString() == windowName) 
       { 
        return new object[2] { GetWindowThreadProcessId(hWnd), hWnd }; 
       } 
      } 
     } 
     return null; 
    } 
} 
응용 프로그램의

Main 메서드

SendInput()의 비밀스런 기술에 대해 설명해 주시면 미리 감사드립니다.

+0

어떻게 마침내 해결 했습니까? PostMessage를 사용하여 창에 키를 보냅니다. – SMUsamaShah

+0

@ LifeH2O 슬프게도 나는 그것을 풀어 본 적이 없다고 생각합니다. PostMessage를 사용하려면 응용 프로그램에서 Windows 이벤트 루프를 실행해야하지만 상호 작용하려는 창에 포커스가 없으면 IIRC가 작동하지 않습니다. – KappaG3

답변

1

키 입력을 보내는 특정 컨트롤의 포커스가 올바르게 설정되어 있는지 확인하십시오.

SetFocus을 사용하면 키 입력을 보내는 컨트롤에 포커스를 지정할 수 있습니다.

SendMessagePostMessage도 키 입력을 보낼 수 있지만 BAD PRACTICE이므로 피해야합니다.

.NET의 Forms 클래스를 통한 키 입력 보내기에 대한 정보는 System.Windows.Forms.SendKeys을 확인하십시오.

많은 경우 키 스트로크 자체가 필요하지 않은 경우 SendMessageWM_SETTEXT을 사용하여 창에서 텍스트를 변경할 수 있습니다.

+0

메모장으로 테스트를하고 있었는데 분명히 작동하지 않습니다. SendKeys를 사용하고 다시보고하려고합니다. 편집 : 나는 당신이 뜻밖의 방법으로 나를 도왔다 고 생각합니다. Send 메서드를 호출하면 다음 예외가 발생합니다. 응용 프로그램이 Windows 메시지를 처리하지 않기 때문에 SendKeys를이 응용 프로그램 내에서 실행할 수 없습니다. 메시지를 처리하도록 응용 프로그램을 변경하거나 SendKeys.SendWait 메서드를 사용하십시오. 제가 생각하기에 문제가 있습니다. – KappaG3

+0

웰프, 내가 옳았다 고 생각합니다. 방금 Windows Form 프로젝트에 코드를 포팅했는데 성공했습니다. 귀하의 답변에 대해 고맙겠습니다. 그러나 SetFocus는 키보드 포커스를 설정하는 것이 아니라 대상 윈도우를 포 그라운드로 가져 오는 것입니다. 루트 제로로 돌아왔다. – KappaG3