2012-03-13 2 views
2

응용 프로그램이 마우스 클릭 위치와 사용자가 클릭하는 창의 제목 (이름)을 얻으려고합니다. 나는 현재 LowLevelMouseProc을 사용해 왔지만 결과는 좋지만 Google 크롬을 클릭 할 때마다 응용 프로그램이 다운됩니다.C에서 마우스 클릭을 기반으로 활성 창 이름 가져 오기

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 
    using System.Windows.Forms; 
using System.Diagnostics; 

     //An attempt to print the screen name of the active window and mouse coordinates at every mouse click 
namespace Project1 
{ 
class InterceptMouse 
{ 

    private static LowLevelMouseProc _proc = HookCallback; 
    private static IntPtr _hookID = IntPtr.Zero; 

    public static void Main() 
    { 
     _hookID = SetHook(_proc); 
     Application.Run(); 
     UnhookWindowsHookEx(_hookID); 
     Application.Exit(); 
    } 

    private static IntPtr SetHook(LowLevelMouseProc proc) 
    { 
     using (Process curProcess = Process.GetCurrentProcess()) 
     using (ProcessModule curModule = curProcess.MainModule) 
     { 
      return SetWindowsHookEx(WH_MOUSE_LL, proc, 
       GetModuleHandle(curModule.ModuleName), 0); 
     } 
    } 

    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); 

    private static IntPtr HookCallback(
     int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     int nodeCount; 
     LinkedListNode<StringBuilder> nodefirst = new LinkedListNode<StringBuilder>(null); 
     LinkedListNode<StringBuilder> nodeprev = new LinkedListNode<StringBuilder>(null); 
     LinkedList<StringBuilder> windowlist = new LinkedList<StringBuilder>(); 


     if (nCode >= 0 && 
      MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam) 
     { 
      MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 
      Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y); 
      IntPtr hwnd = GetForegroundWindow(); 
      StringBuilder windowtitle = new StringBuilder(); 
      if(GetWindowText(hwnd, windowtitle, 2000)>0) 
      Console.WriteLine(windowtitle); 
      //Console.WriteLine(nodeCount); 

      if (nodeCount == 0) 
      { 
       nodefirst = windowlist.AddFirst(windowtitle); 
       nodeCount++; 
      } 
      else 
      { 
       if (nodeCount == 1) 
       { 
        nodeprev = windowlist.AddAfter(nodefirst, windowtitle); 
        nodeCount++; 
       } 
       if (nodeCount > 1) 
       { 
        nodeprev = windowlist.AddAfter(nodeprev, windowtitle); 
        nodeCount++; 
       } 

      } 

     } 

     return CallNextHookEx(_hookID, nCode, wParam, lParam); 

    } 

    private const int WH_MOUSE_LL = 14; 

    private enum MouseMessages 
    { 
     WM_LBUTTONDOWN = 0x0201, 
     WM_LBUTTONUP = 0x0202, 
     WM_MOUSEMOVE = 0x0200, 
     WM_MOUSEWHEEL = 0x020A, 
     WM_RBUTTONDOWN = 0x0204, 
     WM_RBUTTONUP = 0x0205 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct POINT 
    { 
     public int x; 
     public int y; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct MSLLHOOKSTRUCT 
    { 
     public POINT pt; 
     public uint mouseData; 
     public uint flags; 
     public uint time; 
     public IntPtr dwExtraInfo; 
     IntPtr hwnd; 

    } 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr SetWindowsHookEx(int idHook, 
     LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool UnhookWindowsHookEx(IntPtr hhk); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 
     IntPtr wParam, IntPtr lParam); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr GetModuleHandle(string lpModuleName); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
    public static extern IntPtr GetForegroundWindow(); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 
    } 
    } 

비록 내가 낮은 수준의 후크를 사용하고 단지에 Thread.sleep (5000)를 사용하고 매 5 초 후 충돌하지 않는 활성 창 이름을 받고 보관하지 않습니다 다음은 코드입니다. 이유를 찾도록 도와주세요. . 도와주세요.

+0

충돌 메시지가 무엇입니까? –

+0

'GetCursorPos'를 사용하여 클릭의 화면 좌표를 가져올 수 있습니다. 클릭과 연관된 창을 검색하는'WindowFromPoint', 창 제목을 가져 오기위한'GetWindowText', 그리고 좌표를 상대적인 것으로 요구하면'ScreenToClient'를 사용할 수 있습니다 창. 자식이 아닌 윈도우 자체가 필요하면'GetAncestor'를 사용할 수 있습니다. 그 중 일부는 도움이되기를 바랍니다. 후크는 확실히 불필요합니다. – chris

+0

@chris : 귀하의 접근 방식에 진심으로 감사드립니다. 그러나 마우스 클릭을 연결하지 않으면 모든 마우스 클릭 (다양한 창에서 발생하는 제목, 검색해야하는 제목)을 어떻게 추적 할 수 있는지 이해할 수 없습니다. . ScreenToClient 및 WindowFromPoint 호출을 수행하는 GetWindowUnderCursor()를 사용한다고 가정하고이 함수 (GetWindowsUnderCursor)를 매 5 초마다 호출해야 커서가 어느 창에 있는지, 그렇지 않으면 마우스 클릭을 전달하여 알림을받습니다. 사용자가 창을 클릭하자마자 창 이름에 GetForegroundWindow를 사용했습니다. – Ajit

답변

2

StringBuilder의 용량을 지정해야합니다. 보다 철저하게하기 위해 과 같이 GetWindowTextLength을 사용할 수 있습니다.

StringBuilder windowtitle = new StringBuilder(256); 
if (GetWindowText(hwnd, windowtitle, windowtitle.Capacity) > 0) 
    Console.WriteLine(windowtitle); 
0

매우 이상합니다. 가능한 경우 언제나 구부리지 마십시오. 왜 GetForeGroundWindow을 사용하지 않는 것이 좋을까요? 사용자가 창을 클릭하면 바로 전경 창이됩니다. 그렇다면 마우스를 연결해야하는 이유는 무엇입니까?

Chrome에서 애플리케이션이 다운되는 경우 API 호출에서 GetLastError를 true로 설정 한 다음 각 api 호출에서 오류 코드를 인쇄하여 실패한 API 메소드와 실패한 API 메소드를 찾아야합니다. 또한 어디서 실패했는지 Thread.Sleep을 호출하는 위치를 정확하게 지정하지 않았습니다.

두 번째로, 수행하고있는 작업을 수행하는 CodeProject의 두 가지 상세 프로젝트 인 .NET Object SpyWinForm Spy을 살펴볼 수 있습니다.

+1

당신이 신중하게 GetForeGroundWindow를 사용한 코드를 관찰한다면, 마우스를 연결하는 주된 의도는 사용자가 클릭/여는 모든 윈도우의 목록을 가져와야한다는 것입니다. 이제이 방법은 두 가지 방법으로 수행 할 수 있습니다. 1) 매 5 초마다 루프에서 GetWindowUnderCursor (내부적으로 GetForeGroundWindow를 호출합니다)를 호출하여 (즉, Thread.Sleep (5000)을 사용하여) 사용자가 매 5 초마다 또는 2) 마우스 클릭을 걸어 사용자가 창에서 마우스를 클릭하자마자 사용자의 위치를 ​​알게됩니다. 그래서 내가 후크를 사용했다. – Ajit

1

이 대답은 내 answere의 사본이 여기에 게시됩니다How to get active window name based on mouse[...]
이 코드는 마우스를 모니터링 주제를 벗어났습니다. 창에 초점을 둔 창 또한주의하십시오 : 이것은 당신이 요구 한 것에 관한 것입니다 - 창 이름/제목 -.


참조 : How to get active window handle and title

네임 스페이스 :

using System; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading; 

방법 :

[DllImport("user32.dll")] 
private static extern IntPtr GetForegroundWindow(); 

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 

콜 :

// get handle 
IntPtr handle = GetForegroundWindow(); 

// get title 
const int count = 512; 
var text = new StringBuilder(count); 

if (GetWindowText(handle, text, count) > 0) 
{ 
    MessageBox.Show(text.ToString()); 
} 

이 코드를 몇 번 사용했습니다. 정말 사용하기 쉽습니다. 10ms마다 발사하는 타이머를 설정할 수 있습니다. 2 개의 변수를 저장하십시오. 하나는 활성 창이 있고 마지막 하나는 초점이 맞춰진 창입니다. 의사 코드에서 : 만약 newWindow! = oldWindow-> listView.Add (Window).


마지막으로는 다음과 같이 수 :

public partial class Form1 : Form 
    { 
     // DECLARE GLOBALS // 
     [DllImport("user32.dll")] 
     private static extern IntPtr GetForegroundWindow(); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); 

     public static string oldWindow = ""; 
     public static string currentWindow = ""; 

     // TIMER EVENT // 
     private void timerCheckFocus_Tick(object sender, EventArgs e) 
     { 
      // get handle 
      IntPtr handle = GetForegroundWindow(); 

      // get title 
      const int count = 512; 
      var currentWindow = new StringBuilder(count); 

      // if the current title is NOT the old title - so if its a new window // 
      if (currentWindow.ToString() != oldWindow) 
      { 
       // add your window to a listView // 
       listView1.Add(currentWindow.ToString()); 
       // save your currentWindow as oldWindow cuz it got handled // 
       oldWindow = currentWindow.ToString(); 
      } 
     }