11 - 2001

Советы тем, кто программирует на 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 и т.п., но мы их не нашли.

Вот возможные варианты решения этой задачи:

  1. делать цикл по всем полям и искать значение, содержащее '@' в качестве подстроки;
  2. найти значение, которое является массивом, — оно всего одно, представляет собой массив строк и содержит строчки примерно такого вида (этот вариант кажется более надежным):
 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