Как устроить такой фокус: строка как байтовый массив

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Как устроить такой фокус: строка как байтовый массив

Сообщение arthur2 » 28.06.2008 (Сб) 23:44

Возможно ли, и как, если возможно, проделать такой фокус: чтобы некая переменная-строка и некая переменная - массив байтов ссылались на один и тот же участок памяти? Чтобы изменение байтового массива было бы одновременно и изменением строки?

На сколько я понимаю, такое должно получиться, если та и другая переменная будут пользоваться одним и тем же указателем... Я правильно понимаю?
Последний раз редактировалось arthur2 01.07.2008 (Вт) 13:45, всего редактировалось 1 раз.
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 29.06.2008 (Вс) 0:03

Да.
Об этом можно прочитать в моей статье [ МануAll ] Об указателях в VB6.
Об этом также следует прочитать статью GSerg'a о работе с SAFEARRAY.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 01.07.2008 (Вт) 13:42

Хакер
Да, спасибо - именно с этих статей и появилась сама идея. Но, к сожалению, обе они сложны для моего понимания. Тем не менее, сделат такой эксперемент:

Код: Выделить всё
Public Sub test()
    Dim s As String, t As String, stab As Long
   
   s = "траляля"
   stab = VarPtr(stab)
   
   GetMem4 stab + 8, stab + 4
   s = "шушушу"
   Debug.Print t
   Debug.Print s
End Sub



Получился очень странный, по-моему, результат. Строка s полностью скопировалась в t, но именно скопировалась - её изменение на t не сказывается. Значит, вышло всё же две независимые строки?

Не понятно, как такое получилось: реально ведь я скопировал только 4 байта?

Если t объявить t() as byte, код вообще не срабатывает. Выходит, строки и массивы байт всё же устроены по-разному?
Артур
 
   

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 01.07.2008 (Вт) 14:29

arthur2, если уж тебя не устраивает управляемая память, то без понимания, что такое указатели, тебе лучше не спешить.
Когда ты выполняешь строку s = "шушушу", то ты выделяешь новую область памяти и создаешь новый указатель на эту область. Естественно, изменения в новой области памяти никак не будут сказываться на первоначальной.
Выполни вместо этой строки строку Mid$(s,2,3)="123".
Lasciate ogni speranza, voi ch'entrate.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 01.07.2008 (Вт) 14:52

alibek
Спасибо, понял! Работает... Но не работает после компиляции. В смысле, t остаётся пустой. В экзешнике указатели идут не подряд?
Артур
 
   

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 01.07.2008 (Вт) 15:09

Если ты хочешь сопоставить строку и байтовый массив, то тебе не VarPtr нужен, а StrPtr.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 01.07.2008 (Вт) 15:10

arthur2 писал(а):stab = VarPtr(stab)

Что ты хотел этим сказать?
Lasciate ogni speranza, voi ch'entrate.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 01.07.2008 (Вт) 15:35

Переписан на вот так:
Код: Выделить всё
test 0, "", ""


Код: Выделить всё
Private Sub test(ByVal stub As Long, t As String, s As String)
   
   s = "траляля"
   stub = VarPtr(stub)
   
   GetMem4 stub + 8, stub + 4
   MsgBox t
'   Mid$(s, 1) = "шушушу"
   s = "шушушу"
   MsgBox t
   MsgBox s
   
End Sub

Стало работать и после компиляции. Причём, можно уже строку менять и без mid (я так понял, это потому, что бейсик уже не может выделять другой адрес, ведь вернуть-то нужно тот же самый аргумент? Позже: нет, не поэтому - на выходе у строк должны быть уже другие адреса, иначе внутри test их бы не удалось вычислить, прибавлением по 4. Значит, адреса у аргументов временные?)

Осталось понять, как проделать тот же фокус, но чтобы сопоставить строку не строке, а массиву байт.

Что ты хотел этим сказать?
Что просто считаю адреса переменых от адреса заглушки, прибавляя по 4.
Артур
 
   

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 02.07.2008 (Ср) 19:10

В общем, совмесными усилиями вот здесь:
http://forum.sources.ru/index.php?showtopic=241951
насочинялся такой код:
Код: Выделить всё
Option Explicit

Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (ByRef pArr() As Any) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal pSrc As Long, ByVal pDst As Long)
Public Declare Function PutMem4 Lib "msvbvm60" (ByVal pDst As Long, ByVal NewValue As Long) As Long

Public Function stringIsBytes(s As String, b() As Byte) As Long 'функция будет возвращать адрес
                                                                'данных массива до подмены
                                                                '(или ноль, если массив уже привязан к строке)

    Dim pSa As Long
    GetMem4 ArrPtr(b), VarPtr(pSa) 'в pSa получить адрес на SAFEARRAY массива
   
    If pSa = 0 Then 'массив ещё не проредимлен
      ReDim b(0) As Byte
      b(0) = 98 'чтобы проверить перед уничтожением, а вернулись ли данные на место.
      GetMem4 ArrPtr(b), VarPtr(pSa)
     
      Dim pOld As Long
      GetMem4 (pSa + 12), VarPtr(pOld) 'запоминаем данные по этому адресу (потому что мы их сейчас перепишем)
      stringIsBytes = pOld 'функция возвращает адрес старых данных
    End If
   
   
    Dim pStr As Long
    pStr = StrPtr(s) ' адрес данных строки

    GetMem4 VarPtr(pStr), (pSa + 12) 'подмена данных массива данными строки
   
    PutMem4 pSa + 16, LenB(s) 'записать длинну массива в SAFEARRAY.rgsabound.cElements
   
End Function


Public Sub KillBytes(b() As Byte, pOld As Long) 'нельзя просто Erase массив, иначе будет утечка памяти
      Dim pSa As Long
      GetMem4 ArrPtr(b), VarPtr(pSa) 'получаем адрес SAFEARRAY
      If pSa Then
        GetMem4 VarPtr(pOld), (pSa + 12)  ' записываем старые данные на место
        PutMem4 pSa + 16, 1 ' записываем старую длинну (у нас был там всего один меченый байт)
        Debug.Print b(0) 'проверяем, вернулись ли старые данные на место (должно быть 98)
        Erase b
      End If
End Sub


Гляньте, кому не лень - есть косяки?

Специально пометил неприкаянный байт перед подменой, чтобы проверить, вернётся ли он на место перед уничтожением. Вернулся.
Артур
 
   

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 03.07.2008 (Чт) 8:29

А с функциями SAFEARRAY работать было бы не проще? Тем более, что и ДВА кирпича для работы с этой структурой лежат.
Весь мир матрица, а мы в нем потоки байтов!

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Сообщение arthur2 » 03.07.2008 (Чт) 10:30

Пока не понятно - не проще (копипаст не в счёт). А то, чего я тут нагородил, мне понятно.

Впрочем, теперь (кстати, именно благодаря этим самостоятельным ковыряниям), стали понятнее и SAFEARRAY-и. Переписал на вот так:

Код: Выделить всё
Public Sub stringIsBytes(s As String, b() As Byte)

    Dim psa As Long, pAr As Long
    pAr = ArrPtr(b)'адрес массива
    GetMem4 pAr, VarPtr(psa) 'адрес SAFEARRAY массива
   
    If psa = 0 Then 'массив ещё не проредимлен
       SafeArrayAllocDescriptor 1, ByVal pAr 'выделяем новый дескриптор массива и память под него
       GetMem4 pAr, VarPtr(psa) 'получаем дескриптор SAFEARRAY
       PutMem4 psa + 4, 1 'записываем размер элемента массива (массив байтовый, так что 1)
    End If
   
    Dim pStr As Long
    pStr = StrPtr(s) ' адрес данных строки

    GetMem4 VarPtr(pStr), (psa + 12) 'подмена данных массива данными строки
   
    PutMem4 psa + 16, LenB(s) 'записать длинну массива в SAFEARRAY.rgsabound.cElements
   
End Function

Public Sub DestroySAFEARRAY(b() As Byte)
  Dim psa As Long, pAr As Long
  pAr = ArrPtr(b)
  GetMem4 pAr, VarPtr(psa)
  If psa Then
    SafeArrayDestroyDescriptor ByVal psa
    PutMem4 pAr, 0
  End If
End Sub

Артур
 
   


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: AhrefsBot и гости: 33

    TopList