winapi SendMessage LB_GETTEXT возвращает только 8 символов

Gustly
Дата: 09.07.2014 12:15:19
Добрый день.

Такая проблема. Пытаюсь из ListBox вытащить значение.
Dim V$ = Space(99)
MsgBox(SendMessage(wnd, LB_GETTEXT, 15, V))
MsgBox("$" + V + "$")

2-е окно выдает $ + 8 символов + непонятный символ. Замыкающего $ нет. Сама SendMessage возвращает почему-то 8, вместо 9 и более, если другие числа подставлять.

В чем может быть проблема?
13-й квартал
Дата: 09.07.2014 16:27:45
Gustly
Dim V$ = Space(99)
0) Это по-каковски?

1) Любой вопрос по API, в котором не приведено описание API (Declare), можно сразу отправлять в топку. Потому, что иначе гадание и потеря времени. Вангую описание lParam без ByVal.

2) Лучше приложить к сообщению минимальный проект, на котором наблюдается эдефект.

3) Замыкающего "$" MsgBox не выдаёт из-за того, что в строке есть символ с кодом 0, считающийся окончанием строки. Багофича.
Gustly
Дата: 09.07.2014 16:47:00
13-й квартал,

Спасибо что ответили.

0) Это по-каковски?
Win7 x64, visual studio express 2012, visual basic project

1) Любой вопрос по API, в котором не приведено описание API (Declare), можно сразу отправлять в топку. Потому, что иначе гадание и потеря времени. Вангую описание lParam без ByVal.
Код в спойлере

2) Лучше приложить к сообщению минимальный проект, на котором наблюдается эдефект.
Код в спойлере

3) Замыкающего "$" MsgBox не выдаёт из-за того, что в строке есть символ с кодом 0, считающийся окончанием строки. Багофича.
Увы. Дефект словил только в целевой проге. Если я в хэндл передаю ListBox из моего окошка, вроде как строку полностью возвращает.
Например я вижу слово "Functions" возвращает "Function" а LB_GETTEXTLEN = 46
"frame_templates" возвращает "frame_te" а LB_GETTEXTLEN = 58.
При этом сама LB_GETTEXT всегда возвращает 8, если я объявлял SendMessageA и 4 если SendMessageW

+

Public Class Form1
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
    Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Integer) As Long

    Public Declare Function SendMessageCallback Lib "user32" Alias "SendMessageCallbackA" (ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, lParam As String, ByVal lpResultCallBack As Long, ByVal dwData As Long) As Long
    Private Const WM_CHAR = &H102
    Private Const WM_SETTEXT = &HC
    Private Const WM_GETTEXT = &HD
    Private Const VK_RETURN = &HD
    Private Const LB_ADDSTRING = &H180
    Private Const LB_INSERTSTRING = &H181
    Private Const LB_DELETESTRING = &H182
    Private Const LB_SELITEMRANGEEX = &H183
    Private Const LB_RESETCONTENT = &H184
    Private Const LB_SETSEL = &H185
    Private Const LB_SETCURSEL = &H186
    Private Const LB_GETSEL = &H187
    Private Const LB_GETCURSEL = &H188
    Private Const LB_GETTEXT = &H189
    Private Const LB_GETTEXTLEN = &H18A
    Private Const LB_GETCOUNT = &H18B
    Private Const LB_SELECTSTRING = &H18C
    Private Const LB_DIR = &H18D
    Private Const LB_GETTOPINDEX = &H18E
    Private Const LB_FINDSTRING = &H18F
    Private Const LB_GETSELCOUNT = &H190
    Private Const LB_GETSELITEMS = &H191
    Private Const LB_SETTABSTOPS = &H192
    Private Const LB_GETHORIZONTALEXTENT = &H193
    Private Const LB_SETHORIZONTALEXTENT = &H194
    Private Const LB_SETCOLUMNWIDTH = &H195
    Private Const LB_ADDFILE = &H196
    Private Const LB_SETTOPINDEX = &H197
    Private Const LB_GETITEMRECT = &H198
    Private Const LB_GETITEMDATA = &H199
    Private Const LB_SETITEMDATA = &H19A
    Private Const LB_SELITEMRANGE = &H19B
    Private Const LB_SETANCHORINDEX = &H19C
    Private Const LB_GETANCHORINDEX = &H19D
    Private Const LB_SETCARETINDEX = &H19E
    Private Const LB_GETCARETINDEX = &H19F
    Private Const LB_SETITEMHEIGHT = &H1A0
    Private Const LB_GETITEMHEIGHT = &H1A1
    Private Const LB_FINDSTRINGEXACT = &H1A2
    Private Const LB_SETLOCALE = &H1A5
    Private Const LB_GETLOCALE = &H1A6
    Private Const LB_SETCOUNT = &H1A7
    Private Const CB_GETLBTEXT = &H148

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Text = "asd"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim hwnd As Long, wnd As Long, wnd2 As Long, st As String

        'Me.TextBox1.Text =
        FindForm().Text = "zxc"
        st = Space(99)
        Dim V$ = Space(16)
        MsgBox("$" + st + "$")
        hwnd = FindWindow(vbNullString, "Прога")
        MsgBox(hwnd.ToString)
        GetClassName(529602, st, 10)
        'wnd = FindWindowEx(hwnd, 0&, "Afx:00400000:8:00000000:011000A8:00000000", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd = FindWindowEx(hwnd, 0&, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd2 = FindWindowEx(wnd, 0, "Afx:00400000:0:00010005:00000000:00000000", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd2 = FindWindowEx(wnd, 0, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd2 = FindWindowEx(wnd, wnd2, "Afx:00400000:0:00010005:00000000:00000000", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd2 = FindWindowEx(wnd, wnd2, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd = FindWindowEx(wnd, wnd2, "Afx:00400000:0:00010005:00000000:00000000", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd = FindWindowEx(wnd, wnd2, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd = FindWindowEx(wnd, 0&, "AfxFrameOrView70", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd = FindWindowEx(wnd, 0&, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd = FindWindowEx(wnd, 0&, "ListBox", vbNullString) 'получаем hwnd текстбокса блокнота
        wnd = FindWindowEx(wnd, 0&, vbNullString, vbNullString) 'получаем hwnd текстбокса блокнота
        'MsgBox(wnd.ToString)
        'wnd = 529602
        Call GetClassName(wnd, st, 99)
        'TextBox1.Text = st
        'Call SendMessage(wnd, WM_SETTEXT, 0, "Test2")
        'MsgBox(LB_FINDSTRING)
        'MsgBox(SendMessage(TextBox1.Text, LB_GETTEXT, 1, V))
        MsgBox("Len13=" + CStr(SendMessage(wnd, LB_GETTEXTLEN, 13, 0)))
        MsgBox("Len14=" + CStr(SendMessage(wnd, LB_GETTEXTLEN, 14, 0)))
        MsgBox("Len15=" + CStr(SendMessage(wnd, LB_GETTEXTLEN, 15, 0)))
        MsgBox(SendMessage(wnd, LB_GETTEXT, 13, V))
        'MsgBox(SendMessageCallback(463934, LB_GETTEXT, 1, V, 0, 0))
        'MsgBox(SendMessage(wnd, LB_SELECTSTRING, 0, "DRM"))
        MsgBox(Len(V))
        RichTextBox1.Text = V
        MsgBox("$" + V + "$")
        'Call SendMessage(wnd, EM_SETSEL, 6, "") 'сдвигаем курсор на 6 знаков
        'Call SendMessage(wnd, WM_CHAR, VK_RETURN, vbNullString) 'нажимаем Enter
    End Sub

    Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged

    End Sub
End Class


Модератор: Тема перенесена из форума "Visual Basic".
Gustly
Дата: 08.09.2014 16:29:39
Возвращаясь к теме. Может ли это быть из-за того, что целевое приложение очень старое и 32-битное, а я пишу в х64 винде. Можно ли как-то подключить user32.dll с sendmessage например от windows XP или 98 ?
bazile
Дата: 08.09.2014 18:55:11
Gustly, нет дело не в разрядности винды. Нет не надо даже пытаться заменить user32 более старой версией. Что нужно так это переписать код посылки LB_GETTEXT:
+
Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW"(ByVal hWnd As IntPtr, ByVal msg As UInteger, _
	ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW"(ByVal hWnd As IntPtr, ByVal Msg As UInteger, _
   ByVal wParam As IntPtr, ByRef lParam As StringBuilder) As IntPtr

Const LB_GETTEXT    As UInteger = &h0189
Const LB_GETTEXTLEN As UInteger = &h018A
Dim Shared Readonly LB_ERR As New IntPtr(-1)

Private Shared Function GetListBoxItemText(hwndListBox As IntPtr, itemIdx As Integer) As String
	Dim textLen As IntPtr = SendMessage(hwndListBox, LB_GETTEXTLEN, itemIdx, IntPtr.Zero)
	If textLen = LB_ERR Then Return ""
	
	Dim sb As New StringBuilder(textLen.ToInt32() + 1)
	SendMessage(hwndListBox, LB_GETTEXT, itemIdx, sb)
	Return sb.ToString()
End Function

Важным является использование StringBuilder в качестве четвертого аргумента.
Gustly
Дата: 09.09.2014 11:45:55
bazile,

Спасибо за код, но

Dim hwndListBox As IntPtr = 12662938
Dim textLen As IntPtr = SendMessage(hwndListBox, LB_GETTEXTLEN, 4, IntPtr.Zero)
Dim sb As New StringBuilder(textLen.ToInt32() + 1)
'SendMessage(hwndListBox, LB_GETTEXT, 1, sb)

4-я строка вызывает
+
An unhandled exception of type 'System.AccessViolationException' occurred in System.Windows.Forms.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

При этом textLen всегда 8 символов, независимо от 3-го параметра (wParam The zero-based index of the string. )
Где-то в степи
Дата: 09.09.2014 11:53:03
Gustly,
Не могу понять что Вы добиваетесь. Если передать текстовую строку через меседж, имхо вектор рассуждений не пральный, есть общепринятые решения для этого дела, Пож?
bazile
Дата: 09.09.2014 12:23:55
Gustly, почему ты получаешь длину для элемента 4, а потом просишь текст элемента 1? Или все элементы имеют одинаковую длину?
Gustly
Дата: 09.09.2014 12:34:35
Где-то в степи,

Конечная цель - поиск по listbox в стороннем приложение. Но сейчас банально не могу получить правильное значение выбранного элемента. Если я указываю handle от listbox в моем приложение, то через win api все нормально возвращается, если же handle от целевого listbox, то не более 8 символов.

bazile

Код закомментарен, поэтому там 1, а вообще я разные индексы пробовал, везде одна и та же ошибка.

Вообще если сделать объявление не SendMessageW, а SendMessageA , то возвращаемая длина становится разной, но все равно непонятной.

У "!TEST.SQS" - возвращает 27, У "!TEST23.SQS" возвращает 29
Gustly
Дата: 09.09.2014 12:40:15
Сейчас вот поменял в объявление функции

ByRef lParam As StringBuilder

на

ByVal lParam As StringBuilder

Ошибка исчезла, но возвращается беда.

+
!TEST23.°2BЂъяя
!TEST23.xъЅД
!TEST23.°2BЂъяя
!TEST23.xъЅД
!TEST23.xъЅД
!TEST23.xъЅД
!TEST23.°2BЂъяя
!TEST23.xъЅД
!TEST23.xъЅД
!TEST23.xъЅД
!TEST23.°2BЂъяя


Вместо "!TEST23.SQS"

То есть вернул 8 символов + непонятно что