none
Получить значение ListBox из другой программы методом SendMessage LB_GETTEXT. RRS feed

  • Вопрос

  • Добрый день, пытаюсь получить значения строка с помощью этой функции, рабочий код, но когда я пытаюсь получить значения с ListBox из интересующей меня программы, он уже выдает только - ". Q0", Дополнительно проверял индекс выделенной строки и количество строк выдаются верно, может быть это связано с тем, что ListBox multiColumns в этой программе?

    Буду очень признателен за ответ. Времени на поисковики потратил много, а решения не нашел.

        Function GetListBoxItem(ByVal ControlHandle As IntPtr, ByVal ItemIndex As Integer)
    
            Dim RetVal As String = ""
            Dim RetValLength As Integer
            Const LB_GETTEXT = &H189
            Const LB_GETTEXTLEN = &H18A
    
            RetValLength = SendMessage(ControlHandle, LB_GETTEXTLEN, ItemIndex, 0&)
    
            If RetValLength = -1 Then
                MessageBox.Show("Error retrieving item length")
                Return Nothing
            End If
    
            Dim h As IntPtr = Marshal.AllocHGlobal(RetValLength)
    
            RetValLength = SendMessage(ControlHandle, LB_GETTEXT, ItemIndex, h)
    
            If RetValLength = -1 Then
                MessageBox.Show("Error retrieving item")
                Return Nothing
            End If
    
            RetVal = Marshal.PtrToStringUni(h)
    
            Marshal.FreeHGlobal(h)
    
            Return RetVal
    
        End Function

Ответы

  • Может быть в этой программе реализован перехват этих сообщений и/или кастомная отрисовка элементов (Owner Draw)? Тогда попытка "вытащить" строки не имеет смысла, т.к. сам ListBox ими не владеет.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

Все ответы

  • Хорошо бы посмотреть содержимое памяти, адрес которой хранится в h. Может быть возвращается не Юникод-строка, как Вы предполагаете, а Ansi?

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Я про кодировку думал, что она не верная, пробовал Marshal.PtrToStringUni(h), но результат практически такой же - "pQ", а строки в ListBox не короткие, и результат одинаковый для любой строки в ListBox "pQ", а строки разные. Могут еще какие-нибудь причины не правильной работы?
     
    PS Как посмотреть содержимое памяти?
    Буду очень признателен за ответ.
  • Если результат одинаковый, вероятно буфер h вовсе не заполняется, нужно "ловить" ошибку в работе SendMessage. Что она, кстати, возвращает?

    А память можно посмотреть в отладчике. Поставьте точку прерывания после вызова SendMessage, запустите программу в режиме отладки (F5), дождитесь, когда она остановится, откройте окно отображения областей памяти "Отладка - Окна - Память - Память 1", введите в поле адреса значение переменной h и нажмите Enter.


    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Спасибо за быстрый ответ.

    SendMessage всегда возвращает 2.

    SendMessage(ControlHandle, LB_GETTEXTLEN, ItemIndex, 0&) всегда равна 4.

    При этом обращение к ListBox, количество строк правильно определяется, и номер выделенной строки тоже верно показывается.

    Вы процитировали MSDN - но я пробовал, F5 - Отладка - Окна - (но окна память я не вижу, наверное, что-то не понимаю), у меня окна: Вывод, Интерпретация, Локальные, Контрольное значение.


    • Изменено monteloro 26 мая 2014 г. 21:13
  • Я не MSDN цитирую, а объясняю, как открыть окно "Память". Вы отладчиком VS пользовались когда-нибудь? Окна отладчика в большинстве своем доступны, когда VS находится в режиме отладки, а не в режиме редактирования кода! Поэтому, чтобы открыть это окно, Ваша программа должна быть под отладчиком в приостановленном состоянии. Нужна точка прерывания (или точка остановки) в теле интересующей функции.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Я очень признателен, что Вы мне помогаете разобраться, все правильно объясняете, просто я не могу понять в чем причина.

    Да, пользовался, точку остановки устанавливаю после вызова SendMessage, либо F9, либо dblClick. Начинаю отладку - F5.  Меню Отладка - Окна: Вывод, Интерпретация, Локальные, Контрольное значение, Стек вызовов. Окно память, я не вижу.

    Если в режиме редактирования, то только два окна: Вывод, Интерпретация.

    У меня Microsoft Visual Basic 2010 Express.

  • У меня, к сожалению, нет под рукой VS2010 Express, но припоминаю, что там была настройка по поводу отображения краткого или полного меню. Поищите в меню Сервис или в Параметрах.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Я нашел вот такой пункт меню:

    Сервис > Параметры > Расширенные параметры, стояли Основные параметры

    но окно Память не появилось. Как Вы думаете, а точно в VS2010 Express, есть окно Память?

    С уважением.

  • Я попробую установить у себя и отпишусь позднее.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Спасибо большое, буду Вам очень признателен.
  • К сожалению, я нашел вот такую информацию:

    http://msdn.microsoft.com/ru-ru/library/vstudio/s3aw423e%28v=vs.100%29.aspx

    Express

    Тема не применяется

    Pro, Premium и Ultimate

    Тема применяется

    В Visual Basic Express не поддерживается, поэтому я и окно Память не смог найти.
  • Я установил VS2012 Express, в режиме отладки появилась окно Память, ввожу значение h, как есть в адрес - поток Net.systemsEvents вот такая первая строка,

    1f f0 51 00 30 00 00 00 38 c7 c6 7d 3a 00 00 8c 38 09 08 04 6e 00 64 00 3e c7 c6  .рQ.0...8ЗЖ}:..Њ8...n.d.>ЗЖ

    функция возвращает pQ

    На всякий случай полный код:

    Imports System.Runtime.InteropServices Public Class Form1

        <DllImportAttribute("user32.dll")> _
        Public Shared Function SendMessage(hWnd As IntPtr, Msg As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
        End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox(GetListBoxItem(&H204A4, 0)) End Sub Function GetListBoxItem(ByVal ControlHandle As IntPtr, ByVal ItemIndex As Integer) Dim RetVal As String = "" Dim RetValLength As Integer Const LB_GETTEXT = &H189 Const LB_GETTEXTLEN = &H18A RetValLength = SendMessage(ControlHandle, LB_GETTEXTLEN, ItemIndex, 0&) If RetValLength = -1 Then MessageBox.Show("Error retrieving item length") Return Nothing End If Dim h As IntPtr = Marshal.AllocHGlobal(RetValLength) RetValLength = SendMessage(ControlHandle, LB_GETTEXT, ItemIndex, h) If RetValLength = -1 Then MessageBox.Show("Error retrieving item") Return Nothing End If RetVal = Marshal.PtrToStringAnsi(h) Marshal.FreeHGlobal(h) Return RetVal End Function End Class

    Заранее благодарю за помощь.

    Значение ControlHandle я вставляю из Spy++.





    • Изменено monteloro 27 мая 2014 г. 16:39
  • Обычно, дескрипторы объектов Windows уникальны для данного процесса (приложения). Spy может дать значение, неподходящее для Вас. Окно нужно искать самому и получить свой дескриптор.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Я попробую, но думаю не поможет, используя Handle полученного SPY++, другие функции правильно работают, возвращают выделенную строку или количество строк ListBox. Только сама строка не возвращается, буфер заполняется только pQ.
  • Я проверил программный способ поиска дает точно такой же Handle, что Microsoft Spy++. Обращение к этому ListBox, c функциями дает корректный результат, некорректный результат, только при
    LB_GETTEXTLEN  
    LB_GETTEXT     
    длина 4, результат pQ для любой строки ListBox.
  • Создал диалоговое приложение MFC с ListBox-ом, заполнил его строками Юникод. С помощью Spy++ определил дескриптор ListBox-а. Запустил Ваш код с данным дескриптором. Возвращается корректная ANSI-строка. Если же вызвать PtrToStringUni - получается мусор. Установка/сброс стиля multicolumn на результат не влияет.

    Память на которую указывает h посмотреть в отладчике не могу, пишет "Не удается вычислить выражение". Впрочем, VB - не моя стихия, может быть что-то не так сделал. Однако "вытащить" содержимое памяти получилось копированием его в управляемый массив:

    Dim mas(RetValLength) As Byte
    Marshal.Copy(h, mas, 0, RetValLength)

    В нем четко видно однобайтовые символы строки.

    Вы уверены, что окно, к которому Вы обращаетесь - это ListBox? Что за программа, можете ее выложить куда-нибудь?


    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Добрый день, спасибо за ответ.

    По многим признакам это ListBox:

    1. Microsoft Spy++ определяет Class: Listbox

    2. Функция из user32.dll GetClassName тоже возвращает Listbox

    3. Можно обращаясь к нему, как ListBox получать различные значения, выделив строку мышкой, программно можно определить номер выделенной строки или количество строк в этом объекте.

     Но, странное поведение, например: при добавлении 5 строк, на экране ничего не меняется, но если программно проверить, то количество строк увеличилось на +5, а если читать, вставленные новые строки, то опять какой-то мусор из нескольких символов. (в тестовых VB ListBox все правильно работает) Это каталог запчастей японского производителя на английском, программу с радостью бы Вам предоставил, но она с ключом на одном компьютере. Распечатать ListBox  в PDF можно и там эти строки в виде строк, а не картинка.

    Это я вставлял и проверял количество строк.

            Const LB_GETCOUNT As Integer = 395
            Const LB_ADDSTRING As Integer = 384
            Dim s As String = "Test String"
            Dim ptr As IntPtr = Marshal.StringToHGlobalUni(s)
            Dim hWnd As IntPtr = &H3B0894
            SendMessage(hWnd, LB_ADDSTRING, 0, ptr)
            Debug.Print(SendMessage(hWnd, LB_GETCOUNT, 0, 0))

  • Может быть в этой программе реализован перехват этих сообщений и/или кастомная отрисовка элементов (Owner Draw)? Тогда попытка "вытащить" строки не имеет смысла, т.к. сам ListBox ими не владеет.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

  • Думаю, что Вы правы, не стандартное использование ListBox, хотел добавить, что данные программа берет с удаленного сервера. Не подскажите, а какой может быть алгоритм решения или это тупик?
  • Взлом программ не по моей части. Ничем не могу помочь. И Вам не советую, это, действительно, тупик.

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!


    • Изменено kosuke904 31 мая 2014 г. 17:17
  • Все равно спасибо за помощь. Вывел в PDF и сделал обработчик через библиотеку Acrobat.