Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 2000, Андрей Колесов, Ольга ПавловаДля обмена данными между двумя строковыми переменными можно использовать такую простую процедуру:
Sub SwapString (String1$, String2$) Dim Save$ Save = String1$ ' запись в промежуточную переменную String1$ = String2$ String2$ = Save$ End If
Однако мы уже неоднократно подчеркивали, что операции присвоения строковых переменных требуют довольно много времени, так как это связано с динамическим перерезервированием оперативной памяти. Кроме того, время их выполнения напрямую зависит от длины строк.
Мы предлагаем "хитрый" вариант взаимного обмена содержимого двух строк, который выполняется очень быстро и не зависит от числа байтов в строке. Он основан на использовании API-функции для копирования областей памяти и применении реально существующих, но не описанных в документации функций VB (так называемые недокументированные функции):
Declare Sub CopyMemory Lib "kernel32" Alias _ "RtlMoveMemory" (Destination As Any, _ Source As Any, ByVal Length As Long) Public Sub SwapSrting(String1$, String2$) Dim Save As Long 'Своппинг двух строковых переменных Save = StrPtr(String1) Call CopyMemory(ByVal VarPtr(String1), ByVal VarPtr(String2), 4) Call CopyMemory(ByVal VarPtr(String2), Save, 4) End Sub
Как известно, в VB строковые переменные имеют описатель (дескриптор) и собственно содержимое строки. В данном случае мы выполняем своппирование не содержимого строк, а содержимого описателей. Для этого используются две недокументированные VB-функции:
Здесь нужно отметить два момента:
Dim LenBmy& Call CopyMemory(LenBmy$, ByVal StrPtr(MyString$) - 4, 4)
Тем, кто еще пользуется QuickBasic для DOS, напомним, что здесь длина строки хранится в ее описателе вместе с адресом. Но нужно иметь в виду, что адресация строк в QB 4.x и PDS 7.x различается, и соответственно различаются описатели строк. Адрес дескриптора можно получить командами VARSEG (адрес сегмента) и VARPRT (смещение внутри сегмента); копирование областей памяти производится командами PEEK (чтение байта) и POKE (запись байта).
Многие программисты порой забывают о том, что меню, создаваемое с помощью Menu Editor, состоит из компонентов, которые работают так же, как и все остальные элементы управления. Это, в частности, позволяет непосредственно в процессе работы приложения управлять пользовательским интерфейсом. Так, можно изменять название команды меню (свойство Caption), делать ее недоступной для выполнения (Enable) или невидимой (Visible). Если же вы создали массив меню, назначив данному компоненту индекс (свойство Index), динамически создавать или удалять элементы такого массива, можно, например, следующим образом:
i% = mnuFileArray.Ubound + 1 ' следующий номер индекса после самого ' большого уже существующего Load mnuFileArray(i%) ' создание нового элемента массива меню mnuFileArray(i%).Caption = "NewArray" & i% ' название команды ... Unload mnuFileArray(1) ' удалить элемент с индексом 1
Если у вас есть VB-проект, который вы хотите хотя бы иногда использовать в качестве шаблона при создании новых проектов, поместите его в каталог ...\VisualStudio\VB98\Template\Projects\ или другой каталог, который вы указали при установке VB 6.0. (Для VB 5.0 этот каталог называется VB5\Template\Projects\.) Тогда ваш шаблон будет автоматически появляться в окне New Project при создании нового проекта.
Такой вариант создания нового проекта на основе шаблона лучше, чем простая загрузка существующего проекта командой Open, ибо в последнем случае есть опасность забыть, что необходимо обязательно сразу же сохранить проект и все его компоненты командой Save As, иначе все изменения проекта будут записываться в исходный шаблон.
Точно так же вы можете записать шаблоны для отдельных компонентов проекта, которые хранятся в подкаталоге Template:
Template/Подкаталог При выполнении команды Project/Add компонент ----------------------------------- FORMS Form MDIForms MDI Form MODULES Modules CLASSES Class Modules USERCTLS User Control PROPPAGE Property Page USERDOCS User Document -----------------------------------
Если вы хотите создавать программные компоненты формы (модули формы), которые можно использовать как в VB, так и в Office/VBA, то вам нужно пользоваться конструктором MS Forms 2.0. В среде VBA он вызывается командой Insert|UserForm, в среде VB — Project|Add Microsoft Forms 2.0 Form (если такая команда отсутствует в среде VB, то конструктор нужно подключить с помощью команды Project|Components|Designers).
Напомним, что формы UserForms в VBA — это не то же самое, что VB-формы (Ruby Forms). VBA UserForms являются экземплярами ActiveX-конструктора Microsoft Forms 2.0 (FM20.dll), который входит в состав как VB, так и VBA. Более того, все элементы управления, представленные в нем по умолчанию, являются не встроенными, а ActiveX-компонентами (то есть при желании их можно подключить и к VB-форме). Чтобы убедиться в этом, откройте окно Tools|Additional Controls.
К сожалению, эти два вида форм различаются не только форматом их модулей. Каждая из них использует свой собственный набор встроенных элементов управления. Проблема же заключается в том, что эти наборы не только не совпадают функционально, но даже для одинаковых по значению элементов управления используются разные наименования свойств, событий и методов. MS Forms 2.0 не поддерживают ряд очень полезных встроенных элементов управления VB (Timer, FileListBox, PictureBox, DriveListBox, DirListBox, Menu, Shape и Line), но при этом включают другие полезные компоненты (TabStrip, MultiPage) и команды проектирования формы (TabOrder).
Отсутствие встроенных элементов управления (на что мы сетовали в предыдущем совете) можно компенсировать возможностью использования дополнительных элементов управления ActiveX. Если у вас имеется VB 5.0 или 6.0, то вы можете самостоятельно сделать, например, на основе встроенного элемента Timer собственный ActiveX-компонент для реализации функции таймера. Такой OCX можно было бы использовать в конструкторе MS Forms 2.0 (VBA-формы). Но прежде чем делать свои компоненты, стоит посмотреть, нет ли уже готового подходящего элемента на Web-сайтах с VB-ресурсами и разными Freeware & Shareware.
Так, очень нужный порой компонент Timer в виде файла IETimer.OCX можно найти по адресу www.basic.visual2000.ru/develop/vb/source/.
Но здесь мы хотели бы отметить некоторые различия в процедурах подключения OCX к среде VB и VBA.
RegSvr32.exe Your.OCX
Рис. 302
RegSvr32.exe /u Your.OCX
Но иногда оказывается, что этого недостаточно, и приходится вручную просматривать (искать заданный файл) и чистить Реестр.
Мы уже приводили несколько советов, как можно приостановить выполнение приложения на некоторое время. В частности, это можно сделать с помощью элемента управления Timer или путем опроса текущего времени (обращения к функции Timer) в цикле программы. Однако оба варианта имеют некоторые ограничения. Например, элемент управления Timer позволяет делать задержку в диапазоне от 1 секунды до 1 минуты, а функция Timer имеет минимальную дискретность в 1 секунду. При использовании соответствующей API-функции этих ограничений нет:
' описание функции Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) ' обращение к ней (аргумент задается в миллисекундах) Sleep 150010 ' ожидание 150,01 секунды
Но при использовании этой конструкции есть другая проблема — "глубокий сон" приложения не удастся прервать до истечения заданного интервала времени.
С помощью API-функции SendMessage вы можете управлять позициями табулятора в элементе управления ListBox. Это может быть полезно, если каждая строка списка состоит из полей, разделенных символом Tab (код ASCII = 9), то есть если список выводится в виде таблицы, содержащей несколько колонок. Для этого в программный модуль запишите такое объявление и подпрограмму:
Private Declare Function SendMessage Lib _ "user32" Alias "SendMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) As Long Private Const LB_SETTABSTOPS = &H192 Public Sub SetListTabStops(MyListBox As ListBox, _ ParamArray ParmList() As Variant) 'Коррекция позиции Tabs в списке ListBox ' ' Для передачи переменного числа однородных параметров ' используется конструкция ParamArray ' ВНИМАНИЕ! Нижний индекс ParmList равен 0 ' даже при Option Base 1!!! ' Позиция табулятора определяется в специальных величинах ' окна, которая в среднем равна 1/4 символа Dim i As Long Dim NumColumns As Long ' ' формирование массива для установки табуляторов ReDim ListTabs(0 To UBound(ParmList)) As Long For i = 0 To UBound(ParmList) ListTabs(i) = ParmList(i) Next i NumColumns = UBound(ParmList) + 1 ' ' установка новых значений позиций табулятора Call SendMessage(MyListBox.hWnd, LB_SETTABSTOPS, _ NumColumns, ListTabs(0)) ' вывести новое изображение списка lstMyListBox.Refresh End Sub
Чтобы протестировать эту конструкцию, создайте форму, на которой разместите элемент списка lstMyListBox и две командные кнопки Command1 и Command2. Далее запишите такой программный код для этих компонентов:
Private Sub Form_Load() ' Начальное формирование списка ' с тремя колонками, разделенными символом vbTab = Chr$(9) lstMyListBox.AddItem "Колонка11" & vbTab & "Колонка12" _ & vbTab & "Колонка13" lstMyListBox.AddItem "C21" & vbTab & "C22" _ & vbTab & "C23" End Sub Private Sub Command1_Click() ' одинаковое расстояние в 52 позиции (13 символов) Call SetListTabStops(lstMyListBox, 52) End Sub Private Sub Command2_Click() ' переменное расстояние (10 и 30 символов) Call SetListTabStops(lstMyListBox, 40, 120) End Sub
Запустите на выполнение созданный проект, и вы увидите такое изображение списка:
Щелкните кнопку Command1 — список примет следующий вид:
Щелкните Command2, и получится другое изображение: