GSerg'у

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

GSerg'у

Сообщение Approximator » 02.07.2004 (Пт) 1:10

Привет, Сергей. У меня появились вопросы по поводу твоей статьи (про SAFEARRAY). Вот твой адаптированный пример, который почему-то у меня не пошёл:
Код: Выделить всё
Private Declare Function GetMem4 Lib "msvbvm60" _
(ByVal pSrc As Long, ByVal pDst As Long) As Long
Private Declare Function PutMem4 Lib "msvbvm60" _
(ByVal pDst As Long, ByVal NewValue As Long) As Long
Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" _
(arr() As Any) As Long
Private Declare Sub SafeArrayAllocDescriptor Lib "oleaut32.dll" _
(ByVal cDims As Long, ppsaOut As Any)
Private Declare Sub SafeArrayDestroyDescriptor Lib "oleaut32.dll" _
(psa As Any)

Private arrMain() As Long, arr1() As Long, arr2() As Long

Private Sub Form_Load()
  Dim i As Long
 
  ReDim arrMain(1 To 10)
 
  CreateSAFEARRAY ArrPtr(arr1), 4, VarPtr(arrMain(1)), 1, 5
 
  CreateSAFEARRAY ArrPtr(arr2), 4, VarPtr(arrMain(6)), 6, 10
 
  For i = 1 To 10
    arrMain(i) = i
  Next
   
  Me.AutoRedraw = True
  Debug.Print "Главный массив:"
  For i = 1 To 10
    Debug.Print arrMain(i)
  Next
  Debug.Print
 
  Debug.Print "Подмассив 1:"
  Debug.Print LBound(arr1)
  Debug.Print UBound(arr1)
  For i = 1 To 5
    Debug.Print arr1(i)
  Next
  Debug.Print
 
  Debug.Print "Подмассив 2:"
  Debug.Print LBound(arr2)
  Debug.Print UBound(arr2)
  For i = 6 To 10
    Debug.Print arr2(i)
  Next
 
End Sub

Private Function CreateSAFEARRAY(ByVal ppBlankArr As Long, ByVal ElemSize As _
Long, ByVal pData As Long, ParamArray Bounds()) As Long
  Dim p As Long, i As Long
 
 
  SafeArrayAllocDescriptor (UBound(Bounds) + 1) / 2, ByVal ppBlankArr
 
  GetMem4 ppBlankArr, VarPtr(p)
  PutMem4 p + 4, ElemSize
  PutMem4 p + 12, pData
 
  For i = 0 To UBound(Bounds) Step 2
    PutMem4 p + 16 + i * 4, Bounds(i + 1) - Bounds(i) + 1
    PutMem4 p + 20 + i * 4, Bounds(i)
  Next
End Function

Private Function DestroySAFEARRAY(ByVal ppArray As Long) As Long
  Dim p As Long
 
  GetMem4 ppArray, VarPtr(p)
  SafeArrayDestroyDescriptor ByVal p
  PutMem4 ppArray, 0
End Function

Private Sub Form_Unload(Cancel As Integer)
  DestroySAFEARRAY ArrPtr(arr1)
  DestroySAFEARRAY ArrPtr(arr2)
End Sub


Идея очень интересная, однако, массивы не создаются (UBound<LBound, то есть верхняя граница =–1, а нижняя =0; что, собственно, обычно свидетельствует о пустом массиве) – выдаётся девятая ошибка (subscript out of range). Насколько я понимаю, мои изменения не могли никак повлиять на работу программы. Где собака порылась? В чём ошибка?
Последний раз редактировалось Approximator 06.07.2004 (Вт) 5:00, всего редактировалось 1 раз.
С уважением, Approximator.

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 02.07.2004 (Пт) 1:55

Это, как говорится, «во первЫх строках письма», а далее... далее, вот что.
Ты уже заметил мой «нездоровый» интерес к строкам и данным, в них содержащимся. Что поделать – такова задача. Итак, понятное дело, что указатель на данные строки возвращается функцией StrPtr(). Однако, всем известно, что строковая переменная занимает <10 байт> + <длина строки> (по пару байт на символ). Очевидно, что функция VarPtr() от строки будет возвращать указатель на заголовок её структуры (на те самые 10 байт). Первое значение (Long) в структуре заголовка и есть StrPtr() этой строки, то есть это и есть собственно указатель на данные. Естественно, что в неинициализированной строке этот указатель равен 0. Что в остальных шести байтах мне не известно, известно только, что после инициализации они не изменяются. То есть после инициализации всего лишь прописывается указатель на данные (хотя, как мне кажется, ещё где-то должна быть длина строки или... или длину строки определяет какой-нибудь разделитель (символ окончания)? скорее всего, нет, imvho, должна где-то прописываться длина...). Сразу начинают «чесаться» руки (у меня) добиться возможности переписывать указатель на данные «ручками» (не важно чем Get/Put-Mem4’ом или CopyMem’ом, важно, чтобы самому). Мне это видится целесообразным для оптимизации производительности. Особливо, когда не предполагаешь изменять данные в этой строковой переменной, а сама строка предполагается длиннее четырёх байт. Но, как водится, при попытке переписать указатель на данные «ручками» происходит крушение IDE...
(кстати, памятуя о вопросе Lord’а Satan’а «об указателе на указатель...», у меня возникает стойкое ощущение, что он пытался спросить, если об этом, то уж точно о чём-то подобном; просто невнятно изложил свой вопрос)
Вопрос такой, есть ли (уверен, что должна быть) возможность делать это «ручками» из VB? И «паровозом», каково точное описание структуры заголовка строки, какие функции с ним работают? У меня есть подозрение, что функции, работающие с заголовками строк, подобные SafeArrayAllocDescriptor() просто обязаны быть.
Понятно, что если прямо этот вопрос не разрешится, то его можно будет решить «через одно место», то есть, через строковые массивы (главное тогда ответить на вопросы первой части). Тем не менее, работа со структурой SAFEARRAY представляется, если и не менее быстрой, то уж точно менее удобной... впрочем, если хотя бы она будет работать, то это уже будет здорово.
С уважением, Approximator.

BP
Бывалый
Бывалый
 
Сообщения: 234
Зарегистрирован: 17.02.2004 (Вт) 5:34
Откуда: Украина

Сообщение BP » 02.07.2004 (Пт) 19:14

Хм, а у меня получилось несколько подругому.

Передал процедуре с бинарником VarPtr строки,
а затем получил первый Dword по адресу [VarPtr]
Dim Str as String: Str="Hello World!"

'На самом деле это делается по другому, но я записал чтобы всем было понятно.
[EBP+8]=VarPtr(Str)

Теперь АСМ-овый код.

'Переслать в СЧЕ первый параметр. Я послал туда Varptr(Str)
mov ecx,[ebp+8]
'Переслать в АКК первое двойное слово по адресу Varptr
mov eax,[ecx]

Потом сравнил то что вернула функция (функция всегда возвращает в EAX) с StrPtr(Str) и они совпали.
Короче получилось STRPTR=DWORD PTR [VARPTR]

Я честно говоря первый раз услышал про 10 байт. Откуда такая информация. Сколько я ни выспрашивал на masmforum.com, но так и не получил членораздельной информации о способах адресации строк.

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 03.07.2004 (Сб) 1:29

BP писал(а):Хм, а у меня получилось несколько подругому.

Передал процедуре с бинарником VarPtr строки,
а затем получил первый Dword по адресу [VarPtr]
Dim Str as String: Str="Hello World!"

А я о чём написал выше? Цитирую (себя же):
Approximator писал(а):Очевидно, что функция VarPtr() от строки будет возвращать указатель на заголовок её структуры (на те самые 10 байт). Первое значение (Long) в структуре заголовка и есть StrPtr()

'На самом деле это делается по другому, но я записал чтобы всем было понятно.
[EBP+8]=VarPtr(Str)

Теперь АСМ-овый код.

'Переслать в СЧЕ первый параметр. Я послал туда Varptr(Str)
mov ecx,[ebp+8]
'Переслать в АКК первое двойное слово по адресу Varptr
mov eax,[ecx]

Потом сравнил то что вернула функция (функция всегда возвращает в EAX) с StrPtr(Str) и они совпали.
Короче получилось STRPTR=DWORD PTR [VARPTR]

Не уловил каким образом это относится к тому, что я спрашивал. А я спрашивал, возможно ли в среле VB переписать указатель на данные строки. При этом, важно (для меня), чтобы можно было указать на произвольный фрагмент другой строки. Именно об этой возможности я и спрашивал. То есть, чтобы появилась строка являющаяся подстрокой другой строки... данные, разумеется, должны быть общими. Ну и, разумеется, не забывайте про среду (VB + API).
BP писал(а): Я честно говоря первый раз услышал про 10 байт. Откуда такая информация.

MSDN. Справочная статья "Data Type Summary (VB)". Цитата оттуда:
MSDN Data Type Summary писал(а):String
(variable-length): 10 bytes + string length; 0 to approximately 2 billion...

BP писал(а):Сколько я ни выспрашивал на masmforum.com, но так и не получил членораздельной информации о способах адресации строк.
Может не там спросил?
С уважением, Approximator.

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 03.07.2004 (Сб) 6:24

По строкам (кому интересно) информация такая.
Значение StrPtr() можно (мне удалось) переписать только в двух случаях: первый, когда у нас имеется пустая (non fixed length) строка (у которой по VarPtr'у записан "длинный ноль"); второй, когда переписываешь StrPtr у fixed length строки. Результат, как и следовало ожидать в обоих случаях различный. В первом случае пустая строка будет ссылаться на те же данные, что и исходная строка (чей StrPtr мы копировали).
Во втором случае :) наоборот, данные в fixed length строке сохранятся, но... :) сохранятся они по StrPtr'у исходной строки. Точнее не всегда :) :) Дело в том, что StrPtr у fixed length строки "скачет", в связи с чем в исходной строке то будет инфа из fixed length строки, то нет :) действительно, такая хрень :) мало кому может пригодиться... :) Разве что для изврата кого-нить...

Главное, что мне не удалось - не удалось добиться, чтобы одна строка являлась бы подстрокой другой строки... похоже, что здесь только через SAFEARRAY... так что всё зависит от того, что скажет GSerg по поводу моего первого поста в теме.
С уважением, Approximator.

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 03.07.2004 (Сб) 7:01

:) Ну надо же так облажаться :) Ёпрст... :) А знаете где длина как non fixed, так и fixed length строк прописывается? Long перед StrPtr :) и есть длина строки в байтах (по паре байт на символ Юникода)... Так, что на самом деле можно сделать так, чтобы строка ссылалась на фрагмент данных другой строки... достаточно лишь, чтобы в четырёх байтах перед StrPtr'ом подстроки стояла её длина (ну, где-нить сохранить пару байт или даже четвёрку байт проще, чем копировать подстроку, если она длиннее)...

Так что 8 из 10 байт найдены... скорее всего смысл имеют ещё два байта в конце строки, но это пока только мои догадки...

Правда, всё это не снимает моего вопроса GSerg'у относительно SAFEARRAY
С уважением, Approximator.

BP
Бывалый
Бывалый
 
Сообщения: 234
Зарегистрирован: 17.02.2004 (Вт) 5:34
Откуда: Украина

Сообщение BP » 03.07.2004 (Сб) 7:24

Не уловил каким образом это относится к тому, что я спрашивал.
- И не надо.

Может не там спросил?
- Наверное

Вообще то StrPtr считается компилятором и прописывается в экзешник как непосредственное значение(imm32).
Лучше дизассемблируй VB-шный экзешник и глянь что он там делает. А то вокруг да около можно бесконечно ходить.

RayShade
Scarmarked
Scarmarked
Аватара пользователя
 
Сообщения: 5511
Зарегистрирован: 02.12.2002 (Пн) 17:11
Откуда: Russia, Saint-Petersburg

Сообщение RayShade » 03.07.2004 (Сб) 17:24

Автору топика - для подобных целей на будущее есть Личные Сообщения. А пока эта тема едет в Треп.
I don't understand. Sorry.

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 04.07.2004 (Вс) 1:18

Обсуждение статей ДОЛЖНО вестись исключительно в "личке"? Странное правило... ладно, в трёп, так в трёп...
С уважением, Approximator.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 07.07.2004 (Ср) 19:52

Вот! Я вернулся! Ура мне :)

Со строками разобрались правильно, молодцы :)
А по спецификации SAFEARRAY и не может быть UBound<Lbound :)
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 08.07.2004 (Чт) 1:14

GSerg писал(а):Вот! Я вернулся! Ура мне :)

Со строками разобрались правильно, молодцы :)
А по спецификации SAFEARRAY и не может быть UBound<Lbound :)

Действительно, ура тебе!
Со структурой SAFEARRAY разобрался. Проблема была в том, как я объявлял всякие Get/Put-Mem'ы... надо было ByVal, а у меня было ByRef... в этом случае, как раз и происходит UBound<LBound.
Ктати, я немного усовершенствовал твою идею с StrArrPtr, теперь можно вместо VarPtr(), ArrPtr(), StrArrPtr() можешь использовать на выбор одну из нижеследующих УНИВЕРСАЛЬНЫХ функций:
Код: Выделить всё
Public Function AnyPtr(ByRef vVar As Variant) As Long
CopyMem VarPtr(AnyPtr), VarPtr(vVar) + 1, 1
If AnyPtr = 64 Or AnyPtr = 96 Then CopyMem VarPtr(AnyPtr), VarPtr(vVar) + 8, 4
End Function

Код: Выделить всё
Public Function AnyPtrEx(ByRef vVar As Variant, Optional ByRef vTypeName As_String) As Long
Dim lVT As Long
FillMem VarPtr(lVT), 4, 0
CopyMem VarPtr(lVT), VarPtr(vVar) + 1, 1
If lVT = 64 Or lVT = 96 Then
  CopyMem VarPtr(lVT), VarPtr(vVar) + 8, 4
Else
  lVT = 0
End If
AnyPtrEx = lVT
vTypeName = TypeName(vVar)
End Function
С уважением, Approximator.

BP
Бывалый
Бывалый
 
Сообщения: 234
Зарегистрирован: 17.02.2004 (Вт) 5:34
Откуда: Украина

Сообщение BP » 08.07.2004 (Чт) 1:38

Ты их не неправильно объявил, а неправильно обозвал. Смотрите что я наклепал.
Жаль что на этом сайте нет такой удобной отправки примеров как на VbNet.ru
А названия у них очень простые.
Это POKE и PEEK :)
===

Private Declare Sub GetMem1 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Byte)
Private Declare Sub GetMem2 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Integer)
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Long)
Private Declare Sub GetMem8 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Currency)

Private Declare Sub PutMem1 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As Byte)
Private Declare Sub PutMem2 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As Integer)
Private Declare Sub PutMem4 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As Long)
Private Declare Sub PutMem8 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As Currency)

Sub POKE(ByVal Address As Variant, ByVal Value As Variant, Optional ByVal HowMuchBits As Byte = 32)
Select Case HowMuchBits
Case 8
PutMem1 Address, Value
Case 16
PutMem2 Address, Value
Case 32
PutMem4 Address, Value
Case 64
PutMem8 Address, Value
Case Else
MsgBox "Invalid value length" & vbCr & vbCr & "Must be one from: 8/16/32/64" & vbCr & vbCr & vbTab & "8 - Byte (unsigned)" & vbCr & vbTab & "16 - Word/Integer" & vbCr & vbTab & "32 - Dword/Long" & vbCr & vbTab & "64 - Qword/Currency"
End Select
End Sub

Function PEEK(ByVal Address As Long, Optional ByVal HowMuchBits As Byte = 32) As Variant
Dim Value As Variant
Select Case HowMuchBits
Case 8
GetMem1 Address, Value
Case 16
GetMem2 Address, Value
Case 32
GetMem4 Address, Value
Case 64
GetMem8 Address, Value
Case Else
MsgBox "Invalid value length" & vbCr & vbCr & "Must be one from: 8/16/32/64" & vbCr & vbCr & vbTab & "8 - Byte (unsigned)" & vbCr & vbTab & "16 - Word/Integer" & vbCr & vbTab & "32 - Dword/Long" & vbCr & vbTab & "64 - Qword/Currency"
Exit Function
End Select
PEEK = Value
End Function

Private Sub Form_Load()
Dim Var_Byte As Byte, Var_Int As Integer, Var_Lng As Long, Var_Curr As Currency

Var_Byte = 123: Var_Int = 1234: Var_Lng = 123456: Var_Curr = CDec(5234567890#)

Dim strMsg As String
strMsg = "Получение значений переменных по их адресу с помощью PEEK:" & vbCr
strMsg = strMsg & "BYTE: " & PEEK(VarPtr(Var_Byte), 8) & vbCr
strMsg = strMsg & "INTEGER: " & PEEK(VarPtr(Var_Int), 16) & vbCr
strMsg = strMsg & "LONG: " & PEEK(VarPtr(Var_Lng)) & vbCr
strMsg = strMsg & "CURRENCY: " & PEEK(VarPtr(Var_Curr), 64) & vbCr
MsgBox strMsg

POKE VarPtr(Var_Byte), 210, 8
POKE VarPtr(Var_Int), 4321, 16
POKE VarPtr(Var_Lng), 654321
POKE VarPtr(Var_Curr), CDec(9999999999#), 64

strMsg = "Значения переменных изменены с помощью POKE:" & vbCr
strMsg = strMsg & "BYTE: " & Var_Byte & vbCr
strMsg = strMsg & "INTEGER: " & Var_Int & vbCr
strMsg = strMsg & "LONG: " & Var_Lng & vbCr
strMsg = strMsg & "CURRENCY: " & Var_Curr & vbCr
MsgBox strMsg
End Sub

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 08.07.2004 (Чт) 2:06

BP писал(а):Ты их не неправильно объявил, а неправильно обозвал...

Типа, пошутил? :)
С уважением, Approximator.


Вернуться в Народный треп

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

Сейчас этот форум просматривают: Yandex-бот и гости: 73

    TopList