Советы тем, кто программирует на VB & VBA
Совет 410. Настройка интерфейса VB/VBA
Совет 411. Добавьте команды управления комментариями к среде разработки
Совет 412. Как прочитать адреса отправленных писем
Совет 413. Использование пробелов в именах таблиц баз данных ADO
Совет 414. Как передать Null в API-функцию
Совет 415. Как вставить колонки в VB MSFlexGrid
Совет 416. Динамическое увеличение динамического массива
Совет 417. Как работать с адресной книгой MS Exchange
Совет 418. Как отключить ADO Recordset, созданный с помощью объекта Command
Совет 419. Как использовать символ @ в именах параметров SQL Server
Совет 420. Генерация строк соединения для OLE DB
Совет 421. Как создавать ISAM-файлы
Совет 422. Защита от макросов при использовании глобальных шаблонов
Совет 410. Настройка интерфейса VB/VBA
алеко не все VB/VBA-разработчики знают о возможности выполнять настройку интерфейса среды разработки с помощью окна Customize, которое открывается командой View|Toolbars|Customize (рис. 1).
Как и в офисных приложениях, вы можете делать видимыми или невидимыми панели инструментов (многие программисты думают, что существует только одна — Стандартная), создавать собственные панели, перемещать команды между разными панелями и меню, то есть создавать такую систему меню и панелей инструментов, какая вам кажется наиболее подходящей.
Советуем также открыть вкладку Customize|Commands (рис. 2). В списке Categories вы обнаружите перечень категорий команд, которые представлены в среде в виде меню. Но если внимательно посмотреть на состав команд отдельных категорий (список Commands), то можно заметить, что на самом деле их гораздо больше, чем видны в меню по умолчанию.
К сожалению, справка окна Customize работает не самым лучшим образом, поэтому разобраться в смысле незнакомых команд непросто. Пользователи офисных приложений находятся в лучшем положении: в справке имеется раздел VB Use Interface Help с описаниями всех встроенных команд. Они также могут воспользоваться кнопкой Description для вывода подсказки (в VB эта кнопка почему-то не работает).
Совет 411. Добавьте команды управления комментариями к среде разработки
родемонстрируем, как применить предыдущий совет на практике. Так, при разработке приложений довольно часто бывает нужно закомментировать целые блоки программного кода. Это приходится делать в тех случаях, когда нужно на некоторое время удалить код или даже удалить совсем, но оставить его в виде комментария. Обычно вы это делаете, пробегая курсором по всем строкам и нажимая клавишу апострофа. Но все это можно гораздо проще совершить с помощью малоизвестных для вас команд среды разработки VB.
Командой View|Toolbars|Customize откройте окно Customize|Commands. Далее в списке Categories выделите строку Edit. Потом в списке Commands найдите команды Comment Block и Uncomment Block, а затем перетащите их мышью на панель управления Standard. Теперь, если вы выделите некоторый фрагмент кода, то одним щелчком мыши по соответствующей команде сможете устанавливать или убирать апострофы в первой позиции выделенных строк.
Кстати, среди таких малоизвестных команд имеются также Indent и Outdent, которые позволяют сдвигать фрагменты текста на один отступ вправо или влево.
Совет 412. Как прочитать адреса отправленных писем
опустим, вам нужно получить список адресов, по которым вы отправили письма. Если они лежат в папке отправленных писем Outlook, то это можно сделать с помощью такого кода:
Dim myItems As Items Dim mailmsg As MailItem ' выборка писем из папки Set myItems = _ Application.Session.GetDefaultFolder(olFolderOutbox).Items If myItems.Count > 0 Then ' есть письма For Each mailmsg In myItems MsgBox "Куда отправлено - " & mailmsg.To Next Else MsgBox "нет отправленных писем" End If
Если же вам нужно обратиться к некоторой произвольной пользовательской папке, то выборка писем будет выглядеть примерно так:
Set myItems = Application.GetNamespace("MAPI"). _ Folders.Item("Personal Folders").Folders.Item("Outbox").Items
Совет 413. Использование пробелов в именах таблиц баз данных ADO
аверняка вам известно, что в именах таблиц и полей баз данных ADO нельзя использовать ни пробелы, ни идентификаторы типа «Большая деревня». Но на самом деле подобные имена вполне допустимы, просто для их обозначения нужно применять квадратные скобки. Соответственно метод Open будет выглядеть примерно следующим образом:
rst.Open "[Большая деревня]", conn,,,adCmdTable А SQL-запрос может выглядеть так: rst.Open "SELECT [Малыш Дик] FROM [Большая деревня]"
Совет 414. Как передать Null в API-функцию
овольно часто бывает необходимо передать в API-функцию значение Null (0&) в качестве одного из аргументов. Но сделать это порой не так-то просто, поскольку вы можете столкнуться с ограничением объявления функции: VB выдаст в этом случае сообщение об ошибке. Например, рассмотрим такое объявление функции, которое получено копированием соответствующего кода из файла Win32api.txt с помощью API Viewer:
Public Declare Function ScrollWindowEx Lib _ "user32" (ByVal hwnd As Long, ByVal dx As Long, _ ByVal dy As Long, lprcScroll As RECT, _ lprcClip As RECT, ByVal hrgnUpdate As Long, _ lprcUpdate As RECT, ByVal fuScroll As Long) As Long
Здесь видно, что эта функция использует несколько структур RECT, в которых задается область прокрутки Windows. Но если передать вместо адресов блоков данных нуль, то Windows самостоятельно будет определять эти области. С этих позиций следующее обращение к функции является вполне допустимым:
ScrollWindowEx m_hWnd, 0, 5, ByVal 0&, ByVal 0&, 0&, ByVal 0&, _ SW_SCROLLCHILDREN Or SW_INVALIDATE
Однако в данном случае VB выдаст сообщение о том, что такое обращение (передача аргумента по значению) противоречит сделанному выше объявлению (передача по ссылке).
Чтобы решить указанную проблему, можно использовать два варианта. В первом нужно заменить в объявлении типы данных RECT на Any:
Public Declare Function ScrollWindowEx Lib _ "user32" (ByVal hwnd As Long, ByVal dx As Long, _ ByVal dy As Long, lprcScroll As Any, _ lprcClip As Any, ByVal hrgnUpdate As Long, _ lprcUpdate As Any, ByVal fuScroll As Long) As Long
С таким объявлением будет допустима передача как адреса структуры, так и числовой величины.
Однако, как мы уже неоднократно упоминали, использование типа Any повышает риск ошибки (фактически в данном случае отключается синтаксический контроль, вследствие чего вся ответственность за правильность обращения возлагается на программиста). Поэтому мы советуем воспользоваться возможностью альтернативного объявления функции, например следующим образом:
Public Declare Function ScrollWindowExLong Lib _ "user32" Alias ScrollWindowEx (ByVal hwnd As Long, ByVal dx As Long, _ ByVal dy As Long, ByVal lprcScroll As Long, _ ByVal lprcClip As Long, ByVal hrgnUpdate As Long, _ ByVal lprcUpdate As Long, ByVal fuScroll As Long) As Long
Соответственно при необходимости передачи нулевых параметров нужно использовать обращение именно к этой функции:
ScrollWindowExLong _ m_hWnd, 0, 5, ByVal 0&, ByVal 0&, 0&, ByVal 0&, _ SW_SCROLLCHILDREN Or SW_INVALIDATE
Совет 415. Как вставить колонки в VB MSFlexGrid
лемент управления MSFlexGrid предоставляет отличные возможности для визуализации данных в виде таблицы. Однако иногда необходимо, чтобы сам пользователь мог вставлять колонки в определенных точках таблицы, в то время как VB по умолчанию добавляет новые колонки в конец таблицы. Эта проблема легко решается с помощью обновленного свойства ColPosition, для чего достаточно написать такой код:
Private Sub Command1_Click() With MSFlexGrid1 ' добавить новую колонку .Cols = .Cols + 1 ' переместить в нужную позицию .ColPosition(.Cols - 1) = .Col End With Call SetHeaders ' переписать заголовки End Sub Private Sub SetHeaders() ' формирование заголовков Dim i As Integer With MSFlexGrid1 For i = 0 To .Cols - 1 .Col = i .Row = 0 .Text = "Колонка " & i Next i End With End Function
Совет 416. Динамическое увеличение динамического массива
сли вам нужно динамически увеличить верхнюю границу массива, то можно воспользоваться такой конструкцией:
Dim upper As Long ... upper = UBound(myArray) ' текущее значение верхней границы ReDim myArray(upper + 1) 'увеличение
Но если к моменту обращения к функции Ubound массив не был еще инициализирован (то есть был определен в виде Dim myArray()), то приведенный выше код вызовет ошибку. Чтобы избежать этого, используйте такой код:
Dim myArray() As String Private Sub cmdAdd_Click() Dim upper As Long On Error Resume Next ' включаем обработку ошибок upper = UBound(myArray) If Err.Number Then upper = 0 ReDim myArray(0) Else upper = upper + 1 ReDim Preserve myArray(upper) End If On Error GoTo 0 ' отключаем обработку ошибок myArray(upper) = CStr(txtName) ' ввод нового значения End Sub
Совет 417. Как работать с адресной книгой MS Exchange
аш читатель Виктор Крюков поделился своим опытом работы с адресной книгой MS Exchange. В организации, где он работает, почтой управляет MS Exchange 2000, поэтому результат обращения к свойству ContactItem.Email1Address выглядит примерно так:
"/o=UFG/ou=UFG/cn=Recipients/cn=VKryukov".
Как же получить реальный адрес? Виктор предлагает такой рецепт. Объект, который хранит информацию о пользователе, — это AddressEntry, находящийся в коллекции AddressEntries, которая, в свою очередь, находится в объекте AddressList коллекции AddressLists данной сессии. У этого объекта есть семейство Fields, которое и хранит нужные свойства. Видимо, для доступа эти свойства проиндексированы какими-нибудь именованными константами типа PR_ADDRESS_EMAIL и т.п., но мы их не нашли.
Вот возможные варианты решения этой задачи:
- делать цикл по всем полям и искать значение, содержащее '@' в качестве подстроки;
- найти значение, которое является массивом, — оно всего одно, представляет собой массив строк и содержит строчки примерно такого вида (этот вариант кажется более надежным):
SMTP:_servrice@ufg.com MS:UFG/UFG/Service CCMAIL: Service at UFG X400:c=RU;a= ;p=UFG;o=UFG;s=?service;
то есть это адреса в разных форматах. Из данного массива выбирается строчка, начинающаяся с 'SMTP', и обрезается начало, которое и является правильным ответом.
Вот код для примера (мы добавляем в коллекцию Result все имена и адреса), для выполнения которого требуется в References подключить Microsoft CDO 1.21 Library.
Private Sub Form_Load() Dim oSession As MAPI.Session Dim oAList As MAPI.AddressList Dim oAEntry As MAPI.AddressEntry Dim oValue As Variant Dim i As Integer, v As Variant, j As Integer Dim Result As New Collection ' Logon to the MAPI session Set oSession = New MAPI.Session oSession.Logon ' Get the Global Address List Set oFolder = oSession.AddressLists("Global Address List") For Each oEntry In oFolder.AddressEntries For i = 1 To oEntry.Fields.Count v = oEntry.Fields(i).Value If IsArray(v) Then For j = LBound(v) To UBound(v) If Mid(v(j), 1, 5) = "SMTP:" Then Result.Add Mid(v(j), 6) & " : " & oEntry.Name GoTo continue End If Next j End If Next i continue: Next oEntry For i = 1 To Result.Count MsgBox Result(i) Next i End Sub
Совет 418. Как отключить ADO Recordset, созданный с помощью объекта Command
огда вы создаете распределенное приложение, крайне важно, чтобы ваш код занимал как можно меньше ресурсов сервера. В этом случае узким местом является поддержка соединения с сервером баз данных. Конечно, выполнить отключение набора данных из стандартного объекта Recordset очень просто. А вот сделать это с набором данных, который был сгенерирован с помощью объекта Command, не так легко, хотя и возможно.
В этих целях создайте объект Command и установите его свойства, как вы это обычно делаете, но вместо обращения к свойству Execute используйте метод Open объекта Recordset с аргументом Source. Тогда вы отключите соединение обычным образом и удалите объект Command. Все это можно проиллюстрировать с помощью такого кода:
Dim cmd As ADODB.Command Dim rst As ADODB.Recordset Dim param As ADODB.Parameter Set cmd = CreateObject("ADODB.Command") With cmd .CommandText = "sp_myStoredProc" .CommandType = adCmdStoredProc .ActiveConnection = STR_CONN 'Какая-то строка соединения Set param = .CreateParameter("Param1", _ adVarChar, adParamInput, 2, "foo") .Parameters.Append param End With Set rst = CreateObject("ADODB.Recordset") With rst .CursorLocation = adUseClient .Open cmd, CursorType:=adOpenStatic, _ Options:=adCmdStoredProc Set .ActiveConnection = Nothing End With Set DataGrid1.DataSource = rst Set cmd = Nothing End Sub
Совет 419. Как использовать символ @ в именах параметров SQL Server
ак известно, ADO предлагает два варианта поддержки хранимых процедур со значениями параметров: в первом случае нужно сослаться на имя параметра напрямую и просто присвоить значение, а во втором следует построить объект Parameter и добавить его к коллекции Parameters объекта Command. Однако нужно иметь в виду, что, когда вы используете именованные параметры, следует применять символ @ перед именем каждого параметра SQL, например вот так:
cmd.Parameters("@someParam") = 250
Если же вы создаете объект Parameter, этот символ не используется:
Set param = cmd.CreateParameter _ ("someParam", adBigInt, adParamInput, 2, 250) cmd.Parameters.Append param
Совет 420. Генерация строк соединения для OLE DB
оздать строку соединения OLE DB без использования элемента управления DataEnvironment в VB-проекте не так-то просто: нужно в контекстном меню объекта Connection1 выделить команду Properies, а затем установить значение в диалоговом окне Data Link Properties, которое потом переписать в свойство ConnectionSource. Однако существует довольно легкое решение этой проблемы.
Создайте текстовый файл с расширением VBS (VBScript) с таким кодом:
Dim oDataLinks, sRetVal Set oDataLinks = CreateObject("DataLinks") On Error Resume Next 'отслеживание кнопки Cancel sRetVal = oDataLinks.PromptNew On Error Goto 0 ' отключение обработки ошибок If Not IsEmpty (sRetVal) Then ' не была нажата Cancel InputBox "Строка соединения OLE DB = ", sRetVal End If Set oDataLinks = Nothing
После двойного щелчка по имени этого файла начнет выполняться этот код, и вы сможете скопировать строку соединения из поля ввода.
Если же вы работаете с VB, то можно добавить ссылку на Microsoft OLE DB Service Component 1.0 Type Library (OLEDB32.DLL) и использовать Object Browser для обращения к объекту DataLinks.
Совет 421. Как создавать ISAM-файлы
помощью библиотеки ADOX 2.1 (Microsoft ADO Extensions for DLL and Security) можно легко создать новый ISAM-файл в самых различных форматах (Excel, dBase, Paradox, HTML и Lotus) без обращения к соответствующим объектным моделям и даже при отсутствии приложений, для которых эти форматы являются родными.
Приведем пример того, как можно создать XLS-файл с двумя рабочими листами:
Public Sub Main() Dim cn As Connection Dim cat As Catalog Dim tbl As Table Dim fld As Column Set cn = New Connection With cn ' установка соединения .ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Extended Properties=Excel 8.0;" & _ "Data Source=" & App.Path & "\newfile.xls" .Open End With Set cat = New Catalog With cat ' формирование таблиц .ActiveConnection = cn ' первая таблица (рабочий лист) Set tbl = New Table tbl.Name = "NewSheet1" Set fld = New Column fld.Name = "MyCol1" fld.Type = adWChar fld.DefinedSize = 30 tbl.Columns.Append fld ' вторая колонка tbl.Columns.Append "Column2", adInteger .Tables.Append tbl ' добавляем первую таблицу ' вторая таблица (рабочий лист) Set tbl = New Table tbl.Name = "NewSheet2" ' вторая колонка tbl.Columns.Append "МояКолонка1", adWChar, 20 .Tables.Append tbl ' добавляем вторую таблицу cat.Tables.Refresh ' запись End With ' очистка Set fld = Nothing Set tbl = Nothing Set cat = Nothing cn.Close Set cn = Nothing End Sub
Запустите эту процедуры, а потом с помощью Excel убедитесь, что вы получили рабочую книгу с двумя листами, в которых заданы соответственно две и одна колонки. Вы можете создавать файлы и других типов, просто установив в Extended Properties нужное значение: dBase IV, Paradox 4.x и пр.
Совет 422. Защита от макросов при использовании глобальных шаблонов
нам поступил вопрос: «Почему при работе в Word 2000 при использовании глобальных шаблонов, а также при загрузке шаблонов в виде автономных документов или присоединенного файла из каталога «Шаблоны пользователя» не производится проверка на наличие макрокода, хотя такой режим контроля у меня установлен?»
Отвечаем на это следующим образом. Управление режимом контроля за наличием макрокода при загрузке документов производится в диалогом окне Security[Безопасность] (команда меню Tools|Macro|Securiry [Сервис/Макрос/Безопасность]). На его вкладке Trusted Sources [Надежные источники] имеется флажок Trust all installed Add-ins and templates [Доверять всем установленным надстройкам и шаблонам]. Под установленными здесь подразумеваются дополнения и шаблоны, помещенные в каталог «Шаблоны пользователя» (конкретное имя этого каталога указывается в поле User Templates [Шаблоны пользователя] во вкладке File Locations [Расположение] диалогового окна Tools|Options [Сервис|Параметры]).
По умолчанию данный флажок установлен, поэтому все шаблоны из этого каталога не проверяются на наличие макрокода (предполагается, что вы помещаете туда файлы, в которых точно уверены). Если вы все же хотите выполнять такую проверку, то очистите флажок.
В Word 97 специального режима для загрузки шаблонов не было. Но в начальной версии программы иногда имела место ошибка, когда шаблоны загружались без проверки на макрокод. Этот дефект уже давно устранен — заплатку, которая решает данную проблему, можно скачать по адресу http://www.microsoft.com/rus/download/wd97sp.htm.
КомпьютерПресс 1'2002