Russian English
   Basic.net
Среда, 23.09.2020, 03:03
Меню сайта
Категории раздела
Basic [44]
Помощь [0]
Облако тегов
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Форма входа
Главная » Статьи » Basic

Вызов функций по указателю в Visual Basic (часть 1)
Share |
Введение

Как известно, прямой работы с указателями в VB нет. Однако есть функции для почти прямой работы с ними – частично скрытые (VarPtr, ObjPtr, StrPtr) и скрытые посильнее (см. статью про GetMem и PutMem). А вот вызова функций по указателю нет вообще, в то время как это мощнейший инструмент, очень удобный и простой. И привнести его в VB – деяние приятное и полезное. Многое из описанного в этой статье является весьма и весьма спорным с точки зрения переносимости, может, непереносимым вообще. Но возгласы "эй, а это будет работать в следующей версии VB?" не имеют силы: VB 7 уже вышел. VB 7 – это совсем не VB 6, это другой язык. Так что мы знаем, что VB 6 останется именно таким, какой он есть сейчас. И поэтому мы можем использовать даже самые непереносимые и несовместимые методики. При написании этой статьи я придерживался определённой хронологии и описывал решения в том порядке, в котором к ним приходил. Копирайт: сама идея вызова через CallWindowProc с использованием некоей ассемблерной вставки принадлежит не мне. Но реализация именно в таких вот видах и именно с такими вставками – моя. Взгляд изнутри Прежде всего, необходимо разобраться, что такое вызов функции на самом низком уровне, с точки зрения процессора. Как известно, у процессора есть набор инструкций, заложенных в него проектировщиками, которые он и исполняет. "Вызов" (функции) – это тоже просто одна из команд процессора. Их на самом деле несколько, но мы не будет углубляться в изучение ассемблера (во-первых, это выходит за рамки статьи, а во-вторых, я сам в этом не очень силён . Запомним пока, что есть такая команда. Теперь стек... Что такое стек? Это часть памяти (обыкновенной, оперативной), которая резервируется при запуске программы (нужный размер этой стековой памяти записывается компилятором прямо в exe при создании оного). Особенность работы со стеком описывается буквосочетанием LIFO (Last In, First Out; последним пришёл, первым вышел). Образно стек можно представить как трубу с одним открытым концом (а другой запаян). Можно положить что-то внутрь (при этом оно окажется на самом верху), если есть место, конечно. Можно убрать что-то (только самое верхнее, снизу нельзя). Можно посмотреть, что уже наличествует (смотреть можно и сверху, и глубже – представьте, что вся труба стеклянная). Принцип стека используется в программах (во всех программах) по двум причинам. Во-первых, память резервируется сразу при запуске программы, а значит, не будет расходов на её выделение в процессе работы (это обеспечивает высокую скорость). Во-вторых, это самый простой и удобный способ сохранения предыдущего состояния, чтобы сделать что-то другое и потом вернуться. Когда что-то помещается в верхушку стека, всё остальное остаётся неизменным, и потому после удаления верхнего элемента всё как-то само собой возвращается в исходное состояние, совершенно без лишних движений. Думаю, теперь можно переходить к описанию того, как на самом деле происходит вызов функции. Вызов функции – это помещение параметров в стек и выполнение процессорной команды call. Вот и всё. Существует несколько соглашений вызова (они определяют, как именно помещаются параметры в стек). Мы будем рассматривать только одно соглашение – StdCall. Во-первых, именно его используют все функции Windows, во-вторых, именно его использует VB. В соответствии с этим соглашением, параметры помещаются в стек в обратном порядке (справа налево), а функция сама удаляет их из стека по завершении работы. Допустим, у нас есть функция
Код Visual Basic
1
Function Sum (ByVal p1 As Long, ByVal p2 As Long, ByVal p3 As Long) As Long
которая возвращает сумму своих аргументов. Когда мы вызываем её, выполняется такой код:
Код Visual Basic
1
2
3
push p3
push p2
push p1
после чего происходит вызов функции Sum.

Как вы догадались, команда процессора push означает "поместить в верхушку стека". Как нетрудно видеть, первый аргумент окажется в самой верхушке стека, ведь он помещён туда последним. Функция Sum всё это знает, и свои аргументы оттуда берёт (она пока не удаляет их! она пользуется тем, что труба стеклянная). Закончив работу, функция Sum должна вернуть управление туда, откуда её вызвали (иначе программа остановится… вернее, она рухнет, но это детали…). Но КУДА должна вернуть управление функция Sum?

Я написал "после чего происходит вызов функции Sum", но я не написал, как именно он происходит. Очень просто - выполняется единственная команда процессора Call. Эта команда передаёт управление функции Sum, но перед этим помещает в верхушку стека адрес, по которому должна вернуться эта самая Sum! В результате непосредственно перед передачей управления в функцию Sum стек приобретает вид:
Код
1
2
3
4
Адрес возврата
Параметр p1
Параметр p2
Параметр p3
[всё, что глубже, помещено не нами и нас не интересует...]

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



Категория: Basic | Добавил: Admin (14.06.2012)
Просмотров: 2252 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск
Наш опрос
Какую версию Basic вы предпочитаете?
Всего ответов: 2028

© Basic.ucoz.net, 2020