1. Указатель функции
AddressOf получает указатель функции внутри VB. Мы можем передать этот указатель функции API, которому необходимо выполнить обратный вызов этой функции. Его функция — разрешить внешним программам вызывать функции внутри VB.
Однако применение указателей функций в VB не так обширно, как в C, поскольку документ VB только описывает, как передавать указатели функций в API для реализации обратных вызовов, и не указывает на многие магические функции указателей функций, поскольку VB не поощряется. При использовании указателей указатели на функции не являются исключением.
Во-первых, давайте классифицируем, как используются указатели на функции.
1. Обратный звонок. Это самая основная и важная функция. Например, ядром технологии создания подклассов, представленной в документации VB, являются два API: SetWindowLong и CallWindowPROc.
Мы можем использовать API SetWindowLong, чтобы заменить исходный указатель оконной функции на наш собственный указатель функции и сохранить исходный указатель оконной функции. Таким образом, оконные сообщения могут отправляться в наши собственные функции, и мы можем использовать CallWindowProc для вызова ранее сохраненного указателя окна в любое время для вызова исходной оконной функции. Таким образом, мы можем обрабатывать перехваченные сообщения, не нарушая функциональности исходного окна.
Мы должны быть знакомы со спецификой обработки, и документ VB также очень четко объясняет это. Что здесь требует внимания, так это API CallWindowProc, его замечательное применение мы увидим позже.
Здесь мы вызываем обратные вызовы, чтобы разрешить «внешние вызовы указателей на внутренние функции».
2. Для внутреннего использования программы. Например, в C мы можем передать указатель на функцию C в качестве параметра функции C, которой требуется указатель на функцию, например функции библиотеки C qsort, которая будет обсуждаться позже. Ее объявление выглядит следующим образом:
Для сравнения размеров двух переменных требуется указатель на функцию типа COMPARE, чтобы функция сортировки могла вызывать этот указатель на функцию для сравнения разных типов переменных, поэтому qsort может сортировать массивы переменных разных типов.
Назовем это приложение «вызов указателей внутренних функций изнутри».
3. Вызов внешних функций
Вы можете спросить, разве использование API не просто вызывает внешние функции? Да, но иногда нам все же необходимо напрямую получить указатель внешней функции. Например, динамическая загрузка DLL через LoadLibrary, а затем получение нужного нам указателя на вход в функцию через GetProcAddress, а затем вызов внешних функций через этот указатель на функцию. Такая технология динамической загрузки DLL позволяет нам более гибко вызывать внешние функции.
Мы называем этот метод «вызов указателя внешней функции изнутри»
4. Само собой, мы также можем контролировать «вызов указателей на внешние функции извне». Нет, например, мы можем загрузить несколько DLL и передать указатель на функцию из одной DLL функции в другой DLL.
Вышеупомянутые «внутренние» и «внешние» понятия являются относительными (на самом деле, эта классификация все еще находится в разработке). Пожалуйста, помните мою приведенную выше классификацию, потому что в будущих статьях также будет использоваться эта классификация. проанализировать проблему.
Использование указателей на функции — это не что иное, как описанные выше четыре способа. Но в реальном использовании он гибок и изменчив. Например, наследование и полиморфизм в C, а также интерфейсы в COM — это умные применения таблицы указателей функций, называемой vTable. Использование указателей на функции может сделать обработку программы более эффективной и гибкой.
За исключением первого метода, в документе VB не представлены другие методы, а также четко указано, что указатель функции «Basic to Basic» не поддерживается (то есть второй метод, упомянутый выше. Фактически, через определенный HACK). все вышеперечисленные четыре метода могут быть достигнуты. Сегодня давайте посмотрим, как реализовать второй метод. Поскольку его относительно просто реализовать, давайте начнем с простого. Что касается того, как вызывать указатели внешних функций в VB и как реализовать умное применение различных указателей функций путем обработки таблицы переходов указателей функций интерфейса vTable в VB, поскольку это будет связано с внутренними принципами COM, я подробно расскажу в другой статье. .
На самом деле документация VB не является ошибочной. VB не поддерживает указатель на функцию «Basic to Basic», но мы можем сделать это обходным путем, то есть сначала «Basic to API», а затем использовать первый метод. «внешний вызов внутреннего указателя функции», чтобы перейти от «API к BASIC», таким образом достигая цели второго пути от «Basic к Basic». Мы можем назвать эту технологию «принудительным обратным вызовом», которая доступна только в VB. Такая странная. технология.
Это немного запутанно, но подумайте внимательно о CallWindowProc в технологии создания подкласса окна. Мы можем использовать CallWindowProc, чтобы заставить внешнюю операционную систему вызвать наш исходный сохраненный указатель на функцию окна. Аналогично, мы также можем использовать его для принудительного вызова нашей внутренней функции. указатель функции.
Ха-ха, я уже говорил, что нам следует меньше говорить о принципах и больше о движениях. Теперь давайте начнем изучать движения!
Учтите, что мы реализуем qsort в VB, который поддерживает сравнение нескольких ключевых слов, как и C. Полный исходный код можно найти во вспомогательном коде этой статьи. Здесь приведен только код, связанный с применением указателя функции.
Давайте еще раз взглянем на наш последний оператор qsort.
ArrayPtr выше — это указатель на первый элемент массива, который необходимо отсортировать, nCount — количество элементов в массиве, nElemSize — размер каждого элемента, а pfnCompare — указатель нашей функции сравнения. Это объявление очень похоже на qsort в функции библиотеки C.
Как и в языке C, мы можем передать указатель на функцию Basic функции qsort.
Как его использовать:
Умные друзья, вы уже увидели здесь загадку? Можете ли вы в качестве проверки дать мне возможность использовать указатели на функции в qsort? Например, теперь нам нужно сравнить размер i-го элемента и j-го элемента массива, вызвав указатель на функцию.
Да, конечно, вы должны использовать объявленный ранее API сравнения (на самом деле CallWindowProc) для выполнения принудительных обратных вызовов.
Конкретная реализация выглядит следующим образом:
Ходы представлены, понимаете? Позвольте мне кратко объяснить значение параметра Compare выше. Он очень умно использует API CallWindowProc. Для этого API требуется пять параметров. Первый параметр — это обычный указатель на функцию. Этот API может немедленно вызвать указатель функции и передать последние четыре параметра типа Long этого API функции, на которую указывает указатель функции. Вот почему наша функция сравнения должна иметь четыре параметра, поскольку API CallWindowProc требует, чтобы передаваемый ей указатель функции соответствовал прототипу функции WndProc. Прототип WndProc выглядит следующим образом:
Вышеупомянутые LRESULT, HWND, UINT, WPARAM и LPARAM могут соответствовать типу Long в VB. Это действительно здорово, потому что тип Long можно использовать как указатель!
Давайте еще раз посмотрим на рабочий процесс. Когда мы вызываем qsort, используя AddressOfCompareSalaryName в качестве параметра указателя функции, формальному параметру pfnCompare функции qsort присваивается указатель функции фактического параметра CompareSalaryName. В настоящее время вызов Compare для принудительного обратного вызова pfnCompare эквивалентен вызову следующего оператора VB:
Не приведет ли это к ошибке несоответствия типа параметра? Разве первые два параметра CompareSalaryName не относятся к типу TEmployee? Действительно, такой вызов невозможен в VB, поскольку проверка типа VB не разрешает такой вызов. Однако этот вызов на самом деле является обратным вызовом, выполняемым API, и VB не может проверить, является ли тип параметра функции обратного вызова API обычным числовым типом Long или указателем на структуру, поэтому можно также сказать, что мы имеем обошел контроль VB над параметрами функции. Для проверки типа мы можем объявить этот параметр Long как указатель любого типа. Что бы мы ни объявили, VB подумает, что это так. Поэтому мы должны использовать этот метод осторожно. Например, параметр «ArrayPtr (i-1)*nElemSize», который в конечном итоге будет передан в функцию CompareSalaryName, указанную выше, является просто адресом. VB не будет проверять этот адрес. Это будет всегда. адрес рассматривается как указатель типа TEmployee, если он случайно используется как «ArrayPtr. i*nElemSize", тогда, когда i станет последним элементом, мы вызовем ошибку доступа к памяти, поэтому нам нужно обратить внимание на проблемы с границами, как и при работе с указателями в C.
Умное применение указателей на функции уже можно увидеть здесь, но представленный здесь метод по-прежнему имеет большие ограничения. Наша функция должна иметь четыре параметра. Более чистый подход — написать DLL в VC или Delphi и создать более совместимый API для реализации функций. аналогично CallWindowProc. Я проследил внутреннюю реализацию CallWindowProc. Он выполняет большую работу, связанную с оконными сообщениями, что в нашем приложении является избыточным. Фактически, чтобы реализовать API принудительного обратного вызова, вам нужно всего лишь поместить несколько последних параметров в стек, а затем вызвать первый параметр, что представляет собой всего лишь несколько инструкций ассемблера.
Именно из-за ограничений CallWindowProc мы не можем использовать его для вызова указателей внешних функций для реализации третьего метода вызова указателя функции, упомянутого выше. Чтобы реализовать третий метод, мастер Мэтт Керланд предложил кошмарный метод HACK. Нам нужно создать интерфейс IUnknown из воздуха в VB и добавить новую запись после трех исходных записей vTable интерфейса IUnknown. новая запись Вставьте в нее машинный код. Этот машинный код должен иметь дело с этим указателем, прежде чем окончательно вызвать указанный нами указатель функции. Этот указатель функции не является проблемой, является ли он внутренним или внешним. Я вернусь к этому методу, когда мы углубимся во внутреннее устройство COM.
Кроме того, алгоритмы сортировки — это вопрос мнения. Изначально я хотел представить в этой статье наиболее универсальный алгоритм с наилучшей производительностью. Хотя эта идея хороша, не существует «лучшего» алгоритма для каждой ситуации. Метод быстрой сортировки, реализованный с использованием различных технологий указателей, представленный в этой статье, должен быть намного быстрее и занимать гораздо меньше памяти, чем использование объектной технологии для реализации той же функции. Но даже этот алгоритм быстрой сортировки, который я оптимизировал, все равно не так хорош, как ShellSort, поскольку ShellSort прост в реализации. Теоретически, исходя из алгоритма, qsort должен иметь более высокую среднюю производительность, чем ShellSort, но это не обязательно так в VB (вы можете увидеть вспомогательный код этой статьи, который также предоставляет вспомогательный код столбца VBPJ, ShellSort, который очень хорошо, а идея этой статьи взята из This ShellSort).
Однако следует отметить, что и Quick Sort, и ShellSort здесь можно значительно улучшить, поскольку их реализация требует широкого использования CopyMemroy для копирования данных (это один из недостатков использования указателей в VB). На самом деле у нас есть лучший способ — взломать структуру массива VB, которая в автоматизации COM представляет собой SafeArray. Мы можем сразу поместить указатели каждого элемента массива в SafeArray в длинный массив. Без CopyMemroy нам понадобится только это. для обмена элементами в массиве Long для достижения цели обмена указателями элементов массива SafeArray в реальном времени. Данные не перемещаются, перемещаются только указатели. Вы можете себе представить, насколько это быстрее.
->