Программирование на VB6 (FAQ)

Источник: FAQ выпуск 2
Гайдар Магдануров

 

 FAQ (июнь-июль 2003)

 

Данный материал предоставлен журналом CODE

 

Программирование на VB6

 

Q. Могу ли я в своей программе использовать процедуры написанные на других языках программирования не включая их в DLL или ActiveX контрол?

 

G.M. Довольно странный вопрос и я к сожалению, не смог получить уточнения. Надеюсь, что Вы, прочитав эти строки, напишете мне, и я смогу ответить на поставленный вами вопрос.

Но, так или иначе, я задался целью исследовать поставленную задачу, в результате чего я обнаружил один нетривиальный способ использования уже скомпилированных функций, написанных на языке Assembler. Этот метод был найден мной в работе clsASMpic 1.0 на сайте http://www.pscode.com. Код на языке ассемблера написан Робертом Рейментом (Robert Rayment).

Суть метода заключается в том, чтобы хранить уже скомпилированный код в виде константы. Каким образом это реализуется? Ниже приведена измененная мною выдержка из кода с моими пояснениями.

Листинг - пример использования хранимых функций

Private Const ASMCode As String = "5589E581EC780000005756538B5D0 ..." ‘шестнадцатеричное значение сильно сокращено с целью экономии места

Call LoadMCodeFromString (MyFunc, ASMCode) ‘ вызов функции преобразования

‘ Так как VB по умолчанию передает параметры ByRef - мы получаем массив MyFunc

Private Function LoadMCodeFromString(asm_code_ARRAY() As Byte, MCODE As String)

    Dim i As Long, aSize As Long

    aSize = Len(MCODE) ' размер кода

    Debug.Assert Not aSize And 1 ' число не может быть четным

    aSize = aSize \ 2

    ReDim Preserve asm_code_ARRAY(0 To aSize - 1) ' подготавливаем массив

    For i = 0 To aSize - 1

        asm_code_ARRAY(i) = Val("&H" & Mid$(MCODE, i * 2 + 1, 2)) 'создаем массив

    Next

End Function

    ptMf = VarPtr(MyFunc(0)) ' получаем адрес первого элемента, он же и будет адресом нашей процедуры

    CallWindowProc pMf, ... параметры

Как вы можете убедиться - решение весьма элегантно. Строковая переменная содержит код, который потом загружается в память в виде массива байтов. Для получения адреса процедуры используется встроенная функция VB - VarPtr - возвращающая указатель на переменную. И обычный вызов с помощью Win API - CallWindowProc.

Подробнее вы можете рассмотреть этот пример на сайте www.pscode.com.

 

 

Q. В моей программе я использую CommonControls. Для поддержки визуальных стилей WindowsXP я добавляю к своему e xe файлу - файл exe. manifest. Программа действительно отображается в стиле XP, но почему-то все c heck и option закрашены черным цветом. Как этого избежать?

 

G.M. Чтобы эти элементы отображались на форме в WindowsXP так, какими вы хотите их видеть достаточно поместить их на какой-нибудь контрол-контейнер, например Frame или Picture.

Q. Как можно дописать в скомпилированный exe файл какую-то информацию, так чтобы он продолжал запускаться как всегда? И вообще, можно ли это сделать.

 

G.M. Можно. Действительно, любые записанная в конец exe файла данные будут игнорироваться загрузчиком ОС, поэтому вы можете свободно писать и читать добавленные данные, при условии, что, он доступен на чтение и запись. Приведу простой пример - мы имеем exe файл определенного размера (известный заранее) и другую программу с формой, текстовым полем и кнопкой. Содержимое файла, который мы будем использовать как хранилище данных, нас не интересует.

Листинг form1.frm

Private Sub Command1_Click()

Dim f As Integer

f = FreeFile ' получаем свободный id

Open "test.exe" For Binary As #f ' открываем файл

    Put #f, file_size, Text1.Text ' Помещаем содержимое Text1 в конец файла

Close #f

End Sub

Теперь откройте файл любым редактором и посмотрите в его конец. Вы увидите именно тот текст, который был введен в поле Text1. Теперь двойным щелчком запустите файл - вы убедитесь в его работоспособности.

Q. Как получить информацию о PE файле из VB? Т.е. прочитать заголовки и получить нужную информацию?

 

G.M.  Для этого необходимо объявить соответствующие типы, описывающие PE заголовки и заполнить их данными из открытого файла. Для примера приведу код получающий данные из DOSHEADER и NTHEADER.

Объявление соответствующих структур:

Код для модуля

Public Type IMAGEDOSHEADER

    e_magic As String * 2

    e_cblp As Integer

    e_cp As Integer

    e_crlc As Integer

    e_cparhdr As Integer

    e_minalloc As Integer

    e_maxalloc As Integer

    e_ss As Integer

    e_sp As Integer

    e_csum As Integer

    e_ip As Integer

    e_cs As Integer

    e_lfarlc As Integer

    e_ovno As Integer

    e_res(1 To 4) As Integer

    e_oemid As Integer

    e_oeminfo As Integer

    e_res2(1 To 10)    As Integer

    e_lfanew As Long

End Type

Public Type IMAGE_FILE_HEADER

    Machine As Integer

    NumberOfSections As Integer

    TimeDateStamp As Long

    PointerToSymbolTable As Long

    NumberOfSymbols As Long

    SizeOfOptionalHeader As Integer

    Characteristics As Integer

End Type

Public Type IMAGE_DATA_DIRECTORY

    VirtualAddress As Long

    size As Long

End Type

Public Type IMAGE_OPTIONAL_HEADER

    Magic As Integer

    MajorLinkerVersion As Byte

    MinorLinkerVersion As Byte

    SizeOfCode As Long

    SizeOfInitializedData As Long

    SizeOfUninitializedData As Long

    AddressOfEntryPoint As Long

    BaseOfCode As Long

    BaseOfData As Long

    ImageBase As Long

    SectionAlignment As Long

    FileAlignment As Long

    MajorOperatingSystemVersion As Integer

    MinorOperatingSystemVersion As Integer

    MajorImageVersion As Integer

    MinorImageVersion As Integer

    MajorSubsystemVersion As Integer

    MinorSubsystemVersion As Integer

    Win32VersionValue As Long

    SizeOfImage As Long

    SizeOfHeaders As Long

    CheckSum As Long

    Subsystem As Integer

    DllCharacteristics As Integer

    SizeOfStackReserve As Long

    SizeOfStackCommit As Long

    SizeOfHeapReserve As Long

    SizeOfHeapCommit As Long

    LoaderFlags As Long

    NumberOfRvaAndSizes As Long

    DataDirectory(0 To 15) As IMAGE_DATA_DIRECTORY

End Type

Public Type IMAGE_NT_HEADERS

    Signature As String * 4

    FileHeader As IMAGE_FILE_HEADER

    OptionalHeader As IMAGE_OPTIONAL_HEADER

End Type

' Соответствующие переменные

Public DOSHEADER As IMAGEDOSHEADER

Public NTHEADER As IMAGE_NT_HEADERS

‘ Нам понадобится эта функция для копирования данных в структуры

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _

(pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Прочитаем содержимое файла в массив бинарных данных:

Код для процедуры в форме

Dim PEDATA() As Byte

Dim fH As Long

fH = FreeFile 'Лучше получать свободное значение, чем использовать число, например 1

Open "c:\winnt\explorer.exe" For Binary As #fH ‘Не забудьте исправить путь

ReDim PEDATA(LOF(fH) - 1) ' Определяем размер массива под данные

Get #fH, , PEDATA ' Считываем файл

Close #fH

CopyMemory DOSHEADER, DATA(CNT), Len(DOSHEADER)

CopyMemory NTHEADER, DATA(DOSHEADER.e_lfanew), Len(NTHEADER)

‘ Все данные загружены, теперь можно получать любую информацию

MsgBox "Machine: " & NTHEADER.FileHeader.Machine & vbCrLf & _

"Minor OS Version: " & _

NTHEADER.OptionalHeader.MinorOperatingSystemVersion & vbCrLf & _

"Signature: " & NTHEADER.Signature

В общем случае, этот метод подходит для любого файла, описываемого соответствующими ему структурами, частным случаем которого является файл формата PE. В качестве упражнения рекомендую взять описание любого формата, например ICO, и попытаться получить данные из файлов в директории Windows.

Web - программирование

 

Q. В своем приложении я использую глобальную переменную $_SESSION, где храню данные пользовательских сеансов. Не может ли взломщик передать ее содержимое в строке GET запроса?

 

G.M. Подобные вопросы проще разрешить самостоятельно протестировав свое приложение. Я понимаю, что ввело вас в заблуждение. Чтобы показать, что ваши опасения напрасны, рассмотрим следующие два примера. Создадим простое приложение, которое выводит содержимое переменной в броузер.

Листинг test1.php

<?

echo @$_SESSION[‘test’];

?>

Запустим эту программу и передав подобный GET запрос: /test1.php?_SESSION[test]=Hi. Вы можете убедиться, что он проходит и программа выводит ожидаемое слово "Hi". Возможно, что именно это вас и смутило. Но теперь рассмотрим другой пример:

Листинг test2.php

<?

session_start();

echo @$_SESSION[‘test’];

?>

Теперь, сколько бы мы не нажимали Refresh (‘Обновить’) в броузере, придумывая самые неимоверные GET запросы с вариациями на тему передачи содержимого в $_SESSION, да и любую другую подобную переменную, ничего не получится. Можете спасть спокойно, авторы PHP предусмотрели такую маленькую хитрость.

Q. В моем сценарии на PHP 4.0 обрабатывается форма содержащая флажки выбора. Проблема заключается в том, что значение  передается сценарию только тогда, когда флажок выбран. Но хотелось бы получить значение и тогда, когда флажок не выбран. Как решить эту задачу?

G.M. Просто. Так как броузер отправляет значение value флажка лишь если он выбран, то можно перед самим элементом checkbox поместить элемент hidden с тем же значением name. При этом, если флажок будет выбран пользователем, то значение этой переменной примет указанное в value значения checkbox - PHP подставит переменной $check1 последнее значение в запросе (GET или POST).

Листинг form.html

<form action=’test.php’>

  <input type=’hidden’ name=’check1’ value=’0’>

  <input type=’checkbox’ name=’check1’ value=’1’>

<input type=’submit’ name=’submit’ value=’submit’>

</form>

Q. Мне необходимо получить в моем сценарии содержимое странички выдаваемое программой, расположенным на удаленном хосте при передаче ему параметров. Например: http://myhost/index.php?page=1.

 

G.M. Благодаря тому, что в PHP предусмотрена работа с удаленными файлами так же, как если бы они были расположены на вашем хосте, то можно легко получить ответ программы с удаленного хоста:

Листинг get_remote_page.php

<?

$content = join('', file('http://vbstreets/index.php?page=news'));

echo $content;

?>

Проектирование

 

Q. Поможет ли популярности нашего веб-сайта оригинальный и нестандартный дизайн. Отказ от стандартных форм?

 

G.M. Как это обычно бывает - у медали две стороны. На первый взгляд - оригинальности и нестандартность дизайна определенно привлечет внимание пользователей, но по статистике, если пользователь в течение короткого времени не сможет разобраться с тем, как работают веб-приложения на вашем сайте - он покинет ваш ресурс. Поэтому необходимо найти оптимальное решение - совместить нестандартность и простоту работы. Представьте себя начинающим пользователем Интернет, знакомым лишь с простыми Windows приложениями и не желающего вникать в тонкости вашего подхода. Оцените свой ресурс, представив себя на его месте.

Q. Что лучше - использование стандартных элементов веб-форм или использование собственных ActiveX элементов и Java апплетов?

G.M. На мой взгляд, использование ActiveX и Java, конечно, сильно расширяет функциональность ваших веб-приложений и характеризует ресурс как созданный           профессиональными программистами, но с другой стороны требует от клиента загрузки контролов и аппетов и выполнение их. При этом следует учесть, что клиент может обладать сильно устаревшим «железом» и «софтом», что сведет все ваши усилия на нет. Лично я стараюсь использовать простой HTML везде, где это возможно.

Q. Насколько выгодно использовать в своих приложениях компоненты, разработанные по принципу «открытого кода»?

 

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

Q. Необходима ли сайту система управления содержимым?

 

G.M. Прежде всего подумайте, стоит ли на вашем сайте размещать подобную систему? Я могу привести несколько вариантов, когда использование такой системы сильно облегчит поддержание ресурса:

1. Сайт представляет собой часто обновляемый информационный портал с большим количеством информации

2. Сайт разрабатывается более чем одним человеком - чтобы содержимое было структурировано, то необходимо разработать правила публикации, либо использовать единую систему управления контентом.

Если же это домашняя страничка или «визитная карточка» - сайты на которых информация обновляется крайне редко, то установка такой системы неоправданна - вы потратите больше времени на установку, настройку и обслуживание системы.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=1545