Об указателях в VB6

Хакер дает советы, раскрывает секреты и делится своими мыслями по поводу программирования.

Модератор: Хакер

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

Об указателях в VB6

Сообщение Хакер » 10.03.2008 (Пн) 22:24

  • Говорят, что в VB6 нет указателей вообще. Это утверждение не верно.
  • В VB6 нет способа объявить переменную-указатель, а раз так, нет и способа установить её (сделать так, чтобы она указывала куда-то). Однако указатели сами по себе в VB6 есть. Они там существуют в более безопасном воплощении указателей — в виде ссылок.
  • Я думаю уже многие догадались, что речь идёт о ByRef аргументах. ByRef аргумент — по своей сути указатель, и указывать он может на всё-что угодно: на локальную переменную (лежащую в стеке) внутри процедуры, из которой вызывается другая процедура (с аргументом), на какую-то глобальную переменную (лежащую в секции данных). ByRef аргумент может указывать даже на константу. В этом случае константа (её копия) помещается в стек непосредственно перед вызовом, и передаётся указатель на константу (её копию) в стеке.
      На заметку:
      Сделано это не просто так. Указатель передаётся именно на копию константы (которая создаётся прямо перед вызовом, и уничтожается сразу же после вызова), а не саму константу, по той причине, что процедура может менять данные, указатель на которые ей передали. Если бы VB просто передавал указатель на константу, а не на её временную копию, константа, переданная By Ref смогла бы измениться, что противоречит здравому смыслу (её название происходит от лат. constantum - постоянный, а тут вдруг на тебе - изменилась). В общем, в таком случае все константы стали бы пред-инициализированными глобальными переменными. Поэтому используется именно копия константы. Поэтому же, константы даже не помещаются в секцию данных, а вшиваются сразу в код (за исключением строковых констант, конечно, которые всё-равно помещаются в секцию данных).
  • Объявить в процедуре переменную-указатель мы не можем, но можем объявить аргумент-указатель. Это уже хорошо. Однако этот указатель будет указывать не туда, куда нам хочется, верно? Обладая замечательной функцией PutMem4 (или чуть менее замечательной — CopyMemory) мы имеем возможность «перевешать» такой самодельный указатель туда, куда-нам надо.
  • Использование PutMem4 требует от нас две вещи:
    1) Знать, куда писать.
    2) Знать, что писать.

    Куда писать, понятно, — поверх ByRef-аргумента. Что-писать, надеюсь, тоже понятно (адрес, куда должен указывать указатель).
  • Проблемы начинаются тогда, когда мы хотим определить адрес аргумента-указателя. Да чего там, — скажите вы, — VarPtr же есть. VarPtr, действительно, есть, но кроме него есть ещё и один нюанс.

    Дело в том, что VB по-особому кастует указатели к указателям. Скажем, если вы передаёте в функцию переменную lFoo:
    ... по значению (ByVal) — в стек кладётся значение переменноьй
    ... по ссылке (ByRef) — в стек кладётся адрес переменной lFoo.
    Однако, если lFoo — не локальная переменная, а локальный ByRef-аргумент, в стек попадёт не адрес этого аргумента, а адрес того, на что сам этот ByRef-аргумент ссылается, то есть значение (фактическое) этого ByRef-арумента.

    Не совсем понятно? Давайте рассмотрим пример:
    Код: Выделить всё
    Function Main()
        Dim zipa as long
        zipa = 123456789
        AAA zipa
    End Function

    Function AAA(ByRef MyArg As Long)
          MsgBox CStr(MyArg)
          BBB MyArg
    End Function

    Function BBB(ByRef MyBBBArg As Long)
          MsgBox CStr(MyBBBArg)
    End Function


    Здесь в функцию AAA передаётся не само значение переменной zipa, а ссылка (указатель) на неё. Однако, в AAA при вызове BBB в качестве аргумента MyBBBArg передаётся уже не указатель на AAA->MyArg, а указатель на Main->zipa.

    Опять же, если бы VB при использовании ByRef-аргументов везде использовал указатель, мы бы в BBB имели двойной указатель и увидели бы сообщение не "123456789", а какой-нибудь адрес (не просто какой-нибудь, а VarPtr(Main-->zipa) если быть точным)).
  • К чему я всё это? :) Да к тому, что получить реальный адрес ByRef-аргумента в стеке нам с помощью VarPtr никогда не удастся (из-за умного кастования). Максимум, что мы получим - адрес переменной (или копии константы), на которую ссылается ByRef-аргумент.
  • Тем не менее, получить адрес аргумента в стеке всё-же возможно, хотя и не так красиво.
    Если в аргументах перед нашим ByRef-аргументом определить другой аргумент Optional ByVal Stub As Long, то адрес нашего ByRef-аргумента можно получить как VarPtr(Stub) + 4. А адрес следующего адрес следующего — VarPtr(Stub) + 8.
      На заметку:
      Stub можем сделать не первым, а последним аргументом, и отнимать от его VarPtr числа, кратные 4. Надо только учитывать, что Double-переменные занимают 8 байт стека, а не 4.
  • Давайте уже попробуем что-нибудь написать:
    Код: Выделить всё
    Sub MyCoolProc(Optional ByVal Stub As Long, Optional ByRef MyPointer As SomeType)
       ' Определить реальный адрес MyPointer с помощью VarPtr - нельзя.
       ' Но его можно вычислить как VarPtr(Stub) + 4.
    End Sub


    Теперь я думаю, понятно, что записывая по адресу VarPtr(Stub) + 4 нужные значения, мы можем управлять указателем MyPointer. Причём тип MyPointer-а может быть любым (почти), например, это может быть какой-то пользовательский тип.
  • Хотим пример? Пожалуйста. Первое, что мне пришло в голову — быстрая замена символов в строке:
    Код: Выделить всё
    Public Sub Repl(ByVal sString As String, _
                    ByVal Search As Integer, _
                    ByVal Replacement As Integer, _
                    Optional ByVal Stub As Long, _
                    Optional ByRef Replacer As Integer)
        ' По поводу аргументов:
        '  sString     - строка, в которой производится замена
        '  Search      - что ищем
        '  Replacement - чем заменяем
        '     Stub - заглушка, Replacer - наш самодельный указатель
       
       
        Dim CurPos As Long
        Dim EndPos As Long
        Dim ptrAddress As Long ' Здесь будем хранить VarPtr(Stub)+4
                               ' чтобы каждый раз не считать
       
        CurPos = StrPtr(sString)
        EndPos = CurPos + LenB(sString)
       
        ptrAddress = VarPtr(Stub) + 4
       
        Do
            PutMem4 ptrAddress, CurPos
            ' Момент истины:
            If Replacer = Search Then Replacer = Replacement ' А вы думали? ;)
           
            CurPos = CurPos + 2     ' Переходим к обработке след. символа
        Loop Until CurPos = EndPos
        MsgBox sString
    End Sub


    Выглядит массивно из-за комментов. Без комментов эта же функция выглядит так:
    Код: Выделить всё
    Public Sub Repl(ByVal sString As String, ByVal Search As Integer, ByVal Replacement As Integer, _
                    Optional ByVal Stub As Long, Optional ByRef Replacer As Integer)
        Dim CurPos As Long
        Dim EndPos As Long
        Dim ptrAddress As Long
       
        CurPos = StrPtr(sString)
        EndPos = CurPos + LenB(sString)
        ptrAddress = VarPtr(Stub) + 4
       
        Do
            PutMem4 ptrAddress, CurPos
            If Replacer = Search Then Replacer = Replacement ' А вы думали? ;)
            CurPos = CurPos + 2
        Loop Until CurPos = EndPos
        MsgBox sString
    End Sub


    Пробуем:
    Код: Выделить всё
    Repl "мама мыла раму", AscW("м"), AscW("п")

  • Интересно сравнить производительность нашей функции с Replace (стандартной и самодельной):

    Замена в строке "мама мыла раму мило" всех "м" на "п", 200000 циклов замены, время усреднённое (10 экспериментов):

    Стандартный Replace.................................... 1.443839 сек.
    Наш Repl (с указателями)............................ 0.206268 сек. ( :wink: )
    Самописный Replace на основе Mid$ .......... 2.001647 сек.

    (Скопилировано в Native-код. Учитывайте, что Declare Function PutMem4 сжирает 30% времени. Если объявить её (PutMem4) в TLB — будет ещё быстрее).

    Итак, мы получили выигрыш в 7-10 раз. Стоило ли ради этого что-то придумывать? Безусловно, да. Наш код с указателями обогнал почти в 10 раз код без указателей, и в 7 раз - функцию, написанную на C++.

    Не будем, конечно, докапываться до сишной функции, она гораздо более универсальна чем наша, но всё же.
  • Другой пример, — оперирование произвольными байтами переменной:
    Код: Выделить всё
    Public Sub SomeProc(Optional ByVal stub As Long, Optional ByRef pByte As Byte)
        Dim Foo As Long
        Foo = &HFF00FF00
       
        PutMem4 VarPtr(stub) + 4, VarPtr(Foo)  ' Установили указатель
        pByte = &H77
       
        MsgBox Hex$(Foo) ' Будет FF00FF77
       
        ' Другой пример - получить 5-ый байт числа Sqrt(3)
        ' побитовые операции здесь не помогут :)
       
        Dim n As Double
        n = Sqr(3)
       
       
       
        PutMem4 VarPtr(stub) + 4, VarPtr(n) + 4 ' Установили указатель
        MsgBox "5-ый байт числа Sqrt(3) равен: " + CStr(pByte)
    End Sub

  • Теперь, если у вас есть адрес какой-то структуры в памятя, не делайте CopyMemory этой структуры в User-Defined-тип. CopyMemory — это очень долго (особенно если структура большая). Просто установите UDT-шный указатель на эту структуру и работайте с ней через поля своего пользовательского типа.
  • Самые любопытные наверняка хотят спросить: «А вот если нам нужно много указателей (15 штук, скажем), нам что, объявлять у процедуры 15 Optional ByRef аргументов? А не будет ли это влиять на время вызова? А не будет ли из-за этого использоваться лишняя память?»

    Начну с лишней памяти. Если бы в VB были указатели — вы бы сделали их локальными переменными - и они бы заняли 15 × 4 байт в стеке. Когда вы передаёте 15 аргументов, в стек помещается 15 dword'ов, так что используется тоже 15×4 байт стека. Так что здесь вы ничего не проигрываете. А вот небольшая задержка к сожалению будет. Если бы мы имели дело с локальными переменным, VB просто бы сделал sub esp, 60. А так, вместо одной инструкции, мы получим 15 пушей. Впрочем, задержка эта минимальна.
  • Итак, мы разобрались, что с применением не слишком сложной магии в VB появляется поддержка переменных-указателей. Собственно, на этом потенциал нераскрытых возможностей VB не заканчивается. Проделав ту же фишку с Declare Function мы получим ни много, ни мало — вызов функций по указателю средствами языка.

    Можно будет взять адрес у GetProcAddress, присвоить его своему указателю и вызвать (используя родной синтаксис языка) нужную функцию.

    Правда здесь всё на порядок сложнее, и я пока работаю над реализацией этой задумки.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

BV
Thinker
Thinker
Аватара пользователя
 
Сообщения: 3987
Зарегистрирован: 12.09.2004 (Вс) 0:55
Откуда: Молдавия, г. Кишинёв

Сообщение BV » 13.04.2008 (Вс) 22:22

и в 7 раз - функцию, написанную на C++


Это ты про Replace? Гы-гы :) Сравнил...
Вот напиши замену строки, а не символа, и тогда сравнивай.
const char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 14.04.2008 (Пн) 10:40

По поводу нового Replace - его можно ещё ускорить, введя дополнительную переменную-указатель(pReplacer), которая будет ссылаться на основную (на Replacer). В итоге вместо вызова PutMem4 в цикле можно сделать pReplacer+2. Тогда PutMem4 будет вызван всего 2 раза
Изображение

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

Сообщение alibek » 23.05.2008 (Пт) 15:20

Зачем тебе указатель на метод класса? Что ты с ним будешь делать?
Lasciate ogni speranza, voi ch'entrate.

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

Сообщение Хакер » 23.05.2008 (Пт) 15:35

я понимаю что перемещением классов в памяти заведует менеджер памяти , даже в VС++ не так то просто это сделать, как я понял там указатель можно только на статик метод получить

Что такое "перемещение классов"?

а как бы мне получить указатель на метод класса?

У класса нет методов. Класс поддерживает один или более интерфейс и имеет их реализацию.

Интерфейсы имеют методы. Зная номер нужного метода, указатель на его реализацию можно получить в vtable этого интерфейса.

Пример доступа к vtable объекта и получения адреса метода (и последующего вызова этого метода по указателю) можно найти здесь.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 23.05.2008 (Пт) 17:40

метод будет чистый
Метод будет грязный - возвращаемое значение будет кодом ошибки, фактически возвращаемое значение будет отдельным out-параметром, и плюс будет передаваться указатель на this.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Сообщение Zenitchik » 24.06.2008 (Вт) 22:26

Попробовал писать КА на основе этой технологии.
С чтением символов из строки - все здорово и удобно.
А вот как быть с записью?
Лексический анализатор должен для некоторых лексем (имен, например) вычислять их код. Я не знаю заранее, какой длины у меня лексема.
По идее, я ничего особо не теряю, если задам массимальную длину лексемы равной длине исходного кода?
Знание английского языка - затрудняет понимание кода

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

Сообщение Хакер » 24.06.2008 (Вт) 22:38

Я не понял, если ч чтением символов из строки всё здорово и удобно, то почему всё не здорово с записью символов в строку?

С другой стороны, мне кажется, что речь идёт не о записи символов в строку, а чём то другом?

Лексический анализатор должен для некоторых лексем (имен, например) вычислять их код.

Что понимать под кодом лексемы?

Я не знаю заранее, какой длины у меня лексема.
По идее, я ничего особо не теряю, если задам массимальную длину лексемы равной длине исходного кода?

Странно, что ты не понимаешь, какой-длины у тебя лексема.

У тебя КА, делающий токенизацию, заполняет ведь-какую то структуру (массив) информацией о токенах (лексемах)? Так вот пусть он туда же записывает, где начинается токен и где заканчивается.

Так можно и 1) длину найти легко, и 2) не нужно хранить в записе о токене сам токен, его можно (как только потребуется) получить из строки.

З.Ы. Компилятор пишешь? :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Сообщение Zenitchik » 24.06.2008 (Вт) 22:54

КА должен выделять лексемы и "скармливать" их поштучно в порядке следования синтаксическому анализатору. Тип лексемы определяется неявно (он совпадает с состоянием КА перед обработкой следующего за концом лексемы символа), а ее код - например, если, например, мы имеем дело с именем, нужно получить в виде строки.
Код: Выделить всё
Так вот пусть он туда же записывает, где начинается токен и где заканчивается.

Если я так поступлю, получится, что синтаксический анализатор должен будет повторно ее получать с помощью Mid или чего-то подобного. В случае ini-подобного формата (где всего четыре лексемы и у трех из них есть строковый код произвольной длины) - я вряд ли получу выигрыш производительности.

P.S. Упражняюсь. Автоматные языки - прекрасный материал для выработки навыков.
Знание английского языка - затрудняет понимание кода

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

Сообщение Хакер » 24.06.2008 (Вт) 23:09

Если я так поступлю, получится, что синтаксический анализатор должен будет повторно ее получать с помощью Mid или чего-то подобного.

Зачем? Пусть он работает с ней, имя лишь указатели на начало и конец, не выделяя их исходной строки новую подстроку.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Сообщение Zenitchik » 24.06.2008 (Вт) 23:38

Он не будет с ней работать, он работает с лексемой в целом, а ее код нужен "для информации" - он используется в его семантических процедурах.
Например, в ini-файле текст лексемы имя_секции нужно только для того, чтобы занести его в список секций... Хм... Подстроки же можно вообще не хранить... Нигде...
Гениально!
Знание английского языка - затрудняет понимание кода

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

Сообщение Хакер » 24.06.2008 (Вт) 23:40

Он не будет с ней работать, он работает с лексемой в целом, а ее код нужен "для информации" - он используется в его семантических процедурах.
Например, в ini-файле текст лексемы имя_секции нужно только для того, чтобы занести его в список секций...

Эта часть означает "Хакер, ты не прав"?

Хм... Подстроки же можно вообще не хранить... Нигде...
Гениально!

А эта -- "Хакер, ты прав!"?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Сообщение Zenitchik » 24.06.2008 (Вт) 23:50

Переиначив автоподпись тов. Antonariy, могу сказать: внатуре, пока словами не изложишь - хрен допедришь.

UPD:
Так и поступлю. Подстроки будут вычисляться заново "в отдаленном светлом будущем", когда они будут использоваться для отображения в интерфейсе или для записи в файл. А до тех пор - два Long'а вместо одного String'а неопределенной длинны - поди кисло!
Последний раз редактировалось Zenitchik 24.06.2008 (Вт) 23:55, всего редактировалось 2 раз(а).
Знание английского языка - затрудняет понимание кода

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

Сообщение Хакер » 24.06.2008 (Вт) 23:51

Так прав или не прав был Хакер? :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Сообщение Zenitchik » 24.06.2008 (Вт) 23:57

Еще одна вещь: если мне понадобится на этапе синтаксического разбора обнаружить коллизию имен, что я должен сравнивать? По длине и побайтно, или можно как-то умнее?
Знание английского языка - затрудняет понимание кода

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

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

Сначала проверил длину, если совпадает -- поDWORD-но и остаток по-байтно.

Можно сделать умнее, сравнивая не последовательно, а отсекая половины.

Т.е. проверил первый блок, последний блок, серединный блок, середину между первым и серединным, середину между серединным и последним, и так пока не проверишь всю строку.

По идее, скорость обнаружения расхождений (если они есть, должна быть выше, но из-за того, что для этого нужна сложная математика (и скорее всего стек) чтобы расчитывать, какой блок проверять следующим, разница может нивелироваться.


З.Ы. Только что мне пришла идея, как применять вышеописанную практику, чтобы сделать указатели на функции и соотвественно - вызов функций по указателю.

Тогда КА будет моделировать вообще суперски: вместо SelectCase-ов: функторы в роли рёбер графа, и функции-роутера -- в роли узлов.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Об указателях в VB6

Сообщение Zenitchik » 05.11.2008 (Ср) 23:32

З.Ы. Только что мне пришла идея, как применять вышеописанную практику, чтобы сделать указатели на функции и соотвественно - вызов функций по указателю.

Если не секрет, поделитесь идеей. Эта тема неожиданно стала для меня актуальной.
Знание английского языка - затрудняет понимание кода

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

Re: Об указателях в VB6

Сообщение Хакер » 06.11.2008 (Чт) 14:27

Идея заключается в следующем:

Вызов функций, объявленных через Declare Sub/Function — ничто иное, как тоже вызов функции по указателю. Меняя адрес, на который указывает подобный указатель, получаем возможость вызывать всё что угодно по указателю.

У меня на примете два способа, как можно это реализовать. Один из них продуман только для работы в Run-тайме, другой работает и в Run- и в Design- тайме, но чуток некрасив.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Debugger
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1667
Зарегистрирован: 17.06.2006 (Сб) 15:11

Re: Об указателях в VB6

Сообщение Debugger » 12.11.2008 (Ср) 22:03

Давай оба - сравним, попробуем. Покритикуем.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Об указателях в VB6

Сообщение Zenitchik » 23.01.2009 (Пт) 20:53

Оффтоп:
А как бы, оставаясь в рамках такого подхода, реализовать сжатие пробелов? Т.е. каждый раз ряд идущих подряд пробелов заменять одним пробелом?
Знание английского языка - затрудняет понимание кода

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

Re: Об указателях в VB6

Сообщение Хакер » 23.01.2009 (Пт) 20:56

А в чём проблема?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Об указателях в VB6

Сообщение Zenitchik » 24.01.2009 (Сб) 11:43

Я, конечно, понимаю, что на проходе строки нужно получить массив пар старт-финиш, а после прохода - вычислить суммарную длину новой строки, сгенерить ее спейсом и скопимеморить в нее вышеупомянутые подстроки.
Просто возникло сомнение - нельзя ли умнее.
Знание английского языка - затрудняет понимание кода

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

Re: Об указателях в VB6

Сообщение Хакер » 24.01.2009 (Сб) 11:53

Да, это первое, что приходит в голову, но можно умнее.

У тебя операция получается в две стадии: сначала первый проход — поиск и составление массива сдвигаемых частей, потом второй проход (по составленному массиву) — сдвигание (компактификация) строки.

Но не обязательно составлять массив (к тому же, это будет дин. массив, что нехорошо скажется на скорости), а лишь потом компактифицировать строку. Достаточно держать "в уме" всего лишь один описатель {начало_блока_текста; конец_блока_текста}, и при нахождении следующего, делать сдвиг предыдущего, благополучно забывать о нём, и запоминать следующий блок. Сдвигать строку надо в саму же себя, а не в доп. буфере. А после всей процедуре сделать truncate этой строки.
Последний раз редактировалось Хакер 24.01.2009 (Сб) 13:47, всего редактировалось 3 раз(а).
Причина: исправлено "самоё-же себе" на "саму же себя"
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Об указателях в VB6

Сообщение Zenitchik » 24.01.2009 (Сб) 13:43

truncate этой строки

Это хвост чтоли откусить?
Знание английского языка - затрудняет понимание кода

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

Re: Об указателях в VB6

Сообщение Хакер » 24.01.2009 (Сб) 13:44

Ага.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Ramzes
Скромный человек
Скромный человек
Аватара пользователя
 
Сообщения: 5004
Зарегистрирован: 12.04.2003 (Сб) 11:59
Откуда: Из гробницы :)

Re: Об указателях в VB6

Сообщение Ramzes » 24.01.2009 (Сб) 14:07

а разбить строку на массив строк (разделитель пробел), потом собрать обратно не пусты строки (len(trim(str)) > 0) :roll:

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

Re: Об указателях в VB6

Сообщение Хакер » 24.01.2009 (Сб) 14:17

Сразу виден дотнетчик :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Ramzes
Скромный человек
Скромный человек
Аватара пользователя
 
Сообщения: 5004
Зарегистрирован: 12.04.2003 (Сб) 11:59
Откуда: Из гробницы :)

Re: Об указателях в VB6

Сообщение Ramzes » 24.01.2009 (Сб) 15:56

8)

uni
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 105
Зарегистрирован: 05.05.2006 (Пт) 15:24
Откуда: Екатеринбург

Re: Об указателях в VB6

Сообщение uni » 02.01.2011 (Вс) 23:09

Так всё-таки это работает или нет?
Теперь я думаю, понятно, что записывая по адресу VarPtr(Stub) + 4 нужные значения, мы можем управлять указателем MyPointer. Причём тип MyPointer-а может быть любым (почти), например, это может быть какой-то пользовательский тип.

и далее по тексту:
Теперь, если у вас есть адрес какой-то структуры в памятя, не делайте CopyMemory этой структуры в User-Defined-тип. CopyMemory — это очень долго (особенно если структура большая). Просто установите UDT-шный указатель на эту структуру и работайте с ней через поля своего пользовательского типа.


Может я что-то не так понял? Нужно написать что-то вроде:
Код: Выделить всё
Optional ByRef Record As TYPE_USER_RECORD

в описании функции? Но ведь Optional для пользовательских типов в VB6 запрещены компилятором. Хотел использовать этот метод в своей программке, да вышел облом. Пользуюсь копированием содержимого в памяти в локальный экземпляр моего типа. Случайно нашёл статью и хотел расширить познания... а оно вот оно как вышло.
Россия навсегда!
Сетрификаты

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

Re: Об указателях в VB6

Сообщение Хакер » 03.01.2011 (Пн) 6:41

uni писал(а):Так всё-таки это работает или нет?

Работает, причём более чем хорошо. Я недавно опять использовал, думал, будут проблемы в IDE. Но на моё удивление, в IDE оно сработало превосходно: в Watch WIndow и Locals Window при изменении кодом значения самодельного указателя, автоматически менялось и значение переменной, на которую он указывает.

Откуда IDE узнаёт об изменении внутреннего значения ByRef-переменной и почему вообще написан код, предусматривающий такое изменение (ведь на практике она никогда не изменяется!) — ума не приложу. Видимо, разработчики IDE, как и я, ещё на этапе разработки предусмотрели все возможные варианты, за что им респект.

uni писал(а):Но ведь Optional для пользовательских типов в VB6 запрещены компилятором. Хотел использовать этот метод в своей программке, да вышел облом.

Если нужен указатель на структуру, а Optional использовать нельзя, значит просто не нужно ставить Optional. Optional делается для красоты.

Поэтому, если нужно много указателей, что влечёт появление большого числа псевдо-аргументов, а пользователю функции видеть эти псевдо-аргументы нежелательно, делаем внешнюю функцию без лишних аргументов, которая вызывает такую же функцию (внутреннюю, приватную), но со всеми дополнительными аргументами.

Вот пример из этого проекта:

Код: Выделить всё
Public Sub FindAllExternalReferences( _
                            ByVal pCode As Long, _
                            ByVal lSize As Long, _
                            EntList() As INJECTION_EXTERNAL_ENTITY, _
                            RefList() As INJECTION_EXTERNAL_REFERENCE, _
                            ByVal hModuleContainer As Long, _
                            ByVal pHintCodeSectionStart As Long, _
                            ByVal nHintCodeSectionSize As Long)

    ' /* ТЫСЯЧЯ СТРОК ВЫРЕЗАНА */

    InternalScanCodeForReferences pCode, _
                                  lSize, _
                                  RawRefs, _
                                  TotalRefs, _
                                  IATs, _
                                  TotalTables, _
                                  JmpThunks, _
                                  TotalThunks, _
                                  IATCellAddressLowest, _
                                  IATCellAddressHighest, _
                                  IATCellAddressMask, _
                                  IATCellAddressCommonBits, _
                                  ThunkAddressLowest, _
                                  ThunkAddressHighest, _
                                  ThunkAddressMask, _
                                  ThunkAddressCommonBits, _
                                  pImageBase, _
                                  DefaultImageBase, _
                                  0, 0, 0

    ' /* МНОГО КОДА ВЫРЕЗАНО */
End Sub

Private Sub InternalScanCodeForReferences(ByVal pCode As Long, _
                                          ByVal lSize As Long, _
                                          ByRef RawRefs() As INJ_REFERENCE_RAW, _
                                          ByRef TotalRefs As Long, _
                                          ByRef IATs() As INJ_IAT_INDEX_ENTRY, _
                                          ByVal TotalTables As Long, _
                                          ByRef JmpThunks() As INJ_JMP_THUNK, _
                                          ByVal TotalThunks As Long, _
                                          ByVal IatAddressLowest As Long, _
                                          ByVal IatAddressHighest As Long, _
                                          ByVal IatAddressCommonMask As Long, _
                                          ByVal IatAddressCommonBits As Long, _
                                          ByVal ThunkAddressLowest As Long, _
                                          ByVal ThunkAddressHighest As Long, _
                                          ByVal ThunkAddressCommonMask As Long, _
                                          ByVal ThunkAddressCommonBits As Long, _
                                          ByVal ActualImageBase As Long, _
                                          ByVal AssumedImageBase As Long, _
                                          ByVal lb_pointers As Long, _
                                          ByRef pCursor As Long, _
                                          ByRef Cursor As Byte)
    ' /* МНОГО КОДА ВЫРЕЗАНО */
   
    '
    ' Превращаем pCursor в указатель на Cursor. Методика описана здесь:
    ' http://bbs.vbstreets.ru/viewtopic.php?t=36100
    '
   
    PutMem4 ByVal VarPtr(lb_pointers) + 4, _
            ByVal VarPtr(lb_pointers) + 8


    ' /* ВЫРЕЗАНО */
End Sub
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

След.

Вернуться в МануAll

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

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

    TopList  
cron