Visual2000 · Архив статей А.Колесова & О.Павловой

Советы тем, кто программирует на VB & VBA

Андрей Колесов, Ольга Павлова

© 2000, Андрей Колесов, Ольга Павлова
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" N 8/2000, CD-ROM.


Совет 305. Где хранятся настройки панелей инструментов MS Office

Как известно, пользователь MS Office 97/2000 может выполнять различные операции по настройке меню и панелей инструментов среды — модифицировать, удалять и добавлять команды и панели. Это можно делать как с помощью команды Настройка (Customize), так и программным образом (VBA). Но где хранятся эти настройки, как они связаны с обрабатываемыми документами? Это полезно знать, особенно если вы хотите перенести эти настройки на другой компьютер. Тем более что реализация механизма сохранения настроек для всех офисных приложений различная (это к вопросу о том, что MS Office представляет собой как бы единое семейство продуктов).

Word

Это приложение обладает наиболее удобной и гибкой системой настроек. Как известно, VBA-проекты хранятся в документах или шаблонах Word. В них же хранятся и настройки панелей инструментов, и пользователь может сам выбирать, в каком именно файле они будут записаны.

Таким образом можно реализовать самые разнообразные варианты подключения расширений пользовательской среды. Если записать эти настройки в глобальный шаблон (например, в Normal.dot), то данные команды будут доступны при работе со всеми документами в данной сессии. При сохранении установок в конкретный документ они будут применяться только в нем. Если же настройки включены в присоединенный шаблон, они будут появляться во всех документах, которые его используют.

Excel и PowerPoint

В этих двух приложениях макрокод хранится в документах (рабочих книгах и презентациях), а сами настройки панелей инструментов — в фиксированных файлах. Для версии 97 это соответственно файлы <username>.xlb и <username>.pcb в каталоге Windows, для версии 2000 — файлы Excel\Excel.xlb и PowerPoint\ppt.pcb, которые находятся в каталоге Windows\Application Data\Microsoft\. Таким образом, получается, что пользователь может работать только с одним фиксированным вариантом настроек панелей инструментов.

Но в этих приложения механизм работы команд несколько различается. Для примера напишите такую макрокоманду:

Sub Hello
    MsgBox "Hello!"
End Sub

которую запишите в книгу Hello.xls и презентацию Hello.ppt.

Соответственно в Excel и PowerPoint сделайте кнопку на панели инструментов для обращения к этим макрокомандам. Затем перезагрузите Excel: на панели инструментов будет видна ваша кнопка, хотя программный код для нее не загружен. Нажмите кнопку — произойдет загрузка Hello.xls и выполнится макрокоманда. Теперь повторите те же действия для PowerPoint — макрокоманда выполнится, но презентация не загрузится в среду приложения.

В Excel можно скопировать созданные пользователем панели инструментов в рабочую книгу (со встроенными это проделать нельзя!). Это делается с помощью диалогового окна Attach Toolbars (вызывается командой Attach на вкладке ToolBars окна Customize) — программным образом с применением VBA это сделать нельзя.

После копирования можно удалить пользовательские панели из среды самого Excel с помощью команды Delete окна Customize или метода Delete коллекции CommandBars. Удалить панель из рабочей книги можно только с помощью диалогового окна.

Важное замечание. Когда вы открываете рабочую книгу, производится автоматическое копирование пользовательских панелей из книги в среду Excel (но только в том случае, если панелей с такими именами не было в самом приложении!). При этом следует иметь в виду, что при закрытии рабочей книги эти панели остаются в Excel. Поэтому, если вы хотите, чтобы подобные компоненты появлялись только в сеансе работы с данной рабочей книгой, вы можете программным образом сделать удаление панели при закрытии книги. Если же вы не удалите пользовательскую панель из Excel и будете корректировать ее, то в последующих сеансах вы будете работать именно с ней (копирование из рабочей книги не будет выполняться).

Outlook и FrontPage

В этих двух приложениях все сделано еще проще: программный код хранится в строго фиксированных проектах — Outlook\VbaProject.OTM и FrontPage\Macros\Microsoft FrontPage.fpm, а настройки среды — в столь же фиксированных Outlook\outcmd.dat и FrontPage\State\cmpUI.prt. Все это, в свою очередь, находится в подкаталоге Windows\Application Data\Microsoft\.

Примечание. Для приложений Excel, PowerPoint, Outlook и FrontPage каталог Windows\Application Data\Microsoft\ используется только в случае однопользовательского режима. Если же в операционной системе установлен многопользовательский режим с использованием различных профилей, то в этом случае используется каталог Windows\Profiles\Username\Application Data\Microsoft\.

Access

В документах Microsoft пользовательские настройки встроенных панелей инструментов среды Access хранятся в "специальной базе данных". Наши исследования показали, что эта база — Реестр Windows, хранящийся по адресу: HKCU\Software\Microsoft\Office\9.0\Access\Settings\Commandbars\. Но что и как туда пишется — нам разобраться не удалось. Панели же инструментов, создаваемые с помощью Access, хранятся в файлах этой базы данных.

В начало статьи

Совет 306. Управление порядком перемещения по элементам управления

Как известно, перемещение фокуса от одного элемента управления к другому можно выполнять с помощью клавиатуры: Tab (вперед) или Shift+Tab (назад). Порядок передвижения определяется значением свойства TabIndex элементов управления, который начинается с нуля и для каждого размещаемого на форме элемента управления увеличивается на единицу. Свойство TabIndex, конечно же, имеется только у видимых компонентов (у Timer его нет). Пассивные визуальные компоненты типа Label имеют свойство TabIndex, но фактически не используют его — при перемещении по форме такие элементы автоматически пропускаются.

Для изменения последовательности обхода визуальных компонентов (она редко совпадает с тем порядком, в котором мы создавали диалоговый интерфейс) нужно менять свойства TabIndex. Это можно делать вручную, напрямую корректируя параметры в окне Properties. Обратите внимание, что ошибка использования одного и того же значения TabOrder для разных компонентов здесь исключена за счет автоматической перенумерации.

Пусть, например, у вас имеются четыре компонента с номерами 0, 1, 2, 3. Для последнего вы решили установить TabOrder=1. В этом случае автоматически изменятся значения свойств TabOrder для двух элементов управления: 1->2, 2->3.

Однако гораздо удобнее использовать специальные визуальные средства управления перемещения по элементам формы. Как мы отмечали в совете 301, конструктор MS Forms 2.0 (в среде VB и VBA) для этого использует специальную встроенную команду TabOrder. Чтобы воспользоваться ею, щелкните правой кнопкой мыши форму и в появившемся контекстом меню выберите команду Tab Order. В появившемся одноименном диалоговом окне выделите нужные компоненты и с помощью кнопок Move Up и Move Down передвиньте их в нужную позицию списка, который определяет порядок перемещения (рис. 306-1).

Рис. 306-1

Для обычных VB-форм такой встроенной команды нет, и по этому поводу мы уже выказывали свое недовольство в адрес разработчиков из Microsoft. Однако мы были не правы — такие визуальные средства имеются в VB 5 и VB 6 в виде автономного дополнения TabOrder (исходный код этого проекта находится в подкаталоге ...\MSDN98\98VS\1033\Samples\VB98\TabOrder\ для VB6 или в ...\Samples\CompTool\AddIns\TabOrder\).

Чтобы сделать Add-In (на примере VB 6.0), загрузите проект TabOrder.VBP и выполните команду File|Make TabOrder.DLL. В результате вы не только создадите исполняемый модуль, но и зарегистрируете новое дополнение, которое появится под названием Tab Order Sample Addin в окне Add-Ins|Add-In Manager среды Visual Basic. Для работы с TabOrder необходимо обычным образом установить режим загрузки в этом окне, и тогда на панели инструментов VB появится кнопка TabOrder Window для запуска этого дополнения (рис. 306-2).

Рис. 306-2

Примечание. При работе в VB 5 для включения TabOrder (созданного в этой же среде!) в окно Add-In Manager необходимо добавить в секцию [Add-Ins32] файла VBADDIN.INI строку TabOrder.Connect=0.

Теперь создайте в VB какой-нибудь тестовый пример с формой, на которой разместите несколько разных элементов управления, и щелкните кнопку TabOrder Window. В появившемся окне (рис. 306-3) можно корректировать последовательность перемещения по элементам управления формы.

Рис. 306-3

Однако здесь имеются дополнительные функции, которых нет в аналогичной утилите MS Forms 2.0. Обратите внимание, что на нашей тестовой форме все элементы управления расположены на разных расстояниях от верхней и правой границ формы. С помощью соответствующих кнопок на панели инструментов можно упорядочить список по вертикальному (рис. 306-4) или горизонтальному (рис. 306-5) расположению визуальных компонентов. Кроме того, программист без перезагрузки этой утилиты может восстановить исходную последовательность списка.

Рис. 306-4

Рис. 306-5

Вообще, мы рекомендуем внимательно изучить проект TabOrder, так как он представляет собой интересный пример создания Add-In, использования User-документов и ресурсных файлов. Но один вопрос все же остается — зачем нужно было делать для VB-форм и MS Forms 2.0 две похожие, но тем не менее разные утилиты?

В начало статьи

Совет 307. Как удалить каталог

VB содержит довольно много встроенных функций и операторов для управления файлами и каталогов. Совершенно очевидно, что эти средства работают через Win API, и понятно, что последние обладают более широкими возможностями. Чаще всего встроенные функции VB реализуют усеченный вариант соответствующей API-функции (например, оператор MkDir представляет собой частный случай функции CreatDirectory). Многие же возможности API вообще не представлены в виде VB-операторов. В частности, это относится к операции удаления каталогов, которая может быть выполнена таким образом:

Private Declare Function RemoveDirectory& Lib "kernel32" Alias  _
     "RemoveDirectoryA" (ByVal lpPathName As String)
    
  ' Удаление каталога (пустого!)
  PathName$ = "D:\t\"    ' в конце имени "\" необязательна
  '
  code& = RemoveDirectory(PathName)
  If code& = 0 Then
      ' операция удаления не была выполнена
  Else
      ' каталог удален
  End If

Здесь нужно обратить внимание на несколько моментов:

  1. Удаляемый каталог должен быть пустым.
  2. Имя каталога не обязательно должно заканчиваться на "\" — операция будет успешно выполнена с именем "D:\t".
  3. Почему-то при наличии ошибки обращение к функции GetLastError выдает исключительно 0 (то есть нельзя уточнить характер ошибки).

В начало статьи

Совет 308. Как удалить непустой каталог

Но, как удалить каталог, если в нем находятся файлы и вложенные подкаталоги? Для этого потребуется написать процедуру DeleteDir, которая будет перед удалением каталога просматривать и удалять его содержимое. Обращение к ней может выглядеть следующим образом:

Dim DirCount%, FileCount% ' число удаленных каталогов и файлов
DirName$ = "d:\t\"  ' удаляемый каталог
DirCount = 0: FileCount = 0    ' начальная установка
Result& = DeleteDir&(DirName$, DirCount%, FileCount%)
' Result = 0 - удален

А сама процедура будет содержать такой код:

Public Function DeleteDir&(DirName$, DirCount%, FileCount%)
  '
  ' Удаление каталога со всем его внутренним содержанием

  ' ВХОД:
  '     DirName$ — имя удаляемого каталога
  ' ВЫХОД:
  '    DeleteDir - код завершения (0 - удален, <>0 - не удален
  '    DirCount, FileCount — текущее число удаленных каталогов и файлов
  '    (перед первым обращении эти переменные должны быть обнулены)
  '
  Dim Result&
  '
  ' 1. Удалить все внутри каталога
  Call DeleteDirAll(DirName$, DirCount%, FileCount%, Result&)
  If Result = 0 Then ' без ошибок
    ' 2. удалить сам каталог
    Result = RemoveDirectory(DirName$)
    If Result = 0 Then
      Result = -1
    Else
      Result = 0: DirCount = DirCount + 1
    End If
  End If
  DeleteDir = Result ' код завершения
End Function

Но очевидно, что ключевой задачей здесь является написание процедуры DeleteDirAll, которая будет удалять внутреннее содержание каталога — файлы и подкаталоги. Тут не обойтись без использования рекурсии, и поэтому полезно вспомнить наши советы 229-231. Более того, нам понадобится готовый код подпрограммы CurrentDirCounter ("как есть"), а также продпрограмма FileNameCase, которую можно использовать в качестве заготовки и создать DeleteDirAll:

Public Sub DeleteDirAll(DirName$, DirCount%, FileCount%, Result&)
  '
  ' Удаление всех файлов и подкаталогов заданного каталога
  '
  ReDim arrFile$(100) ' для имен элементов оглавления
  Dim CountFile%, NewPathName$, i%
  '
  On Error GoTo ErrorDeleteFile
  ' формируем список файлов каталога
  Call CurrentDirCounter(DirName$, "*.*", CountFile%, arrFile$(), 0)
    ' удаление файлов
  If CountFile > 0 Then    ' есть файлы в подкаталоге
    For i = 1 To CountFile
      Kill DirName$ & arrFile$(i) ' удалить файл
      FileCount = FileCount + 1 ' счетчик удаленных файлов
    Next
  End If
  'формирование списка подкаталогов
  Call CurrentDirCounter(DirName$, "", CountFile%, arrFile$(), vbDirectory)
  If CountFile > 0 Then    ' есть подкаталоги
    For i = 1 To CountFile
      ' формирование имени вложенного подкаталога
      NewPathName$ = DirName$ + arrFile$(i) + "\"
      '!! рекурсивное обращение к функции удаления каталога
      ' с новым именем, но старыми значениями счетчиков
      Result = DeleteDir&(NewPathName$, DirCount%, FileCount%)
      If Result <> 0 Then Exit Sub ' была ошибка
    Next
  End If
Exit Sub
ErrorDeleteFile:
  Result = Err ' ошибка при удалении файла
End Sub

В начало статьи