Получение текста из SysListView32 в 64-битной - PullRequest
3 голосов
/ 20 марта 2011

вот мой код:

   public static string ReadListViewItem(IntPtr lstview, int item)
    {
        const int dwBufferSize = 1024;

        int dwProcessID;
        LV_ITEM lvItem;
        string retval;
        bool bSuccess;
        IntPtr hProcess = IntPtr.Zero;
        IntPtr lpRemoteBuffer = IntPtr.Zero;
        IntPtr lpLocalBuffer = IntPtr.Zero;
        IntPtr threadId = IntPtr.Zero;

        try
        {
            lvItem = new LV_ITEM();
            lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
            // Get the process id owning the window
            threadId = GetWindowThreadProcessId(lstview, out dwProcessID);
            if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
                throw new ArgumentException("hWnd");

            // Open the process with all access
            hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
            if (hProcess == IntPtr.Zero)
                throw new ApplicationException("Failed to access process");

            // Allocate a buffer in the remote process
            lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT,
              PAGE_READWRITE);
            if (lpRemoteBuffer == IntPtr.Zero)
                throw new SystemException("Failed to allocate memory in remote process");

            // Fill in the LVITEM struct, this is in your own process
            // Set the pszText member to somewhere in the remote buffer,
            // For the example I used the address imediately following the LVITEM stuct
            lvItem.mask = LVIF_TEXT;

            lvItem.iItem = item;
            lvItem.iSubItem = 2;
            lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
            lvItem.cchTextMax = 50;

            // Copy the local LVITEM to the remote buffer
            bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem,
              Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to write to process memory");

            // Send the message to the remote window with the address of the remote buffer
            SendMessage(lstview, LVM_GETITEMText, 0, lpRemoteBuffer);

            // Read the struct back from the remote process into local buffer
            bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize,IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to read from process memory");

            // At this point the lpLocalBuffer contains the returned LV_ITEM structure
            // the next line extracts the text from the buffer into a managed string
            retval = Marshal.PtrToStringAnsi((IntPtr)(lpLocalBuffer +
              Marshal.SizeOf(typeof(LV_ITEM))));
        }
        finally
        {
            if (lpLocalBuffer != IntPtr.Zero)
                Marshal.FreeHGlobal(lpLocalBuffer);
            if (lpRemoteBuffer != IntPtr.Zero)
                VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
            if (hProcess != IntPtr.Zero)
                CloseHandle(hProcess);
        }
        return retval;
    }

Независимо от того, что я делаю, Retval возвращает пустое значение, хотя lpLocalBuffer - нет.

вот определение ListItem:

   [StructLayout(LayoutKind.Sequential)]
    private struct LV_ITEM
    {
        public int mask;
        public int iItem;
        public int iSubItem;
        public int state;
        public int stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        internal int lParam;
        internal int iIndent;
    }

Я попытался скомпилировать для 86x, 64 бит, любой процессор, кажется, ничего не работает вообще!

есть идеи, почему это может происходить?

C # + .net4, windows 7 64bit.

Ответы [ 5 ]

12 голосов
/ 21 марта 2011

Вот другой подход к этому - используйте UI Automation . Он выполняет кросс-процесс, кросс-битность для вас и будет работать против списков, списков или почти любого другого стандартного пользовательского интерфейса Windows. Вот пример приложения, которое получит HWND из списка под указателем мыши и поместит в него элементы. Он сбрасывает только имя каждого элемента; с Listviews, я думаю, вы можете вернуться в поля в каждом элементе, если хотите.

// Compile using: csc ReadListView.cs /r:UIAutomationClient.dll

using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;

class ReadListView
{
    public static void Main()
    {
        Console.WriteLine("Place pointer over listview and hit return...");
        Console.ReadLine();

        // Get cursor position, then the window handle at that point...
        POINT pt;
        GetCursorPos(out pt);
        IntPtr hwnd = WindowFromPoint(pt);

        // Get the AutomationElement that represents the window handle...
        AutomationElement el = AutomationElement.FromHandle(hwnd);

        // Walk the automation element tree using content view, so we only see
        // list items, not scrollbars and headers. (Use ControlViewWalker if you
        // want to traverse those also.)
        TreeWalker walker = TreeWalker.ContentViewWalker;
        int i = 0;
        for( AutomationElement child = walker.GetFirstChild(el) ;
            child != null; 
            child = walker.GetNextSibling(child) )
        {
            // Print out the type of the item and its name
            Console.WriteLine("item {0} is a \"{1}\" with name \"{2}\"", i++, child.Current.LocalizedControlType, child.Current.Name);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    };

    [DllImport("user32.dll")]
    private static extern IntPtr WindowFromPoint(POINT pt);

    [DllImport("user32.dll")]
    private static extern int GetCursorPos(out POINT pt);
}
4 голосов
/ 29 августа 2012

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

Я использовал рекомендацию в этом вопросе , который был в C ++, и немного изменил структуру LV_ITEM, чтобы она работала с 64-битной версией VB.NET (я не тестировал в C #, но я представляю решение очень похоже.)

Public Structure LV_ITEM64

    Public mask As Integer
    Public iItem As Integer
    Public iSubItem As Integer
    Public state As Integer
    Public stateMask As Integer
    Public placeholder1 As Integer
    Public pszText As Integer
    Public placeholder2 As Integer
    Public cchTextMax As Integer
    Public iImage As Integer

End Structure

Затем при объявлении экземпляра структуры я использовал следующий код для выбора между 64-битной и 32-битной структурами:

Dim lvi As Object

If IntPtr.Size = 4 Then
    lvi = New LV_ITEM
Else
    lvi = New LV_ITEM64
End If
1 голос
/ 20 марта 2011

Существует как минимум одно препятствие, которое необходимо преодолеть, если ваша программа 32-разрядная, а целевая - 64-разрядная.Или наоборот.Объявление LVITEM будет неправильным, IntPtr имеет неправильное количество битов.Что заставляет Marshal.SizeOf () возвращать неправильное значение.Выравнивание в порядке, я думаю, случайно.Изменение поля на int или long может решить проблему в зависимости от разрядности целевой программы.Что вы можете узнать, посмотрев на Taskmgr.exe, вкладка Процессы.Имя процесса постфиксируется с помощью «* 32», если это 32-битный процесс.Или просто избегайте неприятностей, установив настройки целевой платформы вашего проекта в соответствии с целевым процессом (x86 или AnyCPU).

Отладьте это с помощью Debug + Windows + Memory + Memory1.Поместите «lpLocalBuffer» в поле «Адрес» и посмотрите, что вы видите против того, что читает ваш код.Вы точно должны быть в состоянии сказать из шестнадцатеричного представления, что вы получили строку правильно.Обратите внимание, что если вы видите нули между строковыми символами, тогда целевой процесс использует Unicode-версию представления списка.Затем необходимо прочитать Marshal.PtrToStringUnicode.

1 голос
/ 20 марта 2011

Вы пояснили, что пытаетесь прочитать элементы из элемента управления представлением списка в 32-битном процессе в другой 64-битный процесс.

Я видел много вопросов на эту тему на разных форумах, и ни один из них, казалось, никогда не достигал успешного результата.

Я думаю, что ваш лучший вариант - создать 32-битный исполняемый файл, который сможет считывать данные из списка другой программы.

0 голосов
/ 12 декабря 2013

Извините, мой ответ так поздно, но я только что столкнулся с той же проблемой.Вот структура, которую я использовал для VB.NET, которая работает как в 32-, так и в 64-битных системах.

<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure LV_ITEM
    Public Mask As UInteger
    Public Index As Integer
    Public SubIndex As Integer
    Public State As Integer
    Public StateMask As IntPtr
    Public Text As String
    Public TextLength As Integer
    Public ImageIndex As Integer
    Public LParam As IntPtr
End Structure
...