2009-12-28 2 views
9

오픈 소스 패스워드 매니저 Keepass에 대한 플러그인 작업에 관심이 있습니다. 지금은 Keepass이 현재 창 제목에 따라 복사/붙여 넣기 할 비밀번호를 감지합니다. 이렇게하면 Keepass가 현재 사이트 (예 : Chrome)를 기반으로 창 제목을 적극적으로 업데이트하지 않는 앱에 필요한 현재 비밀번호를 감지하지 못합니다.Spy ++와 비슷한 기능을 C# 응용 프로그램에서 어떻게 얻을 수 있습니까?

Spy ++ 작동 방식과 유사한 다른 프로세스 창 요소 (단추, 레이블, 텍스트 상자)를 어떻게 통과합니까? Spy ++를 실행하면 다른 프로그램 창 위로 마우스를 가져 가서 다양한 컨트롤 (레이블, 텍스트 상자 등)에 관한 다양한 속성에 대한 모든 종류의 정보를 얻을 수 있습니다. 이상적으로는 Keepass 플러그인을 사용하여 활성 창 요소를 살펴보고 비밀번호를 복사/붙여 넣기 할 일치하는 계정을 찾기 위해 현재 창 감지 기능을 향상시키고 싶습니다.

어떻게하면 C#을 사용하여 다른 프로세스 창 요소를 탐색하고 레이블 및 텍스트 상자 값을 검색 할 수 있습니까?

+1

재미있는 questin, 나는 당신이 그것을 달성하기 위해 WINAPI하는 PInvoke를해야합니다 밝혀졌다하지만 놀라지 않을 것이다. –

답변

22

저는 이와 비슷한 질문에 대답했습니다 : How can I detect if a thread has windows handles?. 상태와 마찬가지로 주 아이디어는 창 핸들을 얻기 위해 EnumWindowsEnumChildWindows API 호출을 사용하여 프로세스 창과 자식 창을 열거 한 다음 WM_GETTEXT를 사용하여 창 텍스트를 가져 오는 GetWindowText 또는 SendDlgItemMessage를 호출하여 창 텍스트를 가져 오는 것입니다. 나는 당신이 필요로하는 것을해야만하는 예제를 만들기 위해 코드를 수정했다. (약간 미안하다 :). 프로세스와 창을 반복하여 창 텍스트를 콘솔로 덤프합니다. 이 도움이

static void Main(string[] args) 
{ 
    foreach (Process procesInfo in Process.GetProcesses()) 
    { 
     Console.WriteLine("process {0} {1:x}", procesInfo.ProcessName, procesInfo.Id); 
     foreach (ProcessThread threadInfo in procesInfo.Threads) 
     { 
      // uncomment to dump thread handles 
      //Console.WriteLine("\tthread {0:x}", threadInfo.Id); 
      IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id); 
      if (windows != null && windows.Length > 0) 
       foreach (IntPtr hWnd in windows) 
        Console.WriteLine("\twindow {0:x} text:{1} caption:{2}", 
         hWnd.ToInt32(), GetText(hWnd), GetEditText(hWnd)); 
     } 
    } 
    Console.ReadLine(); 
} 

private static IntPtr[] GetWindowHandlesForThread(int threadHandle) 
{ 
    _results.Clear(); 
    EnumWindows(WindowEnum, threadHandle); 
    return _results.ToArray(); 
} 

// enum windows 

private delegate int EnumWindowsProc(IntPtr hwnd, int lParam); 

[DllImport("user32.Dll")] 
private static extern int EnumWindows(EnumWindowsProc x, int y); 
[DllImport("user32")] 
private static extern bool EnumChildWindows(IntPtr window, EnumWindowsProc callback, int lParam); 
[DllImport("user32.dll")] 
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); 

private static List<IntPtr> _results = new List<IntPtr>(); 

private static int WindowEnum(IntPtr hWnd, int lParam) 
{ 
    int processID = 0; 
    int threadID = GetWindowThreadProcessId(hWnd, out processID); 
    if (threadID == lParam) 
    { 
     _results.Add(hWnd); 
     EnumChildWindows(hWnd, WindowEnum, threadID); 
    } 
    return 1; 
} 

// get window text 

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

private static string GetText(IntPtr hWnd) 
{ 
    int length = GetWindowTextLength(hWnd); 
    StringBuilder sb = new StringBuilder(length + 1); 
    GetWindowText(hWnd, sb, sb.Capacity); 
    return sb.ToString(); 
} 

// get richedit text 

public const int GWL_ID = -12; 
public const int WM_GETTEXT = 0x000D; 

[DllImport("User32.dll")] 
public static extern int GetWindowLong(IntPtr hWnd, int index); 
[DllImport("User32.dll")] 
public static extern IntPtr SendDlgItemMessage(IntPtr hWnd, int IDDlgItem, int uMsg, int nMaxCount, StringBuilder lpString); 
[DllImport("User32.dll")] 
public static extern IntPtr GetParent(IntPtr hWnd); 

private static StringBuilder GetEditText(IntPtr hWnd) 
{ 
    Int32 dwID = GetWindowLong(hWnd, GWL_ID); 
    IntPtr hWndParent = GetParent(hWnd); 
    StringBuilder title = new StringBuilder(128); 
    SendDlgItemMessage(hWndParent, dwID, WM_GETTEXT, 128, title); 
    return title; 
} 

희망,

+1

EnumChildWindows는 이미 반복되었으므로 여기서 이중 재귀를 수행합니다. [EnumChildWindows의 오래된 새로운 기능] (http://blogs.msdn.com/b/oldnewthing/archive/2007/01/16/1478717.aspx) –

+1

몇 가지 답변에서 EnumChildWindows를 재귀 적으로 호출한다고합니다. . 그 일을하지 마십시오. MSDN 문서를 읽어보십시오 - 이미 재귀 적이기 때문에 EnumDescendants라는 이름을 붙여야합니다. –

+0

내 컴퓨터에서 일어나는 무한 루프를 어떻게 피할 수 있습니까? 그것은 자식 창을 통해 드릴링 한 후에 논리적으로 파열되어야합니다. –

3

당신은 모든 최상위 크롬 창을 찾을 EnumWindows를 사용하여 다음 EnumChildWindows 재귀 (제론 Wiert Pluimers '의견을 참조) 메인 윈도우의 모든 아이를 얻기 위해 호출합니다. 또는 기본 크롬 창이 있으면 GetWindow을 사용하여 원하는 항목 (세 번째 자녀의 자녀 컬렉션 또는 이와 유사한 항목)을 알 수 있으므로 트리를 수동으로 탐색 할 수 있습니다.

창을 찾으면 SendMessage에 WM_GETTEXT 매개 변수를 사용하여 창 레이블을 읽을 수 있습니다.

+0

@ 블린디, 정보 주셔서 감사합니다! Chrome을 비롯한 모든 활성 창에서이 작업을 수행 할 것입니다.일단 내가 창/자식 창에 대한 핸들을 가지고 있다면, 그 창 요소를 어떻게 탐색해야합니까? – mmcdole

+0

이들은 모두 .NET Framework 호출이 아니라 기본 Windows API 호출이므로 CLR로부터 P/Invoke가 필요합니다. –

+0

@DrJokepu, Aye. 나는 P/Invoke 마술을해야한다고 생각했다. – mmcdole

1

창을 가리키는 기능입니다. 창 외부에 마우스 메시지가 표시되도록 SetCapture()이 필요합니다. 그런 다음 WindowFromPoint()을 사용하여 마우스 위치를 창으로 변환하십시오. 먼저 클라이언트 좌표에서 창 좌표로 위치를 변환해야합니다.

SetCapture()을 마우스 클릭 메시지에서 아무 데나 시도하면 무시됩니다. 이것이 Spy ++가 아이콘을 클릭하고 가리 키려는 창에 아이콘을 끌어서 놓는 이유입니다.

+0

내가하려는 것은 단순히 창을 반복하는 것입니다. 마우스 상호 작용없이 요소. 일단 EnumChildWindows를 호출하면, 그 자식 창에 속하는 요소의 텍스트를 어떻게 얻을 수 있습니까? – mmcdole

+0

창 메시지를 보냅니다. WM_GETTEXT 등이 충분하지 않다면 '요소'가 의미하는 것을 정의해야합니다. –

+0

@ John Knoeller, 모든 텍스트 레이블, 텍스트 상자 및 버튼. – mmcdole

3

here에는 Managed Spy에 대한 정보와 작성자가이 도구를 작성한 이유가 있습니다.

+0

당신이 참조하는 응용 프로그램은 * managed * 응용 프로그램에서만 작동하며 대부분의 브라우저처럼 관리되지 않는 응용 프로그램이 아닙니다. – Blindy

0

당신은 HWndSpy을 사용할 수 있습니다 간주. 소스 코드는 here입니다.

enter image description here

관련 문제