невозможно прочитать заголовок другого приложения

Прыгаю о том, как я найду дескриптор Windows в моей основной программе...

in C#

Я запускаю notepad.exe, затем что-то набираю в нем, затем нахожу дескриптор главного окна с помощью SPY++ (0x111111) и

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]

internal static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount);
.
.
.
GetWindowText((IntPtr)(0x111111), str, 1024);

этот код отлично работает и возвращает мне заголовок главного окна.

: : но когда я делаю то же самое, чтобы найти заголовок дочернего элемента notepad.exe, он просто ничего не устанавливает для str. шпион++ сказал мне, что подпись ребенка имеет значение.


person amir beygi    schedule 05.01.2011    source источник
comment
Какого ребенка???? Может варьироваться от пункта меню «Файл» до строки состояния или чего-то еще!   -  person Grant Thomas    schedule 05.01.2011
comment
я что-то написал в текстовой области. затем используйте spy++, чтобы найти дескриптор с текстом в заголовке, а затем используйте этот дескриптор в моем коде.   -  person amir beygi    schedule 05.01.2011
comment
Вы понимаете, что дескриптор (hWnd) не всегда будет одним и тем же? Жесткое кодирование значения в вашем приложении бесполезно.   -  person Cody Gray    schedule 05.01.2011


Ответы (2)


«Самый правильный» способ сделать это:

public static string GetWindowText(IntPtr hwnd)
{
    if (hwnd == IntPtr.Zero)
        throw new ArgumentNullException("hwnd");
    int length = SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
    if (length > 0 && length < int.MaxValue)
    {
        length++; // room for EOS terminator
        StringBuilder sb = new StringBuilder(length);
        SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
        return sb.ToString();
    }
    return String.Empty;
}

const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;

[DllImport("User32.dll", EntryPoint = "SendMessage")]
extern static int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
extern static IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
extern static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, [In] string lpClassName, [In] string lpWindowName);

Обратите внимание на использование атрибутов [In] и [Out] для исключения ненужного копирования во время сортировки.

Также обратите внимание, что вы никогда не должны предоставлять методы p/invoke внешнему миру (не общедоступным).

person Tergiver    schedule 05.01.2011
comment
Не могли бы вы дать мне несколько советов о том, как найти последние изменения в моем текстовом поле? у меня есть растущее текстовое поле, могу ли я связать что-то со списком событий этого текстового поля или что-то в этом роде? - person amir beygi; 05.01.2011
comment
В системном элементе управления EDIT нет уведомления об изменении, которое могло бы отслеживаться внешним процессом. Вы можете опросить его или написать глобальный хук для реализации такого события. Этот хук будет CBT_CALLWNDPROCRET. Это требует написания собственной DLL (т. е. C/C++) и усложняется, если перехватываемый процесс является одновременно 32- и 64-разрядным, как Блокнот. - person Tergiver; 05.01.2011

В документации по функции GetWindowText четко указано, что «GetWindowText не может получить текст элемента управления в другом приложении. ... Чтобы получить текст элемента управления в другом процессе, отправьте сообщение WM_GETTEXT напрямую вместо вызова GetWindowText».

Вы можете получить текст элемента управления с помощью следующего кода:

[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessageGetText(IntPtr hWnd, uint msg, UIntPtr wParam, StringBuilder lParam);

const uint WM_GETTEXT = 13;
const int bufferSize = 1000; // adjust as necessary
StringBuilder sb = new StringBuilder(bufferSize);
SendMessageGetText(hWnd, WM_GETTEXT, new UIntPtr(bufferSize), sb);
string controlText = sb.ToString();
person Bradley Grainger    schedule 05.01.2011
comment
Аргумент wParam — IntPtr. Возвращаемое значение тоже не имеет большого значения в этом случае. - person Hans Passant; 05.01.2011
comment
@ Ганс Спасибо; Я исправил подпись, чтобы она соответствовала задокументированным типам LRESULT (LONG_PTR), UINT и WPARAM (UINT_PTR). - person Bradley Grainger; 05.01.2011
comment
Спасибо, это работает, не могли бы вы сказать мне, как я могу узнать длину текста перед чтением или как я могу прочитать хвост текста? - person amir beygi; 05.01.2011
comment
@amir: Вы пытаетесь копаться во внутренностях другой программы. Это явно неопределенная территория. Вы должны переосмыслить свое решение. - person Mike Caron; 05.01.2011
comment
@amir: используйте WM_GETTEXTLENGTH (msdn.microsoft.com /en-us/library/ms632628(v=vs.85).aspx). - person Bradley Grainger; 05.01.2011