Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 1996, Андрей Колесов, Ольга ПавловаКак уже подчеркивалось ранее, все операции над строковыми переменными, в том числе сравнения, выполняются существенно медленнее операций с числовыми данными и даже часто уступают по времени обращения к числовым функциям. В частности, при проверке пустой строки конструкция:
If Len(sName) Then ...
выглядит предпочтительнее, чем:
If sName <> "" Then ...
Для этого следует просмотреть весь список элементов управления и присвоить значение False каждому свойству Enabled с помощью такого кода:
Sub cmdArray_Click () Dim iLoop As Integer For iLoop = 0 To Me.Controls.Count - 1 Me.Controls(iLoop).Enabled = False Next iLoop End Sub
Так же просто сделать невидимой всю форму:
Me.Enabled = False
Однако следует помнить, что в последнем случае, сделав невидимым управляющее меню формы, вы сможете закрыть эту форму, лишь выгрузив ее из другой формы.
Для быстрого поиска нужной строки в списке, в частности в элементе управления Combo Box, можно использовать функцию API SendMessage() (вариант для 16-разрядных приложений), обеспечивающую более эффективное выполнение программы, чем аналогичный код на VB. Для этого можно, например, прибегнуть к процедуре CBFindString:
Declare Function SendMessage Lib "User" _ (ByVal hWnd As Integer, ByVal wMsg As Integer, _ ByVal wParam As Integer, lParam As Any) As Long Sub CBFindString(ctlEdit As Control, sSearch As String) Dim lPos As Long Const CB_FINDSTRING = &H40C lPos = SendMessage(ctlEdit.hWnd, CB_FINDSTRING, 0, ByVal sSearch) If lPos >=0 Then ctlEdit.ListIndex = lPos End Sub
Это можно сделать с помощью такой процедуры:
Public Sub UnLoadAll () Dim i As Integer i = Forms.Count ' количество загруженных форм Do While i > 0 Unload Forms(i - 1) ' если форма не выгрузилась - завершить выполнение функции If i = Forms.Count Then Exit Do i = i - 1 Loop End Sub
При работе в Windows 95/NT для ожидания истечения некоторого отрезка времени (реализации задержки) вместо оператора DoEvent удобнее использовать функцию API (Win32) Sleep, которая должна быть описана следующим образом:
Public Declare Sub Sleep Lib "kernel32" _ Alias "Sleep" (ByVal lMilliseconds As Long)
Обращение к ней может выглядеть так:
Sleep 15*1000& 'миллисекунды, в данном случае - 15 секунд
При разработке VB-приложения иногда бывает полезно на уровне программного кода определить, в каком режиме это приложение работает: "Design Mode" (работа в среде VB) или "Run Time" (выполнение автономного модуля). Для этого, в частности, можно анализировать имена каталогов, где хранятся VB-проект и законченное приложение, примерно в таком виде:
If InStr (App.Path, "VB") Then Stop
Форму, сделанную в VB4, нельзя напрямую использовать в среде VB3. Если это все же необходимо, следует вручную отредактировать файл с определением формы, который выглядит примерно так:
VERSION 4.00 Begin VB.Form Form1 Caption = "Form1" ClientHeight = 5940 ... End Attribute VB_Name = "Form1" Attribute VB_Creatable = False Attribute VB_Exposed = False Option Explicit
Чтобы загрузить эту форму в VB3, нужно заменить VERSION 4.00 на VERSION 2.00, удалить все префиксы "VB.", которые имеются в конструкции Begin VB.Form Form1, а также удалить все объявления Attribute. Запомните этот файл и загрузите его в VB3.
Для этого проще всего использовать команду Dir$:
FileName$ = "C:\WINDOWS\WIN.INI" 'имя проверяемого файла If Dir$ (FileName$) <> "" Then ' данный файл существует Else ' данный файл не существует End If
Иногда приходится анализировать наличие указанного каталога (к примеру, при установке вашей программы на жесткий диск) и создавать новый при его отсутствия. Для этого можно использовать, например, такую процедуру:
Sub CreateLongDir(sDir As String) Dim sBuild As String, sDirTmp As String, i As Integer ' sDirTmp = sDir & "\" i = InStr (sDirTmp, ":") If i > 0 Then ' задано имя диска sBuild = Left$(sDirTmp, i) ' имя текущего каталога sDirTmp = Mid$(sDirTmp, i + 1) Else sBuild = "" ' имя текущего каталога End If Do ' проверка-создание вложенных каталогов i = InStr (2, sDirTmp, "\") If i = 0 Then Exit Do sBuild = sBuild & Left$(sDir, i - 1) sDirTmp = Mid$(sDirTmp, i) If Dir$(sBuild, 16) = "" Then 'нет такого каталога MkDir sBuild ' создание каталога End If Loop End Sub Sub Test () ' примеры обращения ' полное имя каталога с именем диска Call CreateLongDir("C:\Tests\TestDir\NewDir") ' полное имя каталога в текущем диске Call CreateLongDir("\Current\TestDir\NewDir") ' имя нового каталога относительно текущего каталога Call CreateLongDir("Current\TestDir\NewDir") End Sub
Здесь крайне важно дать правильное описание имени каталога при обращении к CreateLongDir (в соответствии с правилами обращения к функциям VB: MkDir, ChDir, RmDir, Dir):
Содержимое ячеек элемента управления Grid, поставляемого с VB, нельзя редактировать напрямую. Но проблема легко решается с помощью следующей конструкции:
Private Sub Grid1_KeyPress(KeyAscii As Integer) Grid1.Text = Grid1.Text & Chr(KeyAscii) End Sub
Эта операция выполняется для текущей выбранной ячейки таблицы.
Переход из VB3 в VB4 сопровождается некоторыми не очень приятные моментами, связанными с тем, что вроде бы очевидные программные конструкции работают в нем по-другому. Например, могут возникнуть проблемы с тривиальным гудком. Сколько раз "бибикнет" такая конструкция?
Beep: Beep
Ответ: в Basic/DOS, VB3 - два раза, а в VB4 - один.
Если на слух эта разница не очень убедительна, попробуйте другую конструкцию:
Beep: Beep: A = 1
В этом случае при работе в VB4 вы вообще ничего не услышите. Почему-то зарезервированное слово Beep (так же, как и Cls), написанное первым в строке с разделителем ":", в VB4 воспринимается как метка.
В VB3 отменить выделение всех элементов списка можно таким образом:
List1.Selected (-1) = False
К сожалению, в VB4 эта конструкция не работает.
Изменить расположение элементов списка можно, перетаскивая их мышью с помощью такой конструкции:
Sub List1_MouseDown (Button As Integer, Shift As Integer, _ X As Single, Y As Single) ' Эта процедура запоминает номер текущего ' элемента списка и его содержимое ' при нажатии клавиши мыши Old_Index = List1.ListIndex TmpText = List1.Text End Sub Sub List1_MouseUp (Button As Integer, Shift As Integer, _ X As Single, Y As Single) ' Когда вы отпустите клавишу мыши, ' указанный ранее элемент переместится ' в текущую позицию списка New_Index = List1.ListIndex If New_Index <> Old_Index Then List1.RemoveItem Old_Index ' удалить старый List1.AddItem TmpText, New_Index ' вставить новый End If End Sub
В общем модуле программы нужно зарезервировать глобальные переменные:
Dim TmpText As String Dim Old_Index As Integer Dim New_Index As Integer
Чтобы подробнее познакомиться с составом и описанием методов и свойств различных объектов (например, элементов управления OCX), имеющихся на вашем компьютере, в VB 4.0 можно воспользоваться командой Object Browser:
Если для продолжения процедуры необходимо определить, с каким типом элемента управления она в данный момент работает, можно применить следующую конструкцию, использующую функцию TypeOf:
Function myFunc (ctl As Control) ' Эта процедура работает для VB3 и VB4 If TypeOf ctl Is TextBox Then ' Текстовое поле Else If TypeOf ctl Is CommandButton Then ' Кнопка End If End Function
В VB4 это можно сделать с помощью новой функции TypeName, при этом код будет выглядеть примерно так:
Function myFunc (ctl As Control) Dim sCtlType As String sCtlType = TypeName(ctl) Select Case sCtlType Case "TextBox" ... Case "CommandButton" ... End Select End Function
Узнать точное имя типа или класса конкретного элемента управления можно, посмотрев его в окне Properties в среде VB.
Приложение А в руководстве "Building Applications with Microsoft Access for Windows 95" содержит основные инструкции по преобразованию 16-разрядных приложений для Access 2.0 в 32-разрядные приложения для Access 7.0. Ниже приводится пошаговая инструкция преобразования для незащищенных баз данных, основанных на последней бета-версии Access 7.0. Она немного дополняет информацию, содержащуюся в руководстве:
1. Если ваше приложение для Access 2.0 вызывает дополнительные 16-разрядные DLL-библиотеки или использует 16-разрядные элементы управления OLE, убедитесь, что у вас есть соответствующие 32-разрядные версии.
2. Преобразуйте библиотеки или дополнительные средства, необходимые для Access 7.0. Вам, вероятно, потребуется обновить библиотеки или дополнительные средства третьих фирм на 32-разрядные версии.
3. Если вы еще не разделили свое приложение для Access 2.0 на отдельное приложение и файлы данных data.mdb, то сделайте это сейчас. Вы можете связать таблицы Access 2.0 с приложениями как для Access 2.0, так и Access 7.0, а не наоборот. Access 7.0 имеет ограничение на 32 индекса плюс связи для одной таблицы, выйдя за которое, то вы потеряете индексы и/или связи.
4. Удалите ненужные объявления функций Windows API, а также вызовы функций. Например, библиотека CTL3DV2.DLL не нужна в приложениях, выполняемых только в Windows 95.
5. Откройте модуль кода, затем из меню Run выберите команду Compile Loaded Modules, чтобы скомпилировать весь код, содержащийся в приложении. Если вы не сделаете этого, переход к Access 7.0 может не удасться.
6. Временно отключите макрокоманду AutoExec для своего приложения, предварительно переименовав ее.
7. Сожмите MDB-файл, используемый в вашем приложении.
8. Попытайтесь преобразовать свой MDB-файл. Если преобразование не получится, создайте новую базу данных в Access 7.0 и вручную импортируйте туда объекты из MDB-файлов Access 2.0, используя в меню File команду Get External Data, а затем Import.
9. Используйте Add-in Manager для подключения необходимых дополнительных средств к своему приложению.
10. Если ваше приложение обращается к библиотечному коду, выберите команду References из меню Tools для того, чтобы открыть диалоговое окно References. Затем установите тип .MDA в поле Files of Types, выберите необходимый библиотечный MDA-файл и создайте ссылку на него.
11. Если ваше приложение содержит элементы управления OLE, проверьте, что необходимые ссылки установлены для 32-разрядных, а не для 16-разрядных версий.
12. Откройте преобразованный модуль и выберите команду Compile All Modules из меню Run. Исправьте ошибки в программе таким образом, чтобы все модули компилировались без ошибок. Приложение А руководства описывает наиболее часто требуемые изменения, которые следует внести в код.
13. После того как вы скомпилировали все свои модули, выберите команду Save All Modules из меню File.
14. В окне Module Options установите параметр Break On All Errors, если ваш опыт обработки ошибок на уровне строки с помощью операторов On Error Resume Next и If Err Then недостаточен.
15. Теперь попробуйте запустить свое приложение. Вероятно, появятся некоторые ошибки выполнения, но они обычно легко исправляются в Debug Window (Окне Отладки).
В VB4 как процедуры-функции, так и подпрограммы могут использовать ключевое слово Optional (Необязательный) в объявлении процедур для указания необязательного параметра. Например:
Function mfbCheckDBStatus(Optional vroTest As Variant) As Boolean
В этом случае функция может вызываться без указания параметра:
MfbCheckDBStatus
Если необходимо проверить наличие необязательного параметра, используйте функцию IsMissing для его тестирования.
Для этого можно воспользоваться функцией WinAPI:
Declare Function GetsystemMetrics Lib "User" (ByVal _ iIndex As Integer) As Integer Sub Form_Load() Dim iXres As Integer, iYres As Integer ' разрешающая способность экрана монитора: iXres = GetsystemMetrics(0) ' по оси X iYres = GetsystemMetrics(1) ' по оси Y End Sub
Довольно часто бывает необходимо по полному имени файла определить имя его каталога или/и само имя файла внутри каталога. Для этого можно воспользоваться следующими процедурами (они будут работать в любой версии MS Basic, здесь используется формат представления текста модуля для QB):
DECLARE FUNCTION InstrReverse% (Text$, Key$) DEFINT I-N SUB FileNameTest (PathFile$, Path$, File$) ' ' Вход: PathFile$ - полное имя файла ' Выход: Path$ - имя каталога ' File$ - имя файла ' Например: ' Вход: PathFile$ = "d:\Test1\Test2\File.txt" ' Выход: Path$ = "d:\Test1\Test2\" ' File$ = "File.txt" '''''''''''''''''''''''''''''''''''''''''''''' in = InstrReverse(PathFile$, "\") IF in <= 0 THEN Path$ = "": File$ = PathFile$ ELSE Path$ = LEFT$(PathFile$, in): File$ = MID$(PathFile$, in + 1) END IF END SUB FUNCTION InstrReverse (Text$, Key$) ' ' поиск ПОСЛЕДНЕГО (а не первого, как в INSTR) ' контекста в строке ' Text$ - исходная строка ' Key$ - разделитель в строке ' Например: ' Вход: Text$ = "d:\Test1\Test2\Test3" ' Key$ = "\" ' Выход: InstrReverse = 15 '''''''''''''''''''''''''''''''''''''' in = INSTR(Text$, Key$) IF in > 0 THEN DO WHILE in < LEN(Text$) in1 = INSTR(in + 1, Text$, Key$) IF in1 <= 0 THEN EXIT DO ELSE in = in1 LOOP END IF InstrReverse = in END FUNCTION