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());
}
}
@squelos: cảm ơn điều đó cũng sẽ giúp ích. – RanRag
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. –
@ HansPassant: Vì vậy, những gì 'zabulus' đề xuất sẽ không hoạt động. – RanRag