Я пишу приложение для автоматизации некоторых повторяющихся задач на моей работе. Одна из задач, которую я хочу выполнить, - это иметь возможность автоматизировать процесс создания диска восстановления из "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;
}
Вы обращаетесь к памяти в другом процессе, вам нужно выделить память с помощью VirtualAllocEx
. 32-битное / 64-битное совпадение между вызывающим и целевым объектами также имеет значение. Соответствие ANSI / Unicode не имеет значения (я думаю, что это нормально, если исходным приложением является Unicode). В C# должно быть намного проще использовать автоматизацию пользовательского интерфейса, чем этот метод.
Я думаю, что автоматически упорядочиваются только основные сообщения, поэтому использование межпроцессного SendMessage для элементов управления не сработает.
@bunglehead это действительно работает, вам просто нужно вручную маршалировать буферы памяти
Связанный: Как правильно прокручивать / получать текст / выбирать элемент окна SysTreeView32
@RemyLebeau, этот пример как раз и означает, что он не будет работать автоматически. И это не совсем маршалинг, если вы читаете / записываете в память другого процесса.
@bunglehead Я не говорил, что это сработает автоматически. Я сказал, что это сработает, если вы обработаете детали вручную
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 Объявления доступны по адресу PInvoke.net. Marshal
имеет SizeOf()
перегрузка, который принимает на входе System.Type
. Я просто забыл применить typeof()
к TVITEM
. Я обновил код.
tank, у меня проблема только с WriteProcessMemory. Если вы проверите определение в pinvoke, вы увидите, что lpBuffer ожидает байта []. (pinvoke.net/default.aspx/kernel32/WriteProcessMemory.html) можно просто поменять его на IntPtr?
Почему вы не используете UIAutomation?