Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 1997, Андрей Колесов, Ольга ПавловаУ вас могут появиться некоторые проблемы при установке VB 5.0 (в том числе и свободно распространяемой редакции VB5/CCE), если на компьютере уже стоит VB 4.0. На это особенно стоит обратить внимание тем, кто рискует работать с бета- версиями, уже вовсю распространяющимися на "китайских" CD.
Дело в том, что VB 5.0 и VB 4.0 используют ряд одних и тех же элементов управления OCX. Соответственно, при установке VB 5.0 он автоматически заменяет эти модули на свои. С чисто юридической точки зрения может возникнуть проблема с дистрибьюцией приложений, написанных для VB 4.0, в которые могут попасть компоненты VB 5.0. Об этом были специально предупреждены бета-тестеры. Но могут возникнуть трудности и чисто технического характера. Поясним это на собственном примере.
Мы установили на свой ПК, где уже был VB 4.0, сначала VB5/CCE (бета-версия 1), а потом VB5/Pro (официальная бета-версия 2). Неожиданно при запуске утилиты API Text Viewer (VB 4.0) появилось сообщение об ошибке: "Модуль COMDLG32.OCX отсутствует или неверно лицензирован". Потом обнаружилось, что и другая утилита не хочет работать с COMCTL32.OCX. Однако сам VB 4.0 работал с этими модулями нормально. После небольшого анализа прояснилась следующая ситуация.
Оказалось, что на CD находилось по две версии этих модулей: для VB5/CCE с датой 25.10.96 и для VB5/Pro с датой 01.10.96. Соответственно при их установке автоматически остались модули с более поздней датой (VB5/CCE). Мы подумали, не возникли ли проблемы из-за каких-то наших ошибок при установке (мы проводили установку в несколько приемов, что-то добавляя, что-то удаляя и пр.), и решили заново провести полную инсталляцию. Однако после удаления VB5/Pro с компьютера исчезли все его OCX: он посчитал их своими собственными компонентами! Остались лишь файлы COMDLG32.OCX и COMCTL32.OCX, которые он, по-видимому, определил как чужие из-за их даты. Короче говоря, VB 4.0 оказался без расширенного набора элементов управления.
Повторив установку VB5/Pro, мы обнаружили те же самые ошибки. Тогда, заподозрив дефект именно в модулях от 25.10.96, мы опять удалили VB5/Pro (для чистоты эксперимента), переименовали на жестком диске файлы COMDLG32.OCX и COMCTL32.OCX от 25.10.96 и повторили установку. С версиями модулей от 01.10.96 все вроде бы стало работать нормально.
Хотелось бы подчеркнуть, что речь здесь шла не о конечных версиях продукта, а об их бета-версиях. Однако описанная ситуация является довольно типичной для проблемы обновления версий вообще. В связи с этим нужно сказать, что идея компонентной организации вычислительных систем (когда разные приложения используют одни и те же компоненты — OCX, DLL и пр.) является безусловной прогрессивной и заманчивой, но в процессе ее практической реализации возникают многие вопросы, в том числе касающиеся обновления версий компонентов, контроля их принадлежности (например, когда при удалении приложения оно заодно удаляет и общие модули) и т.п. К тому же, несмотря на все желания разработчика, не всегда удается добиться полной совместимости "снизу вверх". Или у вас вдруг могут возникнуть проблемы с оперативной памятью из-за увеличения размеров новых версий компонентов. Так что будьте внимательны.
Наш читатель обратился к нам с такой проблемой:
При создании базы данных в VB 4.0 с помощью Data Manager для русскоязычных строковых переменных не создается индекс и не работают методы поиска и сортировки. Однако если создать БД с помощью русскоязычного ACCESS в русскоязычной версии Windows, то потом с ней можно работать в VB без всяких проблем. Создавать БД в среде VB без Data Manager я не пробовал, может быть, там этих проблем и нет.
Ответ
При работе в VB правила сортировки и поиска в базах данных в самой программе устанавливаются с помощью параметра locale при создании и преобразовании БД (методы CreateDatabase и CompactDatabase). После создания базы данных значение параметра фиксируется (для объектов DataBase и Field) в виде свойства CollatingOrder, которое можно прочитать (но не изменить!) в ходе работы программы:
object.CollatingOrder
Этот параметр можно использовать в программе для настройки операции сравнения и поиска строковых переменных, создания новых объектов (например, баз данных) и пр.
Значения наиболее актуальных вариантов параметра locale для VB 4.0 (в нем предусмотрено 20 значений для разных языков) таковы:
dbSortGeneral = 1033 = &409 ' English dbSortCyrillic = 1049 = &419 ' Russian - в VB 3.0 его нет dbSortNeutral = 1024 = &400 ' без сортировки
(Для VB 3.0 состав кодов меньше, русского языка там нет, символьные названия констант и их значения — другие).
Эти параметры также можно задавать и при работе в Data Manager. Создавая и открывая базу данных, выделите в списке нужную таблицу и нажмите кнопку Design. В списке описателей полей Вы увидите параметр CollatingOrder, который задается в цифровом виде (конечно, было бы удобнее иметь дело со списком типа General, Cyrillic...). Затем выберите нужное поле, нажмите Edit и т.д.
Учитывая предыдущий вопрос читателя, имеет смысл подробнее рассмотреть некоторые аспекты работы с символьными переменными, в том числе с русскими буквами. Вообще говоря, переменная String представляет собой простую последовательность байтов. Соответственно операции сравнения (на них были основаны также алгоритмы поиска и сортировки) сводятся к последовательному сравнению значений байтов строки (кодов символов). Однако в частном (но наиболее часто встречающемся) случае строка является осмысленным набором символов, которые записаны в виде ASCII кода. А для них операции побайтового сравнения кодов не всегда подходят.
Например, для варианта русской кодовой таблицы ASCII последовательность сортировки (в порядке возрастания кодов) выглядит так:
ABCD...abcd...АБВГ...абвг
Для алфавитной же сортировки требуется другая последовательность (именно она реализуется при работе с базами данных):
aAbBcCdD...аАбБвВгГ
А для операций поиска необходимо часто вообще игнорировать регистр букв (прописные/строчные) (ignore case). В старых версиях Basic операторы Ucase/Lcase (преобразование всей строки в прописные или строчные буквы) являются фактически единственными командами, позволяющими производить какие-либо специфические операции над символьными переменными. Но они работали только с английскими буквами!
Впервые алгоритмы алфавитной сортировки и поиска были реализованы в базах данных ISAM, включенных в MS Basic PDS 7.1. В них была предусмотрена поддержка нескольких национальных языков, но русского среди них не было. В VB 3.0 в дополнение к работе с БД (а, может быть, и раньше: с VB 2.0 мы не работали) появились аналогичные возможности на языковом уровне — операторы InStr и StrComp. В VB 4.0 была реализована также поддержка для русского языка.
Преобразование символьных данных
В VB 4.0 операторы Lcase/Ucase выполняют соответствующие преобразования не только над латинскими буквами (как в VB3), но и со всеми буквами установленной кодовой таблицы Windows 95. (У нас установлена 32-разрядная версия VB4. Может быть, это работает и для Windows 3.x? Поделитесь опытом.) Таким образом, для cp251 результатом функции Ucase$("Вася") будет: ВАСЯ. В VB 4.0 появилась также новая функция StrConv, которая выполняет дополнительные варианты преобразования кодов (в том числе аналог операций Ucase/Lcase в Unicode и обратно).
Операции сравнения
В VB есть два оператора: поиска — InStr (расширенный вариант оператора INSTR, используемого в Basic/DOS) и сравнения — StrComp. В них имеется возможность использования дополнительного (необязательного) параметра Compare:
В случае отсутствия параметра Compare в операторе используется его значение по умолчанию — 0 (двоичная обработка). Однако этот параметр по умолчанию можно изменить для всех процедур конкретного модуля с помощью оператора Option Compare Binary/Text: Binary указывает на режим двоичного сравнения (он установлен по умолчанию), Text — на алфавитный режим для текущей кодовой таблицы Windows.
Примеры (VB4, cp1251):
Print StrConv ("Петя петухов", 3) ' = Петя Петухов Print InStr ("Петя петухов", "п", 0) ' = 6 Print InStr ("Петя петухов", "п", 1) ' = 1 Print StrComp ("Петя", "петЯ", 1) ' = -1 Print StrComp ("Петя", "петЯ", 0) ' = 0
ASCII-таблица латинских символов организована таким образом, что коды строчных букв (a-z = &H61-&H7A) отличаются от кодов соответствующих прописных (A-Z = &H41-&H5A) только наличием 1 в третьем разряде байта (&H20). Более того, данный принцип реализован для Windows-кодировки (cp1251) русских букв: а-я = &HE0-&HFF, А-Я = &HC0-&HDF.
Знание этой особенности кодировки может иногда пригодиться для каких-либо нетривиальных преобразований символьных данных, в том числе при программировании на языках, не имеющих операторов типа Ucase/Lcase (например, на ассемблере). Вот как может выглядеть фрагмент такого преобразования, когда прописные буквы меняются на строчные, а строчные — на прописные:
Word$ = "aBcDeF" For i% = 1 to Len(Word$) Mid$(Word$,i%) = Chr$(Asc(Mid$(Word$,i%)) Xor &H20) Next Print Word$ 'напечатано - AbCdEf
Бывают ситуации, когда в состав вашего приложения входят какие-то специальные шрифты, которые распространяются вместе с ним. Соответственно эти шрифты должны входить в состав дистрибутива и устанавливаться на жесткий диск в процессе инсталляции, то есть их надо уметь аккуратно добавлять к системе. Для этого можно использовать API-функцию LoadFontResource. Однако вначале следует самостоятельно скопировать файл в то место, где хранятся остальные шрифты. В Windows файл шрифта копируется в системный каталог, а в Windows 95 — в каталог \Windows\Fonts.
После вызова функции LoadFontResource нужно известить все открытые окна о том, что таблица шрифтов была изменена. Для этого отправьте в HWND_BROADCAST сообщение WM_FONTCHANGE, которое даст указание Windows послать данное сообщение во все открытые окна.
Процедура AddFont копирует файл шрифтов, регистрирует данный шрифт, а затем уведомляет об этом все открытые окна. Она представлена в варианте для VB 4.0. Для VB 3.0 в этом тексте нужно убрать фрагмент, относящийся к 32-разрядной версии, и объявление Private в операторах Declare:
Option Explicit #If Win32 Then Private Declare Function AddFontResource Lib _ "gdi32" Alias "AddFontResourceA" (ByVal _ lpFileName As String) As Long Private Declare Function SendMessage Lib _ "user32" Alias "SendMessageA" (ByVal hWnd _ As Long, ByVal wMsg As Long, ByVal wParam _ As Long, lParam As Long) As Long Private Declare Function GetWindowsDirectory _ Lib "kernel32" Alias "GetWindowsDirectoryA" _ (ByVal lpBuffer As String, ByVal nSize As _ Long) As Long #Else Private Declare Function AddFontResource Lib _ "GDI" (ByVal lpFilename As Any) As Integer Private Declare Function SendMessage Lib "User" _ (ByVal hWnd As Integer, ByVal wMsg As Integer, _ ByVal wParam As Integer, lParam As Any) As Long Private Declare Function GetWindowsDirectory _ Lib "Kernel" (ByVal lpBuffer As String, ByVal _ nSize As Integer) As Integer #End If Const WM_FONTCHANGE = &H1D Const HWND_BROADCAST = &HFFFF& Function AddFont3 (FileName As String) As Integer Dim WindowsDir As String Dim Lbuf As Long ' - Получение каталога Windows Lbuf = GetWindowsDirectory (WindowsDir, Len (Buffer)) If Lbuf Then WindowsDir = Left$(windowsDir, Lbuf) ' - Копирование файла FileCopy FileName, WindowsDir & "\Fonts\" & FileName ' - Добавление шрифта If AddFontResource (FileName) Then SendMessage HWND_BROADCAST, WM_FONTCHANGE, 0, 0 AddFont3 = True End If End If End Function
Наличие внутри строковой переменной двойных или одинарных кавычек — довольно обычная проблема. Путаница же начинается тогда, когда такие переменные используются для составления сложных символьных выражений, не допускающих применения подобных символов: их появление внутри отдельного слова требует специального выделения самого слова с помощью все тех же кавычек. Пример таких символьных выражений — SQL-операторы.
Чтобы как-то автоматизировать операции расстановки кавычек, можно создать функцию, которая получает символьную переменную и проверяет, есть ли там двойные кавычки, внедренные в саму символьную переменную. Если они есть, тогда данная функция помещает одиночные кавычки в начало и конец переменной. В противном случае она помещает двойные кавычки в начало и конец переменной и возвращает результат вызывающей функции:
SQLx = "INSERT UserTable VALUES (" SQLx = SQLx & Index & "," SQLx = SQLx & q(txtUserName.text) & "," SQLx = SQLx & q(txtPassword.text) & ")" Function q(strng As String) As String ' ' Данная функция возвращает передаваемую ' ей символьную переменную, заключенную ' в кавычки. Если символьная переменная ' содержит двойные кавычки, данная функция ' заключает эту переменную в одиночные ' кавычки. ' If InSTR(1, strng, Chr(34)) Then q = Chr(39) & strng & Chr(39) Else q = Chr(34) & strng &Chr(34) End If End Function
Эта функция экономит время и снижает возможность ошибок при написании длинных SQL-операторов. Единственный случай, когда она не работает, — наличие в одной и той же символьной переменной как одиночных, так и двойных кавычек. Однако это бывает крайне редко.