Форум программистов, компьютерный форум, киберфорум
Наши страницы
The trick
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Работа с указателями в VB6

Запись от The trick размещена 18.04.2014 в 02:25
Обновил(-а) The trick 03.06.2014 в 23:19

Для начала нужные декларации
Visual Basic
1
2
Public Declare Function GetMem4 Lib "msvbvm60" (src As Any, Dst As Any) As Long
Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (src() As Any) As Long
Часто встречаются ситуации когда нужно получить данные имея только адрес (нампример в WndProc, HookProc). Обычно просто копируют данные через CopyMemory в структуру, после изменяют данные и копируют все обратно. Если структура большая, соответствено будут тратиться ресурсы на копирование туда-сюда. В таких языках как C++ все делается проще благодаря указателям, пишется что-то типа newpos=(WINDOWPOS*)lparam и все. Штатно VB6 не поддерживает работу с указателями, но есть несколько обходных путей.
Например можно получить доступ к адресу переменной в стеке переданной по ссылке. Например так.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Private Type Vector
    X As Single
    Y As Single
End Type
Private Type TestRec
    Name As String
    Value As Long
    Position As Vector
    Money As Double
End Type
 
Private Sub Form_Load()
    Dim tr As TestRec
    Test tr
End Sub
 
Private Function Test(Pointer As TestRec, Optional ByVal nu As Long)
    Dim q As TestRec, z As TestRec
    
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
    
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
    
    GetMem4 VarPtr(q), ByVal VarPtr(nu) - 4     ' Устанавливаем указатель на q (Pointer = &q)
    
    PrintRec Pointer
    
    GetMem4 VarPtr(z), ByVal VarPtr(nu) - 4     ' Устанавливаем указатель на z (Pointer = &z)
    
    PrintRec Pointer
    
End Function
 
Private Sub PrintRec(Pt As TestRec)
    Debug.Print "----------------"
    Debug.Print "Name = " & Pt.Name
    Debug.Print "Value = " & Pt.Value
    Debug.Print "Money = " & Pt.Money
    Debug.Print "Position.X = " & Pt.Position.X
    Debug.Print "Position.Y = " & Pt.Position.Y
End Sub
Также можно создать указатель используя массивы. Идея состоит в создании 2-х массивов с 1 элементом каждый, один будет хранить адрес переменной, другой будет ссылаться на эти данные. Первый всегда Long, второй нужный тип данных. Это удобно например если нужно проходить по спискам и т.п.
Ни для кого не секрет что массив в VB представляет собой обычный SafeArray массив. В структуре данных такого массива содержится много полезной информации, также есть указатель на данные. Что мы делаем, мы создаем 2 массива:
1-й (с адресом) ссылается на указатель на данные второго массива. В итоге изменяя значения в первом массиве, 2-й автоматически будет ссылаться на нужные данные.
2-й с непосредственно с даными, на который указывает первый.
Так же после всех манипуляций нужно вернуть все указатели назад, чтобы VB корректно очистил память.
Для всех манипуляций я создал вспомогательные функции и структуру для восстановления данных.
Адрес SafeArray можно получить через Not Not Arr, но в IDE после таких манипуляций идут глюки с вычислениями с плавающей точкой.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Public Type PtDat
    Prv1 As Long
    Prv2 As Long
End Type
 
' Создать указатель. 1-й параметр указатель, 2-й значение указателя
Public Function PtGet(Pointer() As Long, ByVal VarAddr As Long) As PtDat
    Dim i As Long
    i = GetSA(ArrPtr(Pointer)) + &HC
    GetMem4 ByVal i, PtGet.Prv1
    GetMem4 VarAddr + &HC, ByVal i
    PtGet.Prv2 = Pointer(0)
End Function
' Освободить указатель
Public Sub PtRelease(Pointer() As Long, prev As PtDat)
    Pointer(0) = prev.Prv2
    GetMem4 prev.Prv1, ByVal GetSA(ArrPtr(Pointer)) + &HC
End Sub
' Получить адрес SafeArray
Public Function GetSA(ByVal addr As Long) As Long
    GetMem4 ByVal addr, GetSA
End Function
Пример использования.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Private Sub Form_Load()
    Dim pt() As Long, var() As TestRec, prev As PtDat       ' Указатель, данные указателя, данные востановления
    Dim q As TestRec, z As TestRec                          ' Структуры, на которые мы будем ссылаться
    
    ReDim pt(0): ReDim var(0)
 
    q.Name = "The trick"
    q.Position.X = 5: q.Position.Y = 15
    q.Value = 12345: q.Money = 3.14
    
    z.Name = "Visual Basic 6.0"
    z.Position.X = 99: z.Position.Y = 105
    z.Value = 7643: z.Money = 36.6
    
    prev = PtGet(pt, GetSA(ArrPtr(var)))                    ' Создаем "указатель"
 
    pt(0) = VarPtr(q)                                       ' Устанавливаем указатель на q (pt = &q)
    PrintRec var(0)
    pt(0) = VarPtr(z)                                       ' Устанавливаем указатель на z (pt = &z)
    PrintRec var(0)
 
    PtRelease pt, prev                                      ' Возвращаем все на место
 
End Sub
Размещено в Без категории
Просмотров 2255 Комментарии 8
Всего комментариев 8
Комментарии
  1. Старый комментарий
    Запись от уни размещена 31.05.2014 в 22:39 уни вне форума
  2. Старый комментарий
    Цитата:
    Сообщение от уни Просмотреть комментарий
    Спасибо. Я читал эту статью
    Запись от The trick размещена 31.05.2014 в 22:42 The trick вне форума
  3. Старый комментарий
    Аватар для Dragokas
    По первому коду:
    Visual Basic
    1
    
    VarPtr(nu) - 4
    Правильно ли я понимаю - последние 4 байта переменной Pointer содержат адрес структуры, на которую она указывает ?

    Можно ли тоже самое вычислить примерно так? :
    Visual Basic
    1
    
    VarPtr(Pointer) + len(Pointer) - 4
    Запись от Dragokas размещена 15.11.2014 в 23:28 Dragokas вне форума
  4. Старый комментарий
    Цитата:
    Правильно ли я понимаю - последние 4 байта переменной Pointer содержат адрес структуры, на которую она указывает ?
    Нет. Реально функция представляет собой такую:
    Visual Basic
    1
    2
    
    Private Function Test(ByVal AddressPointer As Long, Optional ByVal nu As Long)
    End Function
    Т.к. параметры передаются через стек задом-наперед то стек принимает такой вид (образно):
    Code
    1
    2
    3
    4
    
    Адрес     Значение
     
    0000000   AddressPointer
    0000004   nu
    AddressPointer - указатель на структуру Pointer, VB6 обращается к этой структуре внутри этой функции по этому указателю. Теперь если мы поменяем значение AddressPointer, то оно будет указывать на что-то другое - в этом смысл. Напрямую мы не можем узнать значение AddressPointer, т.к. если выполнить VarPtr(Pointer) то мы фактически получим VarPtr(Pointer.Name). Но т.к. мы знаем правило размещения параметров в стеке, мы можем узнать этот адрес из переменной nu, поэтому адрес будет равен VarPtr(nu) - 4.
    Запись от The trick размещена 15.11.2014 в 23:40 The trick вне форума
  5. Старый комментарий
    Аватар для Dragokas
    Понятно. Т.е. VarPtr(nu) - это будет адрес в стеке.
    А VarPtr(Pointer) - адрес самой структуры, а не адрес указателя на нее.
    Запись от Dragokas размещена 15.11.2014 в 23:57 Dragokas вне форума
  6. Старый комментарий
    Цитата:
    Сообщение от Dragokas Просмотреть комментарий
    Понятно. Т.е. VarPtr(nu) - это будет адрес в стеке.
    А VarPtr(Pointer) - адрес самой структуры, а не адрес указателя на нее.
    Да.
    Запись от The trick размещена 15.11.2014 в 23:58 The trick вне форума
  7. Старый комментарий
    Аватар для Dragokas
    Можно еще теоретический вопрос:
    nu - хранит данные в стеке.
    Pointer - хранит в стеке ссылку, которая ведет на данные в куче.
    Как система хранит информацию о типе данных, на которые ссылается переменная (указатель это или данные) ?
    Запись от Dragokas размещена 16.11.2014 в 00:29 Dragokas вне форума
  8. Старый комментарий
    Цитата:
    Сообщение от Dragokas Просмотреть комментарий
    Как система хранит информацию о типе данных, на которые ссылается переменная (указатель это или данные) ?
    В данном случае в скомпилированном виде никак. Указатель можно переписать на что угодно и он будет это интерпретировать как структуру если сможет.
    Запись от The trick размещена 16.11.2014 в 18:42 The trick вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru