Получить текст элементов Treeview (SysTreeView32) с помощью win32 api

Я пишу приложение для автоматизации некоторых повторяющихся задач на моей работе. Одна из задач, которую я хочу выполнить, - это иметь возможность автоматизировать процесс создания диска восстановления из "RecoveryDrive.exe" в Windows 10. Весь процесс завершен, но на одном этапе человеку нужно выбрать диск. в элементе управления SysTreeView32.

Я попытался найти, как получить текст текущего выбранного treeNodeItem.

У меня есть дескриптор элемента управления, но когда я пытаюсь его прочитать, используя образец кода, найденный в Интернете, приложение recoveryDrive аварийно завершает работу.

Я подозреваю, что это связано с несоответствием 64 бит / 32 бит с используемыми мной методами api и, возможно, несоответствием кодировки ASCI и Unicode ... Я также думаю, что мне нужно использовать LocalAlloc внутри целевого приложения Ручка или память

вот pasteBin кода в текущем состоянии.

У него также есть 3 страницы, на которых я основал свой код. Сбой приложения в функции GetTreeItemText, когда я использую sendMessage.

Я нашел пример того, как это сделать на C++, но я этого не совсем понимаю.

 public static string GetTreeItemText(IntPtr treeViewHwnd, IntPtr hItem)
            {
                int ret;
                TVITEM tvi = new TVITEM();
                IntPtr pszText = LocalAlloc(0x40, MY_MAXLVITEMTEXT);

                tvi.mask = TVIF_TEXT;
                tvi.hItem = hItem;
                tvi.cchTextMax = MY_MAXLVITEMTEXT;
                tvi.pszText = pszText;

                ret = SendMessageTVI(treeViewHwnd, TVM_GETITEM, 0, ref tvi);
                string buffer = Marshal.PtrToStringUni((IntPtr)tvi.pszText,
                MY_MAXLVITEMTEXT);

                //char[] arr = buffer.ToCharArray(); //<== use this array to look at the bytes in debug mode

                LocalFree(pszText);
                return buffer;
            }

Почему вы не используете UIAutomation?

David Heffernan 02.05.2018 06:32

Вы обращаетесь к памяти в другом процессе, вам нужно выделить память с помощью VirtualAllocEx. 32-битное / 64-битное совпадение между вызывающим и целевым объектами также имеет значение. Соответствие ANSI / Unicode не имеет значения (я думаю, что это нормально, если исходным приложением является Unicode). В C# должно быть намного проще использовать автоматизацию пользовательского интерфейса, чем этот метод.

Barmak Shemirani 02.05.2018 10:52

Я думаю, что автоматически упорядочиваются только основные сообщения, поэтому использование межпроцессного SendMessage для элементов управления не сработает.

bunglehead 02.05.2018 12:10

@bunglehead это действительно работает, вам просто нужно вручную маршалировать буферы памяти

Remy Lebeau 02.05.2018 17:32

@RemyLebeau, этот пример как раз и означает, что он не будет работать автоматически. И это не совсем маршалинг, если вы читаете / записываете в память другого процесса.

bunglehead 02.05.2018 18:18

@bunglehead Я не говорил, что это сработает автоматически. Я сказал, что это сработает, если вы обработаете детали вручную

Remy Lebeau 02.05.2018 19:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
1 484
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

LPARAM сообщения TVM_GETITEM является указателем на структуру TVITEM. Дело в том, что эта структура ДОЛЖНА быть размещена в том же процессе, который владеет элементом управления TreeView. Таким образом, при отправке TVM_GETITEM через границы процесса вы должны использовать VirtualAllocEx() для выделения TVITEM и его буфера pszText в адресном пространстве целевого процесса, а затем использовать WriteProcessMemory() / ReadProcessMemory() для записи / чтения данных этой структуры.

Попробуйте что-то вроде этого (вы можете найти объявления для функций Win32 API, используемых в PInvoke.net):

public static string GetTreeItemText(IntPtr treeViewHwnd, IntPtr hItem)
{
    string itemText;

    uint pid;
    GetWindowThreadProcessId(treeViewHwnd, out pid);

    IntPtr process = OpenProcess(ProcessAccessFlags.VirtualMemoryOperation | ProcessAccessFlags.VirtualMemoryRead | ProcessAccessFlags.VirtualMemoryWrite | ProcessAccessFlags.QueryInformation, false, pid);
    if (process == IntPtr.Zero)
        throw new Exception("Could not open handle to owning process of TreeView", new Win32Exception());

    try
    {
        uint tviSize = Marshal.SizeOf(typeof(TVITEM));

        uint textSize = MY_MAXLVITEMTEXT;
        bool isUnicode = IsWindowUnicode(treeViewHwnd);
        if (isUnicode)
            textSize *= 2;

        IntPtr tviPtr = VirtualAllocEx(process, IntPtr.Zero, tviSize + textSize, AllocationType.Commit, MemoryProtection.ReadWrite);
        if (tviPtr == IntPtr.Zero)
            throw new Exception("Could not allocate memory in owning process of TreeView", new Win32Exception());

        try
        {
            IntPtr textPtr = IntPtr.Add(tviPtr, tviSize);

            TVITEM tvi = new TVITEM();
            tvi.mask = TVIF_TEXT;
            tvi.hItem = hItem;
            tvi.cchTextMax = MY_MAXLVITEMTEXT;
            tvi.pszText = textPtr;

            IntPtr ptr = Marshal.AllocHGlobal(tviSize);
            try
            {
                Marshal.StructureToPtr(tvi, ptr, false);
                if (!WriteProcessMemory(process, tviPtr, ptr, tviSize, IntPtr.Zero))
                    throw new Exception("Could not write to memory in owning process of TreeView", new Win32Exception());
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

            if (SendMessage(treeViewHwnd, isUnicode ? TVM_GETITEMW : TVM_GETITEMA, 0, tviPtr) != 1)
                throw new Exception("Could not get item data from TreeView");

            ptr = Marshal.AllocHGlobal(textSize);
            try
            {
                int bytesRead;
                if (!ReadProcessMemory(process, textPtr, ptr, textSize, out bytesRead))
                    throw new Exception("Could not read from memory in owning process of TreeView", new Win32Exception());

                if (isUnicode)
                    itemText = Marshal.PtrToStringUni(ptr, bytesRead / 2);
                else
                    itemText = Marshal.PtrToStringAnsi(ptr, bytesRead);
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
        }
        finally
        {
            VirtualFreeEx(process, tviPtr, 0, FreeType.Release);
        }
    }
    finally
    {
        CloseHandle(process);
    }

    //char[] arr = itemText.ToCharArray(); //<== use this array to look at the bytes in debug mode

    return itemText;
}

Танки за код! похоже, что это будет работать. Я не нашел подходящего объявления для ваших WriteProcessMemory и ReadProcessMemory. И Marshal.SizeOf (TVITEM) выдает ошибку: «TVITEM» - это тип, который недопустим в данном контексте. Если вы можете помочь с этим, я смогу это протестировать :)

Benoit 04.05.2018 03:48

@Benoit Объявления доступны по адресу PInvoke.net. Marshal имеет SizeOf() перегрузка, который принимает на входе System.Type. Я просто забыл применить typeof() к TVITEM. Я обновил код.

Remy Lebeau 04.05.2018 05:09

tank, у меня проблема только с WriteProcessMemory. Если вы проверите определение в pinvoke, вы увидите, что lpBuffer ожидает байта []. (pinvoke.net/default.aspx/kernel32/WriteProcessMemory.html) можно просто поменять его на IntPtr?

Benoit 05.05.2018 01:30

@Benoit Да, можно поменять на IntPtr. Аналогичный пример сортировки показан для TCITEM на PInvoke.net, он использует IntPtr вместо byte[].

Remy Lebeau 05.05.2018 01:39

Другие вопросы по теме