2012-03-12 38 views
19

Tôi đang cố gắng tạo một ứng dụng sẽ thông báo cho tên và nghệ sĩ hiện tại của bản nhạc đang phát cho người dùng mà tôi cần theo dõi track change event.Thiết lập móc trên tin nhắn Windows

Tôi đã sử dụng Winspector và phát hiện ra rằng bất cứ khi nào có thay đổi theo dõi trong các thông báo spotify WM_SETTEXT sẽ được gửi.

enter image description here

Đối với điều này tôi tin rằng tôi phải thiết lập một HOOK thông qua ứng dụng của tôi để tìm kiếm WM_SETTEXT nhắn được gửi bởi ứng dụng khác.

Hiện tại, vấn đề tôi đang gặp phải là tôi không thể lấy bất kỳ mã mẫu làm việc nào để làm việc. Tôi đọc tài liệu của setwindowshookex và cũng đã làm một số googling nhưng thực sự bị mất như tôi không có nền của C# và xử lý các cửa sổ tin nhắn/sự kiện. Vì vậy, nếu bạn có thể cung cấp cho tôi một mã làm việc nhỏ để quấn đầu của tôi xung quanh setting up hook trên một ứng dụng khác hoặc nếu bạn có thể hướng dẫn tôi đến một số bài viết hay về cách đạt được điều này.

+0

@squelos: cảm ơn điều đó cũng sẽ giúp ích. – RanRag

+2

Các loại móc bạn muốn sử dụng đòi hỏi một DLL có thể được tiêm vào quá trình khác. Bạn không thể viết một DLL như vậy trong ngôn ngữ C#, quá trình này sẽ không tải CLR và khởi tạo. Một ngôn ngữ không được quản lý là bắt buộc, C, C++ hoặc Delphi là những lựa chọn điển hình. –

+0

@ HansPassant: Vì vậy, những gì 'zabulus' đề xuất sẽ không hoạt động. – RanRag

Trả lời

42

Dưới đây là một cách tiếp cận khác nhau: bỏ qua API SetWindowsHook, và thay vào đó sử dụng WinEvents, mà sử dụng SetWinEventHook để thay thế. Chúng có phần tương tự như các cửa sổ móc, trong đó cả hai đều liên quan đến chức năng gọi lại được gọi tại các sự kiện cụ thể, nhưng WinEvents dễ sử dụng hơn từ C#: bạn có thể xác định rằng WinEvents được phân phối "out context", nghĩa là các sự kiện được đăng trở lại quá trình của riêng bạn, vì vậy bạn không cần một DLL riêng biệt. (Mã của bạn không cần chạy vòng lặp thư trên cùng một chuỗi được gọi là SetWinEventHook).

Hóa ra một trong các loại sự kiện mà WinEvent hỗ trợ là sự kiện 'thay đổi tên', được tự động kích hoạt bởi USER32 bất cứ khi nào văn bản tiêu đề của một thay đổi HWND, có vẻ như là những gì bạn đang tìm kiếm. (WinEvents cũng có thể được sử dụng để theo dõi các thay đổi tập trung và các loại thay đổi trạng thái khác nhau; để biết thêm thông tin.) Nó cũng bị các điều khiển khác kích hoạt khi giao diện người dùng nội bộ của họ thay đổi - ví dụ như hộp danh sách khi văn bản của mục danh sách thay đổi. phải làm một số lọc.

Dưới đây là một số mã mẫu in ra các thay đổi tiêu đề trên bất kỳ HWND nào trên màn hình - bạn sẽ thấy nó in ra thông báo khi văn bản trên đồng hồ trên thanh tác vụ thay đổi. Bạn sẽ muốn sửa đổi mã này để lọc chỉ HWND bạn đang theo dõi trong Spotify.Ngoài ra, mã này lắng nghe để thay đổi tên trên tất cả các quy trình/chủ đề; bạn sẽ nhận được threadID từ HWND đích bằng cách sử dụng GetWindowThreadProcessId và chỉ nghe các sự kiện từ chuỗi đó.

Cũng lưu ý rằng đây là phương pháp tiếp cận mong manh; nếu Spotify thay đổi cách hiển thị văn bản hoặc thay đổi định dạng của văn bản, bạn cần phải sửa đổi mã của mình để theo kịp các thay đổi của nó.

using System; 
using System.Windows; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

class NameChangeTracker 
{ 
    delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, 
     IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 

    [DllImport("user32.dll")] 
    static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr 
     hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, 
     uint idThread, uint dwFlags); 

    [DllImport("user32.dll")] 
    static extern bool UnhookWinEvent(IntPtr hWinEventHook); 

    const uint EVENT_OBJECT_NAMECHANGE = 0x800C; 
    const uint WINEVENT_OUTOFCONTEXT = 0; 

    // Need to ensure delegate is not collected while we're using it, 
    // storing it in a class field is simplest way to do this. 
    static WinEventDelegate procDelegate = new WinEventDelegate(WinEventProc); 

    public static void Main() 
    { 
     // Listen for name change changes across all processes/threads on current desktop... 
     IntPtr hhook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, IntPtr.Zero, 
       procDelegate, 0, 0, WINEVENT_OUTOFCONTEXT); 

     // MessageBox provides the necessary mesage loop that SetWinEventHook requires. 
     // In real-world code, use a regular message loop (GetMessage/TranslateMessage/ 
     // DispatchMessage etc or equivalent.) 
     MessageBox.Show("Tracking name changes on HWNDs, close message box to exit."); 

     UnhookWinEvent(hhook); 
    } 

    static void WinEventProc(IntPtr hWinEventHook, uint eventType, 
     IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) 
    { 
     // filter out non-HWND namechanges... (eg. items within a listbox) 
     if(idObject != 0 || idChild != 0) 
     { 
      return; 
     } 
     Console.WriteLine("Text of hwnd changed {0:x8}", hwnd.ToInt32()); 
    } 
} 
+0

Câu trả lời duy nhất cho đến nay xứng đáng là một upvote ... Và có, đây là một lựa chọn tốt hơn nhiều so với một móc toàn cầu anyway. –

+0

@Brendan: cảm ơn tôi sẽ thử mã của bạn và liên hệ lại với bạn. – RanRag

+1

Hoặc chỉ sử dụng không gian tên 'System.Windows.Automation'. –

2

Để được tư vấn về cách sử dụng SetWindowHookEx, hãy xem SO question 214022. Để biết mã hoạt động trong C#, hãy xem SO question 1811383.

Nói chung, nếu bạn muốn truy cập các chức năng WinAPI từ C#, bạn cần phải làm một vBulletin Gọi Gọi (viết tắt PInvoke). pinvoke.net là tài nguyên tốt trên chữ ký mà nguồn của bạn cần để làm điều đó, nhưng điều đó đã được đề cập đến trong câu hỏi 1811383.

Vì tôi chưa bao giờ hiểu toàn bộ hàng đợi nhắn tin Windows, tôi không biết liệu phương pháp được đề xuất bởi zabulus sẽ hoạt động khi thông điệp bắt nguồn từ một quá trình khác. Nhưng tôi tìm thấy một số mã ví dụ ở đây: http://en.serialcoder.net/Winforms/527/533/Interoperability%20Win32/How%20can%20I%20use%20%20Hooks%20%20in%20.NET.aspx Hy vọng điều đó sẽ hữu ích.

+0

cảm ơn tôi sẽ nhìn vào họ và lấy lại cho bạn. – RanRag

+0

** Chỉ ** móc bạn có thể cài đặt từ ứng dụng được quản lý (C# /. NET) là móc nối bàn phím mức thấp ('WH_LL_KEYBOARD') và móc chuột cấp thấp (' WH_LL_MOUSE'). Vì không ai trong số đó sẽ làm những gì @Noob đang tìm kiếm ở đây, anh ta sẽ phải gọi 'SetWindowsHookEx' từ một ứng dụng không được quản lý, được viết bằng một ngôn ngữ như C hoặc C++. –

3

Bạn có thể thử ghi đè WndProc ở dạng chính của bạn, một cái gì đó như thế này:

protected override void WndProc(ref Message m) 
{ 
    base.WndProc(ref m); 

    if (m.Msg == WM_SETTEXT) 
    { 
      // Call to your logic here 
    } 
} 
+0

Nhưng làm thế nào tôi sẽ đính kèm này vào một quá trình cụ thể. Tôi có nghĩa là nơi nào tôi vượt qua tham số 'hwnd' đó là cụ thể cho ứng dụng của tôi. – RanRag

+0

Bạn có nguồn ứng dụng của mình không? Nếu có, hãy tìm lớp biểu mẫu được chuyển đến phương thức Application.Run. Và dán mã của tôi vào nội dung của nó – zabulus

+0

Đây là trạng thái hiện tại của mã của tôi http://pastebin.com/sVKm2Y5a – RanRag

Các vấn đề liên quan