В Visio 2002 встроена так называемая технология Microsoft Authenticode, которая дает возможность разработчикам создавать цифровые подписи для проектов VBA и применять цифровые сертификаты, подтверждающие надежность источника. В случае, если подписанный проект был изменен, пользователь может отключить макросы в документе Visio.
Новый формат файла XML расширяет саму объектную модель Visio, VBA-расширения, а также поддерживает так называемые СОМ-настрой-ки. Кроме этого, XML-формат взаимодействует с XML-приложениями, что облегчает обмен информацией посредством созданных диаграмм, включая данные, не связанные с самим образом. Объект может моделироваться с использованием около 90 новых свойств в автоматическом режиме и обеспечивать доступ к большому количеству Visio-диаграмм.
Через СОМ-поддержку настроек вы можете усовершенствовать программируемый код в Visio через Component Object Model (COM), при этом цифровые сигнатуры для VBA проектируются посредством Microsoft Authenticode, а сама технология позволяет в цифровой форме подписывать VBA-проекты в их решениях.
Любой документ или файл Microsoft Visio содержит проект, к которому вы можете добавить модули и формы в зависимости от того, какое решение вам требуется.
Любой документ или файл Microsoft Visio 2002 содержит проект, к которому вы можете добавить модули и формы в зависимости от того, какое решение вам требуется, при этом в минимальной конфигурации каждый проект содержит модуль класса ThisDocument. Этот модуль класса изображает свойства, методы и события определенного документа, связанного с вашим проектом.
Окно обозревателя проекта Project Explorer отображает список проектов и объекты в документах Microsoft Visio.
Окно свойств Properties отображает список свойств для выбранного помета.
Рабочая область программирования показывает все модули, модули классов и пользовательские формы, открытые во время разработки. Вы строите вашу программу в этой области.
Поле меню отображает команды, которые вы строите, запускаете и переустанавливаете в вашей программе.
Инструментальная панель дает быстрый доступ к наиболее используемым командам в среде разработки.
В Окне кода вы можете писать, изображать и редактировать код в новых процедурах или в существующих процедурах событий. Вам можете открывать любое количество Окон кода для различных модулей, модулей классов или пользовательских форм, поэтому вы с легкостью можете анализировать код, а также копировать и вставлять его из окон в окна.
Вы можете запустить редактор Visual Basic, даже не открывая документ Microsoft Visio, но сначала вы должны открыть документ, чтобы увидеть проект VBA для данного документа. Пример Microsoft Visio добавляет проекты только в те документы, которые открыты в виде Original или Сору. Однако, если документ уже имеет проект, вы можете посмотреть проект для документа, который открыт в виде Read Only.
Как запустить в действие редактор Visual Basic
1. Запустите приложение Microsoft Visio, откройте эскиз, трафарет или чертеж в виде Original или Сору, но не в виде Read Only.
2. Выберите Tools -> Macros -> Visual Basic Editor.
Вы можете выбрать вашу рабочую среду в VBA, настроив такие параметры, как размер шрифта, цвет кода, параметры ошибок в синтаксисе и условия объявления переменных.
Выберите Tools -> Options, нажмите на Редактор (Editor) или Редактор формата (Editor Format) и настройте необходимые параметры.
По умолчанию, проекты должны иметь то же имя, что и документ, с которым они связаны. Однако каждый проект имеет окно Свойства проекта (Project Properties), где вы можете переименовать ваш проект и предоставить дополнительную информацию (к примеру, описание проекта). Кроме того, вы можете вообще заблокировать ваш проект.
Как открыть окно Project Properties
Выберите Tools -> Project Name -> Properties и настройте свойства проекта или нажмите правой кнопкой на имени проекта в окне обозревателя проекта Project Explorer.
Навигация по проекту
Для навигации по проектам в редакторе Visual Basic воспользуйтесь окном Project Explorer. В этом окне перечислены модули, модули классов и пользовательские формы для проектов во всех открытых файлах Microsoft Visio. Вы можете дважды щелкнуть на любом модуле, модуле класса или пользовательской форме в Project Explorer и открыть его окно кода.
Сохранение проекта
Проект VBA сохраняется в самом документе Microsoft Visio. Документ Microsoft Visio может быть сохранен как:
Когда пользователь создает новый документ Microsoft Visio из эскиза, программа копирует проект VBA и его предметы в новый документ.
Как сохранить документ Microsoft Visio и его проект VBA
Выберите File -> Save или в Visual Basic Editor выберите File -> Save File Name.
Имя файла и местоположение документа изображаются в скобках после названия проекта в окне Project Explorer для VBA.
Проект Microsoft Visual Basic for Applications (VBA) состоит из модулей, модулей класса и пользовательских форм.
Модулем является набор объявлений, следующих за процедурами — список инструкций, которые выполняет программа.
Модуль класса определяет объект, его свойства и его методы. Модуль класса работает как эскиз, из которого по ходу выполнения программы создается пример объекта. Любой проект VBA, основанный на пакете Microsoft Visio содержит в себе модуль класса ThisDocument. Этот модуль класса изображает свойства, методы и события документа Microsoft Visio, который содержит данный проект VBA.
Пользовательская форма содержит органы управления пользовательским интерфейсом (к примеру, кнопки команд и текстовые окна). Простой проект может требовать одну пользовательскую форму или модуль, хотя более сложные проекты содержат множество модулей, модулей класса и пользовательских форм. Например, решение, которое генерирует чертеж, может включать пользовательскую форму для сбора информации от пользователя. Кроме того, решение может включать код, организованный в одном или более модулей и/или модулей класса, которые:
В случае, если решение загружает в ответ на событие (к примеру, открытие документа), то решение может включать в себя код, который запускается в действие, когда происходит указанное событие. Вы можете добавлять существующие модули, модули классов или пользовательские формы к вашему проекту, просто импортируя файлы или экспортировать предметы проекта для использования в других проектах.
Вставка модулей в проект
Многие программы VBA содержат один или более модулей — набор объявлений, следуемых за процедурами. Каждый проект VBA для Microsoft Visio содержит модуль класса ThisDocument, который является объектом, изображающим документ проекта. Вы можете создать дополнительные модули класса, чтобы определить в вашем проекты выбранные объекты VBA.
Окно кода изображает пустой модуль, куда вы можете вставить процедуры, создающие эскизы, в которые вы вводите код VBA.
Как добавить процедуры к модулям и модулям класса
1. Выберите Insert->Procedure, чтобы открыть диалоговое окно Add Procedure.
2. В поле Name дайте имя процедуре. Имя процедуры появится в подменю ее модуля в меню Microsoft Visio Macros. Имя процедуры не может включать в себя пробелы или зарезервированные слова, к примеру, MsgBox, If или Loop, которые VBA использует в качестве операторов своего программного языка.
3. В поле Туре укажите тип процедуры: Sub, Function или Property. Модули или модули класса могут содержать более одного типа процедур.
Для того, чтобы написать процедуру, которая не требует аргументов, вставьте процедуру Sub.
Для того, чтобы написать функцию, которая требует аргументов и выдает значение, вставьте процедуру Function.
Для того, чтобы добавить свойства в модуль класса, вставьте процедуру Property.
4. В поле Scope (Предел действия) укажите Public или Private. Scope является мерой, определяющей, сколько раз процедура может быть принята другими модулями и программами.
Процедура с личным private-пределом может быть принята только модулем, который содержит ее — только процедура внутри того же самого модуля может называться личной процедурой, и личная процедура не может появляться в каком-либо меню или в каких-либо диалоговых окнах.
Процедура с общедоступным public- пределом может быть принята другими программами и модулями.
Приложение Microsoft Visio изображает в меню макросов Macros общедоступные процедуры модулей и модуль класса ThisDocument, который вообще не требует аргументов.
5. Для того, чтобы объявить все локальные переменные в виде стационарных, укажите параметр All Local Variables As Statics. Вы можете объявить переменные в вашей процедуре в виде локальных или стационарных (общих).
Стационарные переменные существуют весь срок вашей программы. Локальные переменные существуют только тогда, когда запущена в действие процедура, в которой они объявляются. В случае, если данная процедура выполняется еще раз, то все локальные переменные назначаются заново. Однако вы можете сохранить значение всех локальных переменных в процедуре на срок вашей программы, сделав их стационарными и, следовательно, зафиксировав их значение.
6. Нажмите ОК.
VBA вставляет эскиз процедуры в окно кода, в котором вы можете вводить код. Эскиз содержит первую и последнюю строки кода для того типа процедуры, которую вы вставили.
Вставка пользовательских форм в ваш проект
В случае, если вы хотите, чтобы ваша программа запрашивала у пользователя какую-либо информацию, то вы можете построить пользовательский интерфейс посредством вставки пользовательских форм. Пользовательская форма содержит регулировки пользовательского интерфейса (к примеру, командные кнопки и текстовые окна). Когда вы добавляете пользовательскую форму к вашему проекту, VBA автоматически открывает Инструментальную панель регулировок (Controls Toolbox). Регулировка — это объект, помещаемый вами на пользовательскую форму, которая имеет собственные свойства и методы. Кроме того, регулировка запускает события, на которые вы можете реагировать. Вы можете использовать регулировки для получения входных данных от пользователя, для демонстрации выходных данных и для запуска процедур события.
Как добавить пользовательскую форму к вашему проекту
1. Выберите Insert ->UserForm.
2. Укажите регулировки, которые вы хотите добавить к пользовательской форме из Controls Toolbox и перетащите их на пользовательскую форму.
3. Дважды нажмите на пользовательской форме или регулировке, чтобы изобразить ее кодовое окно.
4. Выберите событие, на которое вы хотите отреагировать из выпадающего списка событий и процедур в кодовом окне. Затем вы можете начать печатать ваш код. Или вставьте процедуру и начните печатать ваш код уже в эскизе процедуры.
Импорт файлов в ваш проект и экспорт файлов из вашего проекта
Для того, чтобы импортировать объект в ваш проект, выберите File -> Import File. Вы можете выбрать любой модуль VBA (файлы с расширением .bas), пользовательскую форму (файлы с расширением .frm) или модуль класса (файлы с расширением .els).
Для того, чтобы экспортировать объект из вашего проекта и сделать его доступным для импорта в другие проекты, укажите в окне Project Explorer объект, который вы хотите экспортировать, выберите File -> Export File и укажите место, где вы хотите сохранить данный файл. При экспорте объект не удаляется из вашего проекта.
Кроме того, вы можете «перетаскивать» проекты или объекты проекта из файла в файл пакета Microsoft Visio, указывая в окне Project Explorer тот проект или объекта проекта, который вы хотите передвинуть. Объект проекта автоматически сохраняется в папке проекта. Проект имеет ссылку в папке References, так как файл Microsoft Visio может содержать только один проект, но этот проект может ссылаться на другие проекты.
Важно: Вы не можете перетаскивать модуль класса ThisDocument из файла в файл пакета Microsoft Visio, но вы можете перетаскивать или копировать и вставлять код из ThisDocument в другие предметы проекта.
Использование библиотек
Библиотека Microsoft Visio содержит описания режима Автоматизации для объектов, свойств, методов и событий, которые задействует «движок» Microsoft Visio. Проекты Microsoft Basic for Applications (VBA), принадлежащие документам Microsoft Visio, автоматически ссылаются на библиотеку Microsoft Visio, которую вы используете для определения объектных типов Microsoft Visio в вашей программе. Использование объектных типов Microsoft Visio, объявленных в библиотеке Microsoft Visio, увеличивает скорость вашей программы, так как VBA интерпретирует объекты Microsoft Visio в расчетное время, а не во время действия.
Когда вы составляете программу, VBA проверяет синтаксис, находит ошибки программирования и определяет соответствие объектных типов типовым библиотекам. В случае, если вы используете тип общих переменных (к примеру, Object), то VBA не интерпретирует их во время запуска программы; в это время VBA запрашивает «движок» Microsoft Visio об объектных ссылках. Этот дополнительный опрос уменьшает скорость вашей программы.
Типовая библиотека также содержит общие символические постоянные, определенные для аргументов и поставляющие значения свойств и методов. Поскольку большинство аргументов для свойств и методов являются цифровыми значениями, использование констант может облегчить написание и чтение кода. Предположим, вы хотите узнать, какой тип окна изображает объект Окно (Window). Свойство Туре объекта Window дает целое число, указывающее на тип окна. Вы можете посмотреть на содержание типовой библиотеки Microsoft Visio, используя Object Browser.
Использование обозревателя объектов
Вы можете использовать Visual Basic Object Browser, чтобы посмотреть типовую библиотеку Обозреватель отображает константы, классы (объекты) и члены класса (свойства, методы и события) типовых библиотек, на которые ссылаются открытые проекты. Обозреватель также отображает свойства, методы, события и константы Microsoft Visio в виде членов в списке Members Of. Поле Подробности (Details) отображает синтаксис каждого члена в виде эскиза кода, который вы можете скопировать и вставить или перетащить в модуль. Затем вы можете подставить туда собственные переменные и аргументы. Использование кодового эскиза уменьшает вероятность ошибок при выводе на печать.
Как использовать Object Browser
1. Выберите View ->Object Browser.
2. Для того, чтобы просмотреть или найти объект, свойство, метод, событие или константу Microsoft Visio, введите ее имя в текстовом поле Search или нажмите по любому члену в списке Members Of.
Настройка ссылок на типовые библиотеки
Приложения, поддерживающие Автоматизацию, поставляют типовую библиотеку для описания объектов. В случае, если вы хотите получить доступ к объектам другого приложения из вашего решения Microsoft Visio, то выберите Tools ->References и укажите типовую библиотеку, которую вы ищите в списке доступных ссылок Available References. Кроме того, вы можете использовать эту процедуру, чтобы настроить ссылку на приложение Microsoft Visio из любого другого приложения, которое поддерживает Автоматизацию. Например, вы можете настроить ссылку на приложение Microsoft Visio из приложения Microsoft Word и далее использовать объекты Microsoft Visio в вашем приложении Word.
Любая типовая библиотека, отмеченная в списке доступных ссылок Available References появляется в окне Project/Library вашего проекта. Вы можете настроить ссылку на любой открытый документ Microsoft Visio или просмотреть любой неоткрытый документ Microsoft Visio в диалоговом окне ссылок References.
Важно: Для того, чтобы просмотреть только класс и члены в типовой библиотеке Microsoft Visio, выберите в окне Project/Library поле Microsoft Visio.
Использование объектных типов Microsoft Visio
Вы можете использовать преимущества типовой библиотеки Microsoft Visio и писать более эффективный код. При использовании объектных типов Microsoft Visio, объявленных в типовой библиотеке Microsoft Visio, вы можете объявить переменные в виде особых типов (к примеру, Microsoft Visio.Page):
Dim pagObj as Microsoft Visio.Page
Использование объектного типа Microsoft Visio (к Примеру, Microsoft Visio.Page) позволяет вашей программе указывать тип объекта, на который она ссылается в типовой библиотеке Microsoft Visio в расчетное время. Это называется ранним сращиванием. Этот пример использует Microsoft Visio, чтобы информировать программу о том, что она ссылается на объектные типы Microsoft Visio в типовой библиотеке Microsoft Visio, и этот пример использует Page, чтобы информировать программу том, что переменная pagObj является объектом Page. Вот несколько обычных объектных типов:
Dim docsObj As Microsoft Visio.Documents'A Documents collection
Dim docObj As Microsoft Visio.Document 'A Document object
Dim shpsObj As Microsoft Visio.Shapes 'A Shapes collection
Dim shpObj As Microsoft Visio Shape 'A Shape object
Когда вы печатаете пропуск после объекта или библиотечного типа, список доступных объектных типов, свойств и методов для предшествующего типа объекта или переменной появляются автоматически. Для того, чтобы вставить в ваш код объектный тип из списка, дважды нажмите на типе в списке.
Важно; В случае, если вы при работе с кодовым окном не видите окна Auto List, то выберите Tools -> Options ->Editor и укажите параметр Auto List Members.
В этом примере соответствующим объектным типом является Page.
Использование всеобщих (global) объектов и объектов ThisDocument
В отличие от самостоятельной программы, которая нуждается в приобретении ссылки на объект Приложение (Application) Microsoft Visio с помощью создания или получения его, код в проекте Microsoft Visual Basic for Applications (VBA) выполняется при запуске примера Microsoft Visio, поэтому вам не нужно приобретать ссылку на объект Application. Инструментальный набор Microsoft Visio поставляет global-объект, который ссылается на пример Microsoft Visio. Движок Microsoft Visio также поставляет объект ThisDocument, который изображает документ Microsoft Visio, связанный с вашим проектом.
Использование всеобщего объекта Microsoft Visio
Всеобщий объект изображает пример и предоставляет более прямой доступ к некоторым свойствам.
Свойства всеобщего объекта Microsoft Visio не имеют приставки (префикса) с ссылкой на объект.
Всеобщий объект Microsoft Visio и его свойства
Объект Application является свойством всеобщего объекта Microsoft Visio, поэтому вы можете получить доступ к любому свойству объекта Application через прямую ссылку на свойство Application всеобщего объекта Microsoft Visio. Перед вами три примера кода, с помощью которого приобретается первый документ в наборе Documents — все три используют разный синтаксис. Пример! создает объект Application. Этот код обычно используется при написании внешней программы:
Dim appMicrosoft Visio As Microsoft Visio.Application
Dim docsObj As Microsoft Visio.Documents
Dim docObj As Microsoft Visio,Document
Set appMicrosoft Visio = CreateObject(«visio.application»)
Set docsObj = appMicrosoft Visio.Documents
Set docObj = docsObj.Item(1)
Application
ActiveDocument
Document
Documents
Window
Windows
ActivePage
ActiveWindow
Addon
Addons
VBE
Documents
Пример 2 использует свойство Application всеобщего объекта Microsoft Visio:
Dim docsObj As Microsoft Visio.Documents
Dim docObj As Microsoft Visio.Document
Set docsObj = Application.Documents
Set docObj = docsObj.Item(1)
Пример 3 имеет непосредственный доступ к свойству Documents всеобщего объекта Microsoft Visio:
Dim docObj As Microsoft Visio.Document
Set docObj = Documents.Item(1)
Обратите внимание, что в примерах 2 и 3 Application и Documents не предшествует объект. Когда вы ссылаетесь на любое свойство или метод всеобщего объекта Microsoft Visio, вам не нужно объявлять переменную для всеобщего объекта или ссылаться на него, как на предшествующий объект свойства — всеобщий объект это уже подразумевает.
Третий пример имеет более прямой метод доступа к набору Documents из проекта VBA.
Предлагаем вам пару примеров кода для наиболее используемых свойств всеобщего объекта Microsoft Visio:
Set docObj = ActiveDocument
Set pagObj = ActivePage
Set winObj = ActiveWindow
Важно: Всеобщий объект Microsoft Visio доступен только при написании кода в проекте VBA для документа Microsoft Visio.
Использование объекта ThisDocument
Любой проект VBA в вашем приложении Microsoft Visio содержит модуль класса для режима умолчания, называемый ThisDocument.
Этот модуль класса изображает свойства, методы и события документа, связанного с проектом. Как и в случае с любым модулем класса, другие программы могут иметь доступ к ThisDocument во время запуска программы.
Документы и связанные объекты более высокого порядка
В случае, если вы хотите манипулировать документом, но документ не должен быть связан с вашим проектом VBA, то приобретите объект Document из набора Documents. В случае, если вы хотите манипулировать документом, связанным с вашим проектом VBA, то используйте объект ThisDocument. Например, чтобы сослаться на первую страницу чертежа Hello.vsd, вы должны получить объект Document из набора Documents всеобщего объекта. Следующий пример получает первую страницу Hello.vsd из набора Pages документа.
Set docObj = Documents.Item(«hello.vsd»)
Set pagObj = docObj.Pages.Item(1)
В случае, если Hello.vsd является документом, связанным с вашим проектом VBA, то вы можете просто использовать объект ThisDocument, как это показано на следующем примере:
Set pagObj = ThisDocument.Pages.Itera(1)
При ссылке на объект Document вы возвращаете себе другие объекты Microsoft Visio с помощью приобретения свойств объекта Document и далее из других объектов в объектной иерархии. Вы можете добавить свойства и методы к объекту ThisDocument, так как он является растяжимым объектом, то есть объектом, чью функциональность вы можете расширить. Объект ThisDocument является единственным растяжимым объектом, который поставляется инструментальным набором Microsoft Visio.
Вы можете указать ThisDocument в окне Project Explorer и изменить его свойства (к примеру, страничные настройки и стиль в режиме по умолчанию); кроме того, в окне Properties вы можете изменить свойства документа.
Запуск кода VBA из приложения Microsoft Visio
Вы можете запустить в действие ваш код Microsoft Visual Basic for Applications (VBA) в программе Visual Basic Editor, чтобы проверить и исправить ее в ходе разработки. Пользователь может запустить в действие ваш законченный макрос в пользовательском интерфейсе Microsoft Visio, выбрав его из подменю Macros в меню Инструменты (Tools). Макрос — это процедура VBA, которая не требует аргументов. Процедуры, требующие аргументов, не появляются в подменю Macros.
Кроме того, программа может запускаться в действие в ответ на события или другими способами, которые вы разработали для нее.
Как запустить в действие макрос из редактора Visual Basic
1. Выберите Tools -> Macros.
2. В списке Macros укажите нужный вам макрос и нажмите на Run. В случае, если нужный вам макрос отсутствует в списке, то убедитесь, что вы выбрали правильный проект, модуль или чертеж в окне Macros In. Личные процедуры не появляются ни в каких-либо меню, ни в каких-либо диалоговых окнах.
Другой способ:
1. В окне Project Explorer откройте модуль, который содержит макрос.
2. В окне кода нажмите на месте вставки макроса.
3. Выберите Run ->Run Sub/UserForm.
Макрос, содержащий точку вставки, запустится в действие.
Как запустить макрос из диалогового окна Microsoft Visio Macros
1. В приложении Microsoft Visio выберите Tools -> Macros.
2. В списке Macros укажите вашу программу и нажмите на Run.
Важно: С точки зрения пользователя неважно, если программа, запускаемая пользователем, является дополнением или макросом, поэтому приложение Microsoft Visio объединяет эти программы в диалоговых окнах. Например, вы можете запустить в действие дополнение или макрос из диалогового окна Macros или из меню Macros.
Как «снабдить» описанием ваш макрос, который появляется в диалоговом окне Macros
1. В Visual Basic Editor откройте Object Browser.
2. Выберите проект, который содержит макрос в окне Project/Library.
3. В списке Class укажите модуль, который содержит макрос, и нажмите правой кнопкой на макросе в списке Members Of, а затем выберите Properties.
4. Введите описание в поле Description.
Как запустить в действие ваш макрос из меню Microsoft Visio Macros
1. Выберите Tools -> Macros.
2. В меню Macros выберите проект, который содержит ваши макросы, и выберите нужный макрос.
В случае, если вы хотите, чтобы ваши макросы появлялись в меню Macros, а не в модуле, который содержит ваши макросы, дайте имя модулю так: ShowInMenu. Модуль ShowInMenu не появляется в меню Microsoft Visio Macros, но его макросы появляются.
В случае, если во время выполнения программы происходит какая-то ошибка, то Microsoft Visual Basic for Applications (VBA) генерирует сообщение об ошибке и останавливает выполнение. Вы можете предотвратить множество ошибок, проверив допущения перед выполнением кода, которое окажется неудачным, если допущения будут неверны. Вы можете «вылавливать» и исправлять ошибки, используя в вашей программе выражение On Error.
Запуск программы в правильном контексте
Решив, в каком контексте запускать программу, вы можете сделать некоторые допущения относительно программного окружения. Например, если вы пишите программу VBA для управления поведения двойного щелчка, то вы можете допустить, что документ открыт и что двойным щелчком выбирается образ в активном окне. Однако это ограничивает способность программы контролировать действия пользователя. Например, ничто не остановит пользователя от попытки запустить программу VBA, разработанную для управления событием двойного щелчка, из диалогового окна Macros, вместо того, чтобы дважды щелкнуть на образе.
В случае, если вашей программе требуется выбранный образ, укажите свойство Selection активного окна, чтобы убедиться в том, что оно содержит хотя бы один объект.
Dim selectObj As Microsoft Visio.Selection
Set selectObj = ActiveWindow.Selection
If selectObj.Count = 0 Then
MsgBox «You must select a shape first.» , , «Select shape»
Else
Continue processing
End If
Проверка: Объекты и получаемые значения существуют
Перед попыткой доступа к объектам убедитесь в том, что они имеются в наборе.
Следующий пример проверяет наличие шаблонов в документе перед попыткой повторения всего набора Masters, что обязательно вызовет ошибку, если набор окажется пустой.
If ThisDocument.Masters.Count = 0 Then
MsgBox «Document has no masters.»
'Go to an error handler
End If
В случае, если свойство или метод должны что-то предоставлять, то неплохо было бы убедиться в том, что они действительно это делают. Например, если ваша программа форматирует текст образа, то вы должны убедиться в том, образ Shape содержит текст:
Dim shpObj As Microsoft Visio.Shape
Dim strText As String
Set shpObj = ActivePage.Shapes. Items(1)
strText = shpObj.Text
If strText = «» Then
MsgBox «The selected shape has no text to format.» , , «Format Shape Text»
Else
'Continue processing
End If
Проверка значений на ошибки
VBA имеет функцию Ошибка (Error), которая выдает в ответ строку. Когда в приложении Microsoft Visio возникает ошибка, она выдает код ошибки и строку, которая описывает ошибку. Используйте функцию Error, чтобы получить строку, связанную с кодом ошибки, который выдается приложением Microsoft Visio.
Объект Microsoft Visio Cell имеет свойство Error, которое указывает, произошла ли при выполнении формулы ячейки какая-либо ошибка. В случае, если ваша программа меняет формулы ShapeSheet, проверьте это свойство и убедитесь в том, что формула работает ожидаемым образом.
Для эффективной работы и последующей минимизации задач на поддержание функциональности используйте ниже приводимые приемы управления проектом. Удаляйте предметы проекта, которые вам больше не нужны. Это уменьшит размер файла и упростит обслуживание проекта.
При необходимости защищайте ваш код от просмотра и видоизменения со стороны пользователей.
Разрабатывайте модули, модули класса и пользовательские формы для многократного использования. Это сэкономит время на написание кода. Кроме того, Microsoft Visual Basic for Applications (VBA) 6.0 позволяет вам запускать в действие дополнения, с которыми могут работать и другие приложения.
Перемещение предметов проекта
При перемещении предмета вы удаляете его из списка проекта, так как нельзя аннулировать действие Remove. Убедитесь в том, что оставшийся код в других модулях и пользовательских формах не ссылается на код в удаленном предмете.
Как удалить предмет проекта
1. Укажите предмет в окне Project Explorer.
2. Выберите File ->Remove <Name>.
3. Программа спросит вас, хотите ли вы экспортировать предмет перед его удалением. Нажмите на Yes, чтобы открыть диалоговое окно Export File или нажмите на No, чтобы удалить предмет.
Защита вашего кода
Для того, чтобы защитить ваш код от просмотра и видоизменения
со стороны пользователей, просто заблокируйте проект. Это означает, что вам необходимо указать пароль, который нужно будет вводить перед просмотром проекта в окне Project Explorer.
Для того, чтобы блокировать ваш проект VBA от просмотра:
1. Выберите Tools -> Drawing Name Properties.
2. Нажмите на Protection и укажите параметр Lock Project For Viewing.
3. Введите пароль и подтвердите его.
4. Сохраните ваш файл Microsoft Visio и закройте его. При следующем открытии вашего файла Microsoft Visio проект будет блокирован. В случае, если пользователь захочет просмотреть или отредактировать проект, ему придется ввести пароль.
Использование встроенного диспетчера Add-in Manager
В редакторе Visual Basic вы можете использовать встроенный диспетчер для управления дополнительными программами, которые расширяют среду разработки VBA. Эти позволяют разработчику использовать конкретную дополнительную программу для того, чтобы добавлять ее функциональность к любому приложению VBA 6.0, включая приложение Microsoft Visio.
Для того, чтобы вывести встроенный диспетчер на экран из редактора Visual Basic, выберите Add-ins ->Add-ins Manager. При этом появится диалоговое окно встроенного диспетчера вместе со списком других расширений Microsoft Visio.
Программирование в Microsoft Visio 2002
Работа с формами
Формы — это окна интерфейса программы. С их помощью можно сообщать пользователю необходимую информацию или получать ее в ответ. Для создания форм используются средства Visual Basic for Applications (VBA).
Создать новую форму: Выберите Insert, а далее дайте команду UserForm.
Поле отображения текста — отражает текстовую информацию.
Поле ввода текста — позволяет пользователю ввести текстовую информацию.
Несколько видов списков — средства выбора варианта наподобие ниспадающего перечня шрифтов.
Флажок — элемент, который может находиться в трех состояниях: включенном, выключенном (это устанавливается как пользователем, так и программистом) или неактивном (определяется программой).
Переключатель — элемент, который также может находиться во включенном, выключенном и неактивном состоянии. В отличие от флажка, переключатели должны быть объединены в группы с помощью элемента «рамка». В случае, если же один из группы включен, то остальные уже не могут быть в таком состоянии.
В Visio 2002 есть диалоговые окна, которые содержат как флажки (принято называть «функции»), так и переключатели. Кстати, почти все закладки меню Tools состоят из флажков, а меню Insert — из переключателей.
Выключатель — кнопка, которая может находиться в нажатом или отжатом состоянии.
Рамка — отражает прямоугольник с заголовком, используется в основном для объединения групп переключателей.
Командная кнопка — обычная, наподобие кнопки ОК или «Отмена» в любом диалоговом окне.
Набор вкладок и 10-й элемент — наборы страниц, которые представляют комплект страниц-вкладок. Отличие между этими двумя элементами (т. е. набором вкладок и 10-м элементом) состоит в том, что 9-й при переключении на другую страницу не затрагивает остальные элементы формы, даже когда они находятся на нем, а изменить состояние других элементов можно только программно. Десятый же элемент при своем переключении принудительно (т. е. вне зависимости от программы) скрывает элементы на одной своей странице и показывает на другой. Для того, чтобы лучше понять разницу между этими двумя типами элементов, создайте форму с каждым из них и посмотрите, как они работают, — при переключении закладок в 9-м элементе на них самих ничего меняться не будет, а при использовании 10-го элемента на каждой новой закладке вы можете разместить свой, отдельный набор элементов.
Так, 9-й элемент может использоваться в какой-либо программе, в частности базе данных, отображая вкладки с именами сотрудников. На каждого из них в нескольких полях есть стандартная текстовая информация, и число этих полей для всех одинаково. В этом случае разумнее будет использовать именно 9-й элемент, соответственно изменяя содержимое полей с текстом при переключении вкладок, чем создавать с помощью 10-го элемента набор страниц с отдельными полями текста на каждой из них, что резко увеличивает объем программы и усложняет ее.
К сожалению, в настоящее время нет единой номенклатуры этих двух элементов форм VBA, поэтому в справочной системе и литературе можно встретить разные названия для каждого из них.
Полоса прокрутки может передавать в программу число, равное расстоянию в пунктах от ее начала. Верхний предел расстояния неограничен.
Счетчик — две кнопки со стрелками. Может передавать в программу свое значение от 1 до 100.
Рисунок — в этот элемент можно вставить из файла рисунок, который будет храниться в форме (и шаблоне с ней).
У каждого элемента есть свое уникальное имя, а также набор свойств, которые необходимо внимательно изучить.
Большой набор свойств имеет также и сама форма. С помощью ниспадающего меню в «Окне свойств» можно быстро перейти к свойствам выбранного элемента.
Среди наиболее распространенных свойств, имеющихся почти у всех элементов, можно отметить следующие: Caption — надпись на поверхности или в заголовке, если он есть; Тор и Left — координаты верхнего левого угла элемента; Height и Width — высота и ширина; Enabled — доступность для изменений пользователем; Tablndex — число, показывающее, когда можно изменить в данном элементе содержание или состояние при переходах между элементами с помощью клавиши табуляции (можно запретить переход на какой-либо элемент, указав его свойство TabStop как False).
Следует обратить внимание и на свойства формы: Picture, PictureAlignment, PictureSizeMode, Picture Tiling. С их помощью на поверхность формы можно поместить какой-нибудь фоновый рисунок.
Следует помнить, что создание элемента — только начало работы над формой. Сам по себе элемент никаких действий не выполняет, кроме, конечно, тех, которые его определяют: кнопка активизируется от щелчка мыши или клавиши Enter, в списке представляются значения и т. д. Для того, чтобы элемент выполнял какое-нибудь действие, когда на него оказывается влияние, надо написать программу «реакции на события». С элементом может произойти множество событий: двойной щелчок мыши на нем, проводка курсора мыши, ввод текста в соответствующее поле и т. д. Итак, чтобы записать программу, необходимо два раза щелкнуть на элементе. Тогда откроется окно и появится «заготовка» следующего вида:
Private Sub UserForm_CHck()
End Sub
Теперь можно вводить команды, которые выполняются в том случае, когда в заголовке происходит событие. Оно считается стандартным для кнопок, флажков, рисунков, рамок, переключателей и окон отображения текста. Для остальных элементов — полей ввода текста, полос прокрутки, счетчиков и др. — стандартным считается событие Change, т. е. их изменение.
Для того, чтобы получить список возможных событий для элемента, следует поставить курсор внутрь процедуры обработки стандартного события и выбрать нужное, которое размещается справа вверху окна написания программы. Слева вверху этого же окна указаны все процедуры и функции программы реакции формы на события. В последней могут быть представлены и другие процедуры и функции, не связанные с элементами формы.
Некоторые события имеют параметры, которые передаются программе. Так, процедура обработки события KeyDown (т. е. нажатие какой-либо клавиши при активном элементе) имеет вид:
Private Sub CommandButton1_KeyDown(ByVal
KeyCode As MSForms.Returnlnteger, ByVal
Shift As Integer)
End Sub
Переменная KeyCode после срабатывания данной процедуры будет иметь значение, равное коду нажатой клавиши, а переменная Shift будет составлять 1, если соответствующая клавиша была нажата, и 0, если не была. Эти переменные можно использовать в процедуре.
Все свойства можно изменять и из программы. Например, команда TextBoxl.Enabled=True позволяет пользователю ввести текст в поле ввода TextBox1, а команда CheckBoxl.Value=False снимет флажок с именем CheckBoxl. Такая возможность делает формы VBA динамическими, т. е. их содержимое может в ответ на действия пользователя изменяться немедленно, причем без выгрузки и повторной загрузки формы. Сама форма может работать как обычное диалоговое окно программы для Windows ХР — к примеру, выбор пользователем значения из списка позволяет каждый раз выводить в форму какой-либо текст, неодинаковый для разных значений. Наиболее часто динамические формы применяются для отключения элементов, которые не могут быть использованными при каких-либо условиях. Например, в форме, состоящей из поля ввода текста и кнопки, вызывающей процедуру вычисления квадратного корня, кнопка может становиться неактивной при написании в поле ввода отрицательного числа.
Для того, чтобы просмотреть форму в режиме выполнения, иначе говоря, представить вид ее в готовой программе, надо нажать клавишу F5, как и для запуска обычной программы.
Следует иметь в виду, что после выгрузки формы все переменные, описанные в программе реакции формы на события, обнуляются. Поэтому получить от формы какие-либо данные уже нельзя. В случае, если в результате работы формы в основную программу должны передаваться некоторые данные, то нужно с помощью инструкции Public объявить в этой программе несколько переменных. Затем в программе реакции формы на события записать в них всю требующуюся информацию, полученную от пользователя, и потом снова использовать значения этих переменных в основной программе.
Программа реакции формы на события представляет собой полноценный модуль проекта, за исключением лишь того, что процедуры из него могут быть вызваны только в другой программе. Так как вызов формы на исполнение с помощью кнопки, строки меню или сочетания клавиш невозможен, то необходимо в основной программе указать команду Имя-Формы.Show и назначить ей кнопки и сочетания клавиш. Следует учесть, что если даже все требуемые функции реализованы в процедурах программы реакции формы на события, то все равно приходится создать основную программу, хотя бы только для вызова данной формы.
Так же как и модули, формы могут быть сохранены в отдельных файлах с помощью функции Export File.
Однако в отличие от модулей, при сохранении форм получается два файла: один содержит текст программы реакции формы на события, другой — информацию о расположении элементов на форме, их свойствах, а также рисунки и фон формы.
Поэтому при импортировании оба этих файла должны находиться в одной папке.
Операторы цикла и перехода
В случае, если нужно повторить ту или иную группу операций несколько раз, то используются такие операторы цикла, как Do...Loop, For...Next и For Each...Next. А если требуется совершить какие-либо действия в зависимости от наличия определенных условий, а также от значения той или иной переменной, то применяются операторы условия: If...Then...Else и Select Case.
Переход к другой части той же программы выполняется с помощью оператора GoTo «имя метки».
В то место, куда нужно совершить переход, на отдельной строке следует проставить метку с соответствующим именем и за ней двоеточие.
Для того, чтобы ускорить выполнение работающей с текстом программы, рекомендуется почаще использовать команду ActiveDocument. UndoClear, которая позволяет очистить список выполненных команд (однако делает невозможной отмену последних изменений).
Для того, чтобы система не тратила время и ресурсы на постоянное обновление экрана и отображение изменений и чтобы выполнять визуальный контроль происходящих изменений, поставьте в начало программы команду Application. Screen Updating = False, а в конец — команду Application.ScreenUpdating = True.
В случае, если возникает нестандартная ошибка, то с помощью команды On Error goto «имя метки» можно заставить программу переходить к указанной метке, а поставив перед каким-либо фрагментом программы команду On Error Resume Next, можно приказать ей (при возникновении ошибки) начать выполнение следующей команды.
О шифровании и паролях
Защита доступа к информации с помощью пароля — самый распространенный способ хранения секретов на ПК. Она используется практически везде — начиная с задания пароля на вход в Internet и заканчивая установкой пароля на проекты VBA. Однако о принципах такой защиты большинству пользователей известно довольно мало. Вот некоторые сведения.
Среди различных математических логических функций есть одна весьма примечательная функция — XOR. Команда, выполняющая ее, имеется практически во всех языках программирования. XOR — это та же функция ИЛИ (если хотя бы на один из входов было подано значение 1, то и результат получается равным 1), однако она при подаче на вход сразу двух единиц выдает 0. Обратите внимание на одно интересное свойство функции: если на ее вход подать значение первого входа и получившийся после этого результат, то выйдет значение второго входа. То же самое происходит и со значением второго входа и с полученным результатом: в результате образуется значение первого входа. Следовательно, зная итоговую величину функции XOR и данные на любом из входов, можно получить значение на другом входе — т. е. функция XOR обратима.
Отсюда и вытекает принцип шифрования паролем. Берем некий текст, который должен быть зашифрован, и определенное слово, выбранное в качестве пароля. Текст — это последовательность букв-байтов. Поскольку байт равен восьми битам, это позволяет представить текст как последовательность битов — нулей и единиц.
Зная зашифрованный текст и ту часть незашифрованного, которая соответствует определенной части обычного, можно с использованием той же функции XOR определить пароль, а затем с его помощью расшифровать остальную часть текста. Потому-то в современных системах для защиты текста паролем и применяют более сложные способы шифрования, к примеру повторение пароля не несколько раз подряд, а через определенные промежутки, либо расположив символы пароля в обратном порядке. Но всегда необходима функция XOR благодаря своей обратимости.
Способы создания программ
Многие используют Visio 2002 как среду разработки программных продуктов посредством Visual Basic for Applications — языку программирования. Visual Basic for Applications — вполне полнофункциональный язык программирования, с помощью которого можно создавать вполне законченные и работоспособные программы. На языке Visual Basic for Applications могут начать создавать программы даже те, кто ранее никогда не занимался программированием, причем для этого не потребуются ни толстые книги, ни дорогие курсы обучения. Необходимо лишь уметь думать, наблюдать, исследовать, ставить эксперименты, делать выводы — в общем, проявлять способности к научному анализу. И именно это делает программирование на Visual Basic for Applications прекрасным способом выработки в себе способности к научному подходу к различным явлениям.
Средства программирования
В поставку Visio 2002 входит интерпретатор языка программирования Visual Basic for Applications, а также средства создания полноценных программных продуктов.
Компоненты проекта делятся на категории:
ThisDocument
При выборе пункта View Object произойдет переход к соответствующему документу — к его содержимому.
В чертеж Visio 2002 можно вставить специальные объекты — кнопки, поля ввода текста, поля выбора вариантов и др. В этом случае после выбора пункта контекстного меню объекта ThisDocument можно написать программу, которая будет выполняться при нажатии соответствующей кнопки, введении текста и др. К написанию такой программы можно перейти и из контекстного меню самого специального объекта.
Модули
Модули — это и есть собственно программы. Щелкнув два раза мышью на имени модуля, можно получить доступ к тексту программы или начать ее создавать.
Для начала написания программы надо в ее окне написать Sub Уникальное имя программы, после чего Visio сама добавит фразу End Sub, и между этими двумя фразами должен заключаться текст основной части программы.
Модули класса
Класс — это особое понятие, играющее очень важную роль в программировании.
Вкратце это тип объектов, определяемых программистом.
Формы»
Формы — это диалоговые окна программ, которые можно разработать по своему вкусу.
Редактор Visual Basic обладает дружественным интерфейсом. Он постоянно подсказывает программисту, какие параметры имеет та или иная команда, как надо корректно ее написать. Например, если набрать команду MsgBox, а после нее открывающую круглую скобку, то над курсором сразу же появится подсказка. С ее помощью можно получить информацию о синтаксисе данной команды, т. е. о том, как конкретно нужно задавать ее параметры в тексте программы —в данном случае текст окошка, количество кнопок, значок и звук при появлении диалогового окна.
Язык Visual Basic for Applications объектно-ориентированный. Это значит, что очень многие его команды имеют особенный формат, отличный, скажем, от формата Basic или Pascal.
Типичная команда Visual Basic имеет такой вид:
<0бъект>.<0бъект, входящий в первый объект>.<...>.<Тот объект, с которым нужно произвести действие>.<Собственно действие>.
Иными словами, каждая команда пишется как бы «с конца»: вначале определяется то, над чем надо произвести действие — объект, а затем само действие — метод. Разделителями компонентов команды служат знаки «точка». Вот пример такой команды:
Application.ActiveDocument.PageSetup.Orientation = wdOrientLandscape.
Эта команда устанавливает альбомную ориентацию листа в документе. У объекта Application (приложение, в данном случае Visio 2002) есть подобъект ActiveDocument (активный документ), у этого подобъекта есть еще подобъект — PageSetup (параметры страницы), далее следует подобъект Orientation (ориентация листа), которому присваивается значение wdOrientLandscape — альбомная ориентация листа.
Такой же вид имеет и команда получения информации об ориентации листа — она будет выглядеть так:
р = Application.ActiveDocument.PageSetup. Orientation.
После ее выполнения значение переменной р будет wdOrientLandscape или wdOrientPortrait соответственно.
При написании команд редактор Visual Basic for Applications постоянно подсказывает возможные варианты следующего шага. Так, к примеру, стоит написать в тексте программы слово Application и поставить точку, как сразу появятся возможные варианты продолжения.
Можно мышкой выбрать нужный вариант, и он обязательно будет правильным — не в смысле верности алгоритма (это остается на совести программиста), а в смысле соответствия правилам синтаксиса и иерархии объектов, а также правописания слова. Затем можно вновь поставить точку, и вновь будет выдан список возможных вариантов продолжения.
Есть также команда завершения слова — можно не писать целиком слово Application, а набрать Appli и нажать Ctrl+пробел. Редактор допишет слово сам или предоставит возможность выбора слова, если его однозначно нельзя определить по первым буквам. Он как бы «ведет» программиста по процессу написания программы, позволяя ему сосредоточиться не на банальном синтаксисе языка, а на решаемой программой проблеме, и не держать в памяти правила написания каждой команды.
Даже если программист не воспользуется предоставляемой подсказкой и введет всю команду вручную, то редактор не даст ему так просто двинуться дальше, если в элементарном синтаксисе данной команды будет ошибка, выдав сообщение «Ошибка компиляции».
Еще одним серьезным достоинством редактора Visual Basic for Applications 6.3 является наличие прекрасной справочной системы. Поставив курсор на любую команду или название объекта Visual Basic for Applications и нажав клавишу F1, можно получить подробную справку о том, зачем нужна эта команда, каков ее синтаксис и даже посмотреть пример ее использования и скопировать его себе, если необходимо.
Подобная дружественность редактора Visual Basic for Applications дает прекрасную возможность для самостоятельного изучения этого языка программирования.
Поставив перед любой командой знак апострофа, можно ее закомментировать, т. е. исключить из выполнения. В этом случае Visual Basic for Applications не будет ее выполнять. Кроме того, после знака апострофа можно написать пояснения к соответствующей части программы для себя и других программистов. Все комментарии отображаются в редакторе Visual Basic for Applications зеленым цветом.
Нажав на соответствующий элемент (кнопку, поле ввода, переключатель и др.) и нарисовав затем мышкой этот элемент на поле формы, можно поместить его на форму. Двойной щелчок на помещенном на форму элементе открывает окно программ пользовательской формы. В нем можно написать программу, которая будет выполняться при активации соответствующего элемента. Можно также выбрать другие действия, которые надо выполнить, чтобы запустить такую программу, — события — но об этом позже. Вообще, у каждой формы есть окно ее программ — реакций элементов на события, хотя оно может быть и пустым.
В случае, если выбрать какой-нибудь элемент формы, а из его контекстного меню пункт Properties, то откроется «Окно свойств». Здесь можно задать все доступные свойства каждого элемента (к примеру, его цвет, цвет границы, надпись, состояние — доступно/недоступно для изменений), поместить на элемент картинку из внешнего файла. Все эти свойства также можно задавать программно, однако некоторые, вроде той же картинки, стоит задавать только в этом окне.
Окно «Просмотр объектов», вызываемое кнопкой F2, является кратким справочником по всем возможным свойствам и методам (т. е. действиям, командам) объектов Visual Basic for Applications.
Можно посмотреть, какие свойства или методы доступны для каждого объекта, какие объекты в принципе существуют в Visual Basic for Applications, вызвать справочный файл для каждого объекта, свойства или метода.
Все модули, входящие в состав проекта, можно сохранить в текстовом файле. Модуль будет сохранен в текстовом файле с расширением .BAS. Можно просто перетащить название модуля в окно «Проводника» Windows, удерживая левую кнопку мыши, — редактор Visual Basic for Applications поддерживает механизм Drag'n'Drop.
Отладка программы
В Visio 2002 редактор Visual Basic for Applications обладает широкими возможностями отладки программы, т. е. поиска в ней алгоритмических и языковых ошибок. Следует учесть, что язык Visual Basic for Applications не компилируемый, а интерпретируемый (готовая программа
представляет собой текст исходного кода, хранящийся в шаблоне или документе, который переводится в машинные коды при каждом ее запуске, в то время как конечный продукт средств создания программ на остальных языках программирования представляет собой файл с исполняемым машинным кодом). Именно по этой причине после завершения написания кода и успешной его отладки программа готова к использованию.
Для запуска на выполнение готовой или записанной программы из редактора Visual Basic for Applications необходимо нажать клавишу F5. Программа начнет выполняться, и, если в ней вдруг найдутся ошибки в записи команд или обращения к неизвестным командам, будет выдано соответствующее сообщение.
В случае, если возникает ошибка вследствие неправильного использования команд, то редактор Visual Basic for Applications выдает диалоговое окно, в котором приводится описание ошибки.
Через меню команд Debug можно перейти в режим отладки программы — специального состояния редактора Visual Basic for Applications, в котором программа может исполняться построчно, а программист имеет возможность видеть значения всех переменных программы и даже принудительно задавать эти значения. Также в режим отладки можно перейти из редактора Visual Basic for Applications, поставив курсор в текст программы и нажав клавишу F8.
В режиме отладки программисту доступны следующие возможности:
Пошаговое исполнение программы
Выполняются все команды последовательно, но выполнение каждой команды требует нажатия кнопки F8. В случае, если программа где-то выдает ошибку алгоритма, то можно посмотреть, на каком конкретно этапе это происходит. При пошаговом исполнении в редакторе желтым цветом подсвечивается команда, которая будет выполнена следующей.
Просмотр значений переменных
Подведя в режиме отладки курсор к имени любой переменной, можно увидеть ее значение. При дальнейшем выполнении программы значения переменных в этом окне принимают те значения, которые они имеют в программе. Это очень полезно для отладки алгоритма программы, когда необходимо найти ошибку во внутренней логике программы, связанную с неправильной или недостаточно точной разработкой порядка действий.
Точки останова
В случае, если программа длинная, то проходить по всем ее строчкам с помощью кнопки F8 — занятие не слишком интересное и приятное. В этом случае лучше использовать точки останова — отметки в тексте программы, где ее исполнение должно остановиться, а сама программа перейдет в режим отладки. Для задания точки останова нужно щелкнуть левой кнопкой мыши на сером левом поле окна модуля, и тогда появится коричневая строка с кружочком на месте точки останова.
Начиная с этого места, программа будет выполняться пошагово по нажатию кнопки F8. Отказаться от пошагового выполнения можно путем нажатия кнопки F5. Точки останова полезны, когда нужно локализовать ошибочное место в программе, особенно если оно находится в середине текста или не единичное.
Окно контрольного значения
В этом окне можно задать определенное выражение, значение которого будет подсчитываться параллельно с выполнением программы. Кроме того, выделив в режиме отладки какое-нибудь выражение и нажав Shift+F9, можно увидеть его значение, а также добавить это выражение в окно контрольного значения и отслеживать его дальнейшие изменения.
Такая возможность необходима, к примеру, при отладке длинной программы с множеством арифметических выражений, одно из которых дает ошибку. С помощью этого окна можно отследить, когда и при изменении какого параметра значение выражения станет неприемлемым.
Окно отладки
В текст программы можно вставить специальную команду Debug.Print (a+b, к примеру). Тогда при выполнении этой команды в «Окно отладки» будет печататься то, что задано в ее параметрах. Возможности применения этого окна многообразны. В него могут выводиться сообщения в случае выполнения какого-либо условия. Могут отображаться и промежуточные результаты вычислений для контроля их правильности — чтобы не смотреть и не искать нужное выражение в окнах «Локальные переменные» или «Контрольное значение», а видеть их сразу на экране.
Окно отладки можно использовать даже как маленький калькулятор или командную строку, и тогда при нажатии Enter после ввода команды она будет тут же выполнена! Надо только перед самой командой писать слово Print, и тогда команда выдаст результат в этом же окне.
Стек вызова
В этом окне отображается структура программы, состоящей из нескольких подпрограмм — процедур или функций, а также показывается, какие переходы возможны между этими подпрограммами. В случае, если программа сложная и большая, то разобраться, какая подпрограмма вызывает ту или иную подпрограмму, достаточно трудно — для этого и стоит использовать «Стек вызова», в котором все изображено наглядно.
В режиме отладки возможен принудительный переход к какой-либо команде для того, чтобы дальнейшее выполнение программы пошло именно с нее. Для этого надо просто перетащить мышкой по серой полосе слева указатель в виде желтой стрелки на нужное место.
Настройка вызова программы
Когда программа написана и отлажена, необходимо придать ей «товарный вид» — убрать лишние команды и модули, создать способ вызова программы (кнопку или пункт меню) или панель инструментов для кнопок. Без этого даже с очень хорошо написанной программой сможет работать лишь ее создатель, да и то не всегда. К тому же красивое оформление всегда радует глаз.
Для начала нужно просмотреть весь код и выкинуть все лишнее — закомментированные ненужные команды, просто излишние комментарии. Потом следует определить, какие модули и формы являются компонентами готовой программы, а какие, возможно, были написаны для других программ или в качестве экспериментальных, и запомнить их названия.
Не стоит забывать и об информативном руководстве для пользователя, особенно если программа будет распространяться через Internet. Лучше всего такое руководство оформить в отдельном текстовом файле.
Советы
Команда On Error Resume Next — обработчик ошибок. При возникновении ошибки в тексте (связанной, скажем, с обращением к несуществующему объекту) он продолжит выполнение программы с той команды, которая следует за вызвавшей ошибку. Обработчик ошибок может также выглядеть как On Error GoTo метка, и тогда при ошибке произойдет переход к указанной в нем метке, а программа продолжит выполняться именно с нее.
Для того, чтобы ускорить исполнение программы, работающей с текстом, в ее начало нужно поставить команду Application.ScreenUpdating = False, а в конец — Application.ScreenUpdating = True (если только не требуется контролировать происходящие изменения). Эти команды позволяют системе не тратить время и силы на постоянное обновление экрана и отображение изменений. Но если все же нужно обновить экран, то следует использовать команду AppIication.ScreenRefresh.
На сайте www.microsoft.ru/offext можно найти огромное число макросов и шаблонов. Изучение их текста помогает освоить Visual Basic for Applications, узнать много новых приемов и команд. Можно даже послать на конкурс свою программу-макрос, а если она будет признана достойной, то и выиграть приз!
В случае, если исходный текст программы на Visual Basic for Applications закрыт от просмотра паролем, а вы его забыли, то не огорчайтесь: в Internet по адресу www.passwords.ru есть программа Advanced VBA Password Recovery, позволяющая восстановить забытый пароль. Ее английская версия требует оплаты и регистрации, а русская — бесплатна).
Во многих программах для Windows используется такой элемент, как ProgressBar — индикатор, показывающий течение того или иного процесса. Причем он есть практически во всех программах-инсталляторах. К сожалению, в VBA этот элемент отсутствует, но его можно создать самостоятельно. Делается он следующим образом (предполагается, что есть часть программы, в которой выполняется продолжительный цикл, т. е. известное число однотипных операций). Создаем небольшую форму и помещаем на нее надпись с пояснительным текстом (в частности, Label!) и две другие (скажем, Label2 и LabelS), низкие и широкие, располагая их одну над другой. Зададим этим формам, к примеру, следующие координаты и размеры:
Label2: Top - 45, Left - 15, Height - 15, Width - 250
Label3: Top - 45, Left - 15, Height - 15, Width - 0
Для Label2 укажем в качестве фонового цвета серый, а для Label3 — зеленый.
Для того, чтобы форма появилась на экране, но могла быть программно модифицируема во время своего отображения, помещаем в нее следующий текст:
Private Sub UserForm_Activate()
...остальной текст программы, которая должна выполняться во время отображения ProgressBar'a...
Unload Me
End Sub
Все, что находится в теле этого обработчика, будет выполняться в то время, как форма находится на экране. По окончании выполнения программы форма будет выгружена, однако если предполагается продолжение работы с ней, то выгружать ее не обязательно. В это время никакие другие события формой обрабатываться не будут.
Ширину любой надписи можно изменять программно. Поэтому для отображения ProgressBar вставим в текст строки, изменяющие ширину надписи Label3. Например, некий цикл, который нужно снабдить ProgressBar, в программе выполняется n раз. Тогда ширина надписи Label3 будет определяться командой в теле этого цикла по формуле: Label3.Width = (scet / n)*250, где scet — счетчик цикла, а 250 — ширина надписи Label2. После каждой такой команды следует вставлять инструкцию Me.Repaint, чтобы перерисовать форму с учетом новых параметров третьей надписи, поскольку автоматической перерисовки до полного отображения формы не происходит — это совершится тогда, когда закончится обработка события UserForm_Activate() и форма будет выгружена. В случае, если форма с ProgressBar должна долго находиться на экране без изменений, то рекомендуется почаще использовать команду Me.Repaint, так как без перерисовки внешний вид формы может быть испорчен окнами других приложений.
Итак, текст для формы с ProgressBar должен иметь следующий вид:
Private Sub UserForm_Activate()
Me.Repaint
For t=1 to n 'n - количество необходимых выполнений команд цикла
... ... ... ... нужные команды программы
цикле ..: ... ... ...
Label3.Width = ((t / n) * 250)
Me.Repaint
Next t
Unload Me
End Sub
В то место основной программы, в котором должен находиться цикл с ProgressBar, следует поместить команду вызова этой формы (здесь: UserForm.Show), не забыв объявить передаваемые в цикл и из цикла переменные основной программы как Public. Цикл как бы «выносится за пределы» основной программы и помещается в обработчик события UserForm_Activate.
Разумеется, текст можно изменять и улучшать. Например, несколько раз использовать ProgressBar в одной форме, каждый раз обнуляя ширину третьей надписи. А еще можно поместить на одну форму несколько обработчиков ProgressBar, где один из них показывает выполнение всего задания, а другой — его текущей части. В общем, есть простор для творчества.
Хранение в документе скрытой информации
Иногда в документ нужно записать какую-нибудь информацию «для внутреннего пользования», в частности такую, которая позволила бы при следующей обработке документа программой использовать определенные предыдущие данные и сделать такую запись с помощью свойства Variable:
ActiveDocuraent.Variables.Add Name:=»x1»,
Value:=»Первая переменная»
ActiveDocument.Variables.Add Name:=»x2»,
Value:=12
Прочитать эти переменные можно с помощью похожей функции:
у1 = ActiveDocument.Variables(«x1»),Value
у2 = ActiveDocument.Variables(«x2»). Value
Таким способом имеет смысл помещать в документ служебную информацию для макросов: сохраненные параметры, скрытые комментарии и др. Но не думайте, что для надежного сокрытия какой-нибудь секретной информации достаточно поместить ее в переменные. С помощью функции
For Each per In ActiveDocument.Variables
Debug.Print per.Name + « « + per.Value
Next per
абсолютно все переменные будут благополучно помещены в окно отладки, где их можно легко и просто просмотреть.
Итак, посредством Microsoft Visio 2002 вы можете «улучшить» ваши проекты и решения, используя встроенный в это приложение язык программирования Microsoft Visual Basic for Applications (VBA) версии 6.З.
Visual Basic — простейшее средство разработки для создания большинства типов приложений, поэтому неудивительно, что оно позволяет проще всего реализовать прием событий. Даже, если вы активный сторонник C++, особенно при создании собственных интерфейсов, вы должны знать, что на Visual Basic гораздо легче разрабатывать клиентские приложения в Microsoft Visio 2002, которые принимают события хотя бы от таких серверов, как Internet Explorer 6.0. К примеру, автоматизируя сервер на Visual Basic, вы прежде всего должны объявить переменную типа сервера, который вы автоматизируете: Вы просто задаете зарезервированное слово WithEvents, чтобы сообщить о том, что хотите получать события от данного сервера.
С появлением элемента управления WebBrowser control в Microsoft Internet Explorer создание Internet-приложений значительно упростилось. Для того, чтобы добавить функции просмотра Web-узлов в свое приложение, достаточно использовать управляющий элемент WebBrowser. Он позволяет обращаться к любому URL-адресу, перемещаться по списку истории посещений или переходить непосредственно на домашнюю страницу пользователя. К вашим услугам любые возможности обозревателя Internet Explorer. С помощью WebBrowser вы также способны автоматически управлять Internet Explorer 6.0.
Конечно, управление Internet Explorer 6.0 — это прекрасно. Но если вы не знаете, что делает Internet Explorer в каждый конкретный момент, значит, по-прежнему не способны на полный контроль. Поэтому Internet Explorer предоставляет интерфейс событий Event interface, через который можно отслеживать его активность и выполнять определенные действия.
Предположим, что в Microsoft Visio 2002 вы создаете приложение, предназначенное для использования в интрасети предприятия и хотите запретить пользователям просмотр внешних серверов Internet. «Прослушивая» события, происходящие в Internet Explorer, ваше приложение определит момент запроса пользователем внешнего URL, а затем полностью " отменит этот переход. Для реализации данного механизма вам потребуется знать, какой тип события запускает Internet Explorer 4.0, как обеспечить прием и реализовать обработку событий в своем приложении.
Что такое события? Для уведомления своих клиентов о каком-то событии СОМ-объект отправляет сообщение. Это сообщение так и называется — событие (event), а процесс передачи сообщения называется запуском события (event firing). Но если событие запущено, а его никто не ждет, то какой в нем смысл? Очевидно, что клиентское приложение, управляющее СОМ-объектом, должно «прислушиваться» к инициализируемым событиям. Когда клиентское приложение «хочет» получать события от СОМ-объекта, оно сообщает об этом СОМ-объекту.
СОМ-объект сможет «разговаривать» со своим клиентом, если он поддерживает один или более исходящих интерфейсов (outgoing interface). Такие интерфейсы реализуются клиентом, и каждый из них подключается к СОМ-объекту через отдельную точку подключения (connection point). Любая точка подключения представлена интерфейсом IConnectionPoint. Часть клиента, обеспечивающая исходящие интерфейсы, известна как приемник событий (event sink); СОМ-объект, который поддерживает данные интерфейсы, называется подключаемым объектом (connect-able object). Для того, чтобы быть подключаемым объектом и поддерживать точки подключения, объект должен реализовывать интерфейс IConnectionPointContainer, через который клиенты будут узнавать, какие исходящие интерфейсы поддерживает сервер.
Каждый подключаемый объект реализует этот интерфейс. Клиент, желающий получать события, может узнать через IConnectionPointContainer, какие точки подключения поддерживает подключаемый объект. Клиент способен получить указатель на IConnectionPointContainer, просто вызвав Querylnterface, используя указатель на другой интерфейс. Затем клиент применяет один из двух методов данного интерфейса для получения указателя на точку подключения.
С помощью EnumConnectionPoints он получает список указателей на все точки подключения, которые поддерживаются этим подключаемым объектом. Другой метод, FindConnectionPoint, позволяет клиенту спрашивать у подключаемого объекта, поддерживает ли он тот или иной интерфейс. Клиент указывает идентификатор интерфейса (IID) той точки подключения, которая его интересует. В случае, если подключаемый объект поддерживает такой интерфейс, он вернет указатель на интерфейс ICormectionPoint для соответствующей точки подключения.
Когда клиент выяснил, какие точки подключения поддерживает данный подключаемый объект, он устанавливает с ним соединение. Клиент извещает подключаемый объект о том, какой приемник событий ему следует использовать для всех событий. Кроме того, благодаря этому соединению подключаемый объект узнает о том, что клиент «намерен» прекратить прием событий от него. Клиент может подключиться к объекту с помощью метода интерфейса IConnectionPoint. Вам в основном предстоит работать с первыми двумя интерфейсами.
Соединение с определенной точкой подключения подключаемого объекта клиент устанавливает по методу Advise. В сущности клиент «говорит» объекту: «Вот приемник событий, который я реализовал для данной точки подключения. Пожалуйста, отправляй ему все события». Клиент . должен передать своему приемнику событий указатель на интерфейс Unknown. Подключаемый объект использует этот указатель для запроса к клиенту об интерфейсе приемника событий, поддерживаемом данной точкой подключения. Кроме того, для получения событий приемник событий должен реализовывать IDispatch или сам интерфейс приемника событий (в Internet Explorer 6.0 для приема событий вы, как правило, будете применять метод IDispatch).
При каждом запуске события подключаемый объект вызывает IDispatch::Invoke (если вы реализуете сам интерфейс приемника событий, подключаемый объект может напрямую вызывать методы приемника событий).
Этот метод возвращает «cookie», который должен быть использован при разрыве соединения. Когда клиент хочет разорвать связь с подключаемым объектом, он вызывает метод Unadvise и передает «cookie», который он получил при вызове Advise.
Несколько слов о других методах. GetConnection-PointContainer позволяет получить указатель на интерфейс IConnectionPointContainer для подключаемого объекта данной точки подключения. GetConnec-tionlnter-face возвращает IID исходящего интерфейса, управляемого конкретной точкой подключения. Данный метод позволяет клиенту преобразовывать указатель на интерфейс IConnectionPoint в IID. Этот IID определяет точный интерфейс, на который указывает интерфейс IConnectionPoint.
Прием событий
Существует несколько способов приема событий — в зависимости от инструмента разработки, применяемого для создания приложения в Microsoft Visio 2002.
Прием событий в Microsoft Visio 2002, созданном с помощью Visual Basic, будет существенно отличаться от приема событий в приложении, написанном на C++ (и, естественно, будет осуществляться гораздо проще). Даже при использовании Active Template Library (ATL), Microsoft Foundation Classes (MFC) или обычного C++ способы приема событий будут различны.
Прием событий в Microsoft Visio 2002
Dim WithEvents IE As InternetExplorer
Убедитесь, что вы установили ссылку на shdocvw.dll в своем проекте Microsoft Visio 2002. В случае, если вы не сделали этого, Visual Basic не сможет довести такую ссылку до Internet Explorer. Далее, просто создайте экземпляр этого сервера, применяя зарезервированное слово New или метод CreateObject.
Когда вы создаете экземпляр сервера, Visual Basic автоматически инициализирует приемник событий и управляет им — все очень просто. Вам не придется беспокоиться о получении указателя на контейнер точек подключения (connection point container), а затем — о поиске необходимой точки подключения. Visual Basic сделает это за вас.
Понятно, что введя код создания сервера, нужно вставить вызовы методов, которые будут обрабатывать запускаемые этим сервером события. Вы можете определить, какие события поддерживаются объектом, заглянув в окно Procedures/Events.
Каждый раз, когда Internet Explorer 4.0 запускает событие, в текстовое окно помещается описание этого события. Создать такой код несложно. Сначала с использованием зарезервированного слова WithEvents задается величина переменной, представляющей экземпляр Internet Explorer. Затем создаются функции-обработчики событий (event handler function) для всех событий, которые вас интересуют. Этот пример поддерживает только некоторое подмножество событий. Вы можете по своему усмотрению добавлять функции-обработчики событий для любых других событий.
Управление событиями в «комбобоксе»
Две проблемы могут приключиться, когда смущенный юзер ползает по комбобоксу при помощи мышки вверх и вниз, а затем нажатием на Enter делает свой юзерский выбор. Во-первых, нажатие на серую стрелочку вызывает два события: Change и Click. Во-вторых, нажатие на Enter перемещает фокус к следующему элементу формы, тогда как нажатие на кнопку мыши не вызывает подобного эффекта (то есть фокус остается на комбобоксе). Поэтому, если ваш код помещен в секцию события Change, то на стрелочки вверх/вниз (клавиатурой) вызовет это событие, чего вы, естественно, не хотите. Напротив, если вы помещаете свой код только в секцию события Lost Focus и юзер щелкает мышью на своем выборе, то фокус не уйдет из комбобокса, а юзер будет созерцать текст, который он выбрал своей мышью, и думать, почему это ничего не происходит. Нижеприведенное решение «фильтрует базар» событий Click, генерирующихся нажатиями на стрелочки клавиатуры, и вынуждает контрол потерять фокус. В секции Declarations формы введите следующее:
Dim bNoise as Boolean
bNoise = False
Private Sub cbTest_KeyDown(KeyCode As_
Integer, Shift As Integer)
If KeyCode = vbKeyDown Or KeyCode_
= vbKeyUp Then bNoise = True
End Sub
Private Sub cbTest_Click()
If bNoise Then
Ignore Noise events
(up or down arrow)
bNoise = False
Else
SendKeys «{TAB}», True
End If
End Sub
Теперь вам остается написать код, содержащий реакцию на выбор юзера, и занести его в секцию события LostFocus комбика.
Комментирование и раскомментирование блоков кода
VB позволяет вам разом закомментировать целый блок кода, а затем также быстро раскомментировать его. Это очень полезно при отладке, когда вам не нужно исполнять целый ряд операторов, и в то же время вы не можете их удалить вот так вот просто за здорово живешь. Между тем, пара кнопок Comment/Uncomment присутствует только в меню Edit, который надо специально вызывать. Для того, чтобы быстро вызвать меню Edit, кликните правой кнопкой мыши на любом меню в VB, и выберите затем команду Edit.
Значения по умолчанию для необязательных параметров
В случае, если вы когда-либо программили на VB4, то вы возможно пользовались мощным инструментом под названием Необязательные параметры (Optional parameters). VB 6.3 пошел еще дальше: теперь эти параметры могут быть любого типа (не только Variants), и могут появляться в процедурах Property. Вы можете теперь задавать для них значение по умолчанию.
Property Get Value
(Optional index As Long =1)
End Property
Вы можете теперь делать это без бывшего ранее обязательным (и очень медленным) тестом IsMissing:
Property Get Value
(Optional index As Long)
If IsMissing(index) Then index = 1
End Property
Центрирование формы на экране
Все знают о коде, позволяющем вам центрировать форму на экране вне зависимости от графического разрешения. Теперь вы можете достичь того же результата, всего лишь присвоив значение vbStartUpScreen (=2) новому свойству StartUpPosition формы. Вы даже можете отцентрировать форму относительно ее родительского окна, присвоив значение vbStartUpOwner (=1). Присвоение можно сделать в окне Ргореrtу соответствующей формы. Когда вы центрируете форму внутри родительского окна, не забудьте добавить второй аргумент в методе Show.
Form2.Show vbModal, Me
He все шаблоны созданы одинаково
В отличие от других продуктов Office XP, шаблоны Word 2002 содержат business-application engine, который хранится отдельно от документов, использующих этот engine. Основанные на шаблонах книги Excel и презентации PowerPoint хранят в себе шаблоны, на основе которых они созданы. На практике, все документы Word состоят из 2х VBA проектов: первый проект создан на базе основного (оригинального, хранящегося в Word) шаблона (все документы Word основаны на шаблонах), а второй проект принадлежит самому документу Word. С другой стороны, книги Excel и презентации PowerPoint, созданные на шаблонах, содержат только один VBA проект. Каждый файл содержит свою собственную копию проекта оригинального шаблона. Изменения, производимые в этом шаблоне, не затрагивают основной шаблон, хранящийся в приложении.
Настройка меню в VB
Вот несколько предложений по настройке IDE в Visual Basic 6.3.
Добавить закладки в панель инструментов можно, кликнув правой кнопкой мыши на кнопке General и выбрав Add Tab.
Вы можете также перемещать и удалять закладки, и перемещать иконы контролов с одной закладки на другую, используя обычный метод drag-and-drop.
Вытащить кнопку любого пункта меню на тулбар можно, кликнув правой кнопкой на любом тулбаре и выбрав пункт Customize. Перейдите на закладку Commands, выберите нужный пункт меню в правом окошке и перетащите его на тулбар. Первыми кандидатами на добавление являются пункты Project-References, Project-Properties и Tools-Add Procedure.
Как создать совершенно новый тулбар на вкладке Toolbars диалогового окна Customize. После того, как вы определили содержимое будущего тулбара, для добавления кнопок на этот тулбар используйте описанную абзацем выше процедуру. Когда у вас на экране активизировано диалоговое окно Customize, кликните правой кнопкой на любой кнопке тулбара и вы сможете поменять рисунок кнопки, создать разделитель, спрятать/показать текст и т.д.
Как спрятать все окошки проекта
Когда вы работаете с несколькими проектами сразу, можно запутаться в нагромождении окошек из разных проектов. Однако, вы можете временно спрятать все окошки, относящиеся к данному проекту, щелкнув по пиктограмме проекта в окошке Project Explorer Так, чтобы все ветви, торчащие из него, исчезли. Тогда же свернутся и все окна, относящиеся к данному проекту. Эту возможность можно отменить, щелкнув на сответствующем квадратике на закладке General в меню Tools-Options.
Использование OBJECT BROWSER для нахождения недокументированных возможностей
В случае, если кликнуть правой кнопкой мыши в правом окошке Object Browser'a (там, где нарисованы члены классов), то выскочит контекстное меню с командой Show Hidden Members. В случае, если щелкнуть на этой команде, то отныне Object Browser будет показывать все hidden-свойства и методы (а также и классы) любой библиотеки, и вы можете использовать это для более детального исследования библиотек объектов.
Например, в библиотеке VBA есть hidden-класс под названием _HiddenModule, в который входят многие известные функции VBA плюс три недокументированные: ObjPtr, StrPtr и VarPtr. ObjPtr возвращает адрес private area экземпляра объекта, StrPtr возвращает адрес первого символа в строке, VarPtr возвращает адрес переменной или дескриптора строки (string descriptor, если имеем случай переменной типа string).
Адрес переменной
В Visual Basic 6.3 есть встроенная функция VarPtr.
Runtime library в VB4 включает эту функцию, но перед использованием ее нужно сначала объявить:
#If Win16 Then
Declare Function VarPtr Lib «VB40016.DLL» (variable As Any) As
Long
#Else
Declare Function VarPtr Lib «VB40032.DLL» (variable As Any) As
Long
#End If
Эта функция полезна при передаче пользовательских типов (Туре structure) во внешнюю процедуру API, и в этом типе какое-либо из полей является адресом другой переменной или записи.
Когда BENCHMARK (измерения скорости работы программы) длятся сутками
Обычно, программисты на Visual Basic 6.3 измеряют скорость выполнения кода при помощи Timer-функции. Однако, если ваша программа должна завершиться на следующий день, то вы должны учесть, что значение, возвращаемое функцией Timer, ресетится в полночь. В случае, если же вас устроит значение с точностью до одной секунды, вы можете упростить ваш код с помощью функции Now.
Dim startTime As Date
StartTime = Now
' the code to be benchmarked
Print «elapsedSeconds = « & Format$ ((Now - startTime) * 86400, «#####»)
Вам понадобится функция Format$ для округления результата до целого.
АРР.РАТН может возвращать UNC-пути
Visual Basic 6.3 может возвращать UNC-путь, типа \\server\pro-grams\..., в зависимости от обстоятельств, оттого как запущена программа и запущена она из Visual Basic 6.3 IDE или скомпилирована в ЕХЕ-файл. Эта особенность может сильно испортить вам жизнь, если вы используете App.Path для установки текущего каталога при старте программы.
ChDrive App.Path
ChDir App.Path
Поскольку ChDrive на умеет обрабатывать UNC-пути, этот код может вызвать фатальную ошибку времени выполнения, но может быть защищен использованием On Error Resume Next. Однако это не защитит вас от всех невзгод, могущих произойти. Наилучшее решение состоит в том, чтобы предоставить юзеру самому ввести каталог во время исполнения программы, затем записать полученный путь в регистр или INI-файл.
«Ускорьте» ваш код использованием CHOOSE
бы можете использовать Choose там, где можно заменить массив или построить таблицы результатов, на стадии компиляции (compile-time), вместо того, чтобы делать это на стадии выполнения (ran time). Например, если вам надо знать значения факториалов чисел от 1 до 10, попробуйте следующий пример (Choose производит выбор факториала из набора имеющихся значений вместо того, чтобы высчитывать факториал каждый раз заново):
Function Factorial(number As Integer)
As Long
Factorial = Choose(number, 1, 2, 6, _
24, 120, 720, 5040, 40320,_
362880, 3628800)
End Function
«ARRAY» — отныне это ошибочное имя для переменных
В случае, если вы часто используете имя array для переменных, вам придется пересмотреть ваш код при переносе его под Visual Basic 6.3. Это слово является теперь зарезервированным (reserved keyword) и не может быть использовано в качестве имени переменной. Вы можете легко переделать ваш код при помощи команды Replace в IDE Visual Basic 6.3, не забудьте при этом черкнуть Find whole words only.
Запуск AUTOMATION MANAGER как HIDDEN-задачи
В случае, если вы используете OLE Remote Automation, вы должны заранее запустить Automation Manager на сервере до того, как случится первая OLE remote communication. По умолчанию, это приложение visible, но вы можете его спрятать. Для этого создайте ярлык для Automation Manager, который бы включал в командной строке переключатель /Hidden:
C:\Windows\System\AutMgr32.Exe/Hidden
С другой стороны, вы можете поменять значение соответствующего ключа в регистре.
Использование коллекции для отфильтровывания дублированных значений
Этот код иллюстрирует, как использовать коллекции (Collection) для генерации уникального набора величин из набора, содержащего дубликаты.
В этом примере сканируется массив строк и сортируются все уникальные с использованием list-box контрола:
Sub Remove_Duplicates(arr() As String)
Dim i As Lon
g Dim RawData As String
Dim DataValues As New Collection
On Error Resume Next
Это вставлено для игнорирования ошибки 457 — Duplicate key.
For i = LBound(arr) To UBound(arr)
RawData = arr(i)
DataValues.Add RawData, RawData
В случае, если Run-time error 457 случилась, то повторяющееся значение игнорируется.
Next
On Error GoTo 0
' Сохранение в List Box
' (свойство Sorted выставлено True)
IstSortedData.Clear
For Each DataValue In DataValues
IstSortedData.Addltem DataValue
Next
End Sub
Запись текущей позиции и размера формы при помощи SAVESETTING
Функции SaveSetting и GetSetting облегчают написание сеттингов. Эти две функции восстанавливают и запоминают текущие позиции формы:
Public Sub FormPosition_Get(F As Form)
' Считывает позицию формы F из
' ini/reg файла и соответственно
1 позиционирует форму
Dim buf As String
Dim 1 As Integer, t As Integer
Dim h As Integer, w As Integer
Dim pos As Integer
buf = GetSetting<app.EXEName,
«FormPosition», F.Tag, «»)
If buf = «» Then
' defaults для центрирования фромы
F.Move (Screen.Width - F.Width) \ _
2, (Screen.Height - F.Height) \ 2
Else
' выделить l,t,-w, h и выставить форму
pos = InStr(buf, «,»)
1 = CInt(Left(buf, pos - 1))
buf = Hid(buf, pos + 1)
pos = InStr(buf, «,»)
t = CInt(Left(buf, pos - 1))
buf = Mid(buf, pos + 1)
pos = IhStr(buf, «,»)
w = CInt(Left(buf, pos - 1))
h =' CInt(Mid(buf, pos + 1))
F.Move 1, t, w, h
End If
End Sub
Public Sub FormPosition_Put(F As Form)
' Пишет op,left,height и
' width позиции формы F в reg/ini файл аппликухи
Dim buf As String
buf = F.left & «,» & F.top & «,» &
F.Width & «,» & F.Height
SaveSetting app.EXEName,_
«FormPosition», F.Tag, buf
End Sub
Вам следует поместить эти процедуры в модуль и вызывать их из событий Load и Unload форм. Вы должны написать имя формы в ее свойство Tag, чтобы эти процедуры работали корректно:
Sub Form_Load()
FormPosition_Get Me
End Sub
Sub Form_Unload()
FormPosition_Put Me
End Sub
Зашифрованные пароли
Следующие две функции легко и эффективно шифруют/дешифруют текстовый пароль. Функции имеют два аргумента: число от 1 до 10 чтобы сдвигать позицию символа ASCII в пароле, и собственно строка пароля. Функция EncryptPassword проходит через каждый символ строки DecryptedPassword, проверяет символ на четность/нечетность, и сдвигает его вверх/вниз согласно параметру Number. Эту делает зашифрованную строку нечитабельной. Зашифрованный пароль «укатывается» затем оператором XOR, который еще более запутывает строку. В приведенном ниже коде ограничен параметр Number числом 10, поскольку не надо делать проверку на «неправильные» символы ASCII. Функция DecryptPassword повторяет в обратном порядке процесс шифрования, применяя XOR, а затем сдвиг.
Function EncryptPassword(Number As
Byte, DecryptedPassword As String)
Din Password As String, Counter As Byte
Dim Temp As Integer
Counter =1
Do Until Counter =_
Len(DecryptedPassword) + 1
Temp = Asc(Mid(DecryptedPassword, _
Counter, 1))
If Counter Mod 2 = 0 Then
'see if even
Temp = Temp - Number
Else
Temp = Temp + Number
End If
Temp = Temp Xor (10 - Number)
Password = Password & Chr$(Temp)
Counter = Counter + 1
Loop
EncryptPassword = Password
End Function
Function DecryptPassword(Number As _
Byte, EncryptedPassword As String)
Dim Password As String, Counter As Byte
Dim Temp As Integer
Counter = 1
Do Until Counter = _
Len(EncryptedPassword) + 1
Temp = Asc(Mid(EncryptedPassword,_
Counter, 1)) Xor (10 - Number)
If Counter Mod 2=0 Then 'see if even
Temp = Temp + Number
Else
Temp = Temp - Number
End If
Password = Password & Chr$(Temp)
Counter = Counter + 1
Loop
DecryptPassword = Password
End Function
Отслеживание DOUBLE CLICK для кнопок на «тулбаре»
Visual Basic 6.3 поддерживает встроенный в Windows XP контрол Toolbar, позволяющий пользователям добавлять кнопки на Тулбар. У этих кнопок есть событие ButtonClick, но если вы хотите отлавливать double-click, то стандартного события ButtonDoubleClick нет. Для того, чтобы исправить это, объявите две переменные уровня формы:
Private mbSingleClicked As Boolean
Private mbDoubleClicked As Boolean
In the Toolbars ButtonClick event, add this code:
В событии ButtonClick Тулбара добавьте следующий код:
Private Sub Toolbar1_ButtonClick_
(ByVal Button As Button)
Dim t As Single
t = Timer
If mbSingleClicked = True Then
mbDoubleClicked = True
MsgBox «Double Clicked»
Else
mbSingleCHcked = True
' позволить юзеру кликнуть еще раз, если он хочет дабл-кликнуть
Do While Timer - t < 1 And mbSingleClicked = True
DoEvents
Loop
' если юзер сделал DoubleClick, выйти из процедуры
If mbDoubleClicked = True Then
mbSingleCHcked = False
mbDoubleClicked = False
Exit Sub
End If
End If
If mbDoubleClicked = False Then
MsgBox «Single Clicked»
End If
'пример обработки этих событий
'If mbDoubleClicked Then
'------- code
'Elself mbSingleClicked Then
------ Code
End.If
'при выходе из процедуры надо реинитить переменные, иначе мы упремся в SingleClickи
If mbDoubleClicked = False Then
mbSingleClicked = False
mbDoubleClicked = False
End If
End Sub
Объем каталога в байтах
Эта функция возвращает число байт, занятых файлами в каталоге:
Function DirUsedBytes(ByVal dirName As
String) As Long
Dim FileName As String
Dim FileSize As Currency
' добавить \, если не было
If Right$(dirName, 1) <> «\» Then
dirName = dirName & «\»
Endif
FileSize =0
FileName = Dir$(dirName & «*.*»)
Do While FileName <> «»
FileSize = FileSize + _
FileLen(dirName & FileName)
FileName = Dir$
Loop
DirUsedBytes = FileSize
End Function
Пример вызова такой функции:
MsgBoxDirUsedBytes(«C:\Windows»)
Как сделать имитацию нажатия клавиши CTRL для выделения несвязанных кусков в LIST BOX
Когда свойство MultiSelect обычного listboxa установлено в 1 — Simple или в 2 — Extended, то юзеру надо жать Ctrl при кликании внутри этого listboxa, чтобы выделять несвязанные (не идущие подряд) элементы. Следующий метод позволяет пользователю выбирать несколько элементов, не нажимая при этом Ctrl. Поместите нижеприведенный код в модуль.
Declare Function GetKeyboardState Lib _
«user32» (pbKeyState As Byte)_
As Long
Declare Function SetKeyboardState Lib _
«user32» (IppbKeyState As Byte)
As Long
Public Const VK_CONTROL = &H11
Public KeyState(256) As Byte
Этот код поместите в событие MouseDown вашего listboxa (назовем его Listl), у которого свойство MultiSelect установлено в Simple или Extended:
' «нажимает» Ctrl
GetKeyboardState KeyState(O)
KeyState(VK_CONTROL) = _
KeyState(VK_CONTROL) Or &H80
SetKeyboardState KeyState(0)
Этот код поместите в процедуру, в которой надо «отжать» Ctrl, к примеру, Listl_LostFocus:
' «отжимает» Ctrl
GetKeyboardState KeyState(O)
KeyState(VK_CONTROL) = _
KeyState(VK_CONTROL) And &H7F
>SetKeyboardState KeyState(0)
Имя текущего компьютера
Часто вам надо знать имя текущего компьютера под Windows XP из вашей VB-программы. Используйте эту простенькую функцию API из kernel32.dll:
Private Declare Function GetComputerNameA Lib
«kerne!32»_
(ByVal IpBuffer As String, nSize _ As Long) As Long
Public Function GetMachineNameO As _ String
Dim sBuffer As String * 255
If GetComputerNameA(sBuffer, 2554) _ <> 0 Then
GetMachineName = Left$(sBuffer, _ InStr(sBuffer,
vbNullChar) _ - 1)
Else
GetMachineName = «(Not Known)»
End If
End Function
Перехват правых кликов на узлах TREEVIEW
Событие Treeview_MouseDown происходит до события NodeClick. Для того, чтобы показать контекстное меню над узлом, используйте этот код и определите ключ (Key) для для каждого узла в виде буквы и идущим за ней числом.
+ Root (R01) ' the letter gives
|— Child 1 (C01) ' the indication to
|--+ Child 2 (C02) ' the context menu
| |--- Child 2.1 (H01)
| |--- Child 2.2 (H02)
Dim bRightMouseDown as Boolean
Private Sub Form_Load()
bRightMouseDown = False
End SubPrivate Sub treeview1_MouseDown_
(Button As Integer, Shift As _
Integer, X As Single, Y As Single)
If Button And vbRightButton Then
bRightMouseDown = True
Else
bRightMouseDown = False
End If
End Sub
Private Sub treeview1_MouseUp_
(Button As Integer, Shift As
Integer, X As Single, Y As Single)
bRightMouseDown = False
End Sub
Private Sub treeview1_NodeClick_
(ByVal Node As Node)
Select Case Left(Node.Key, 1)
Case «R»
If Not bRightMouseDown Then
1 do the normal node click,
' so you must here the code
'for the node code click
Else
' выбор узла
treeviewl.Nodes(Node.Key).Selected = True
' показать контекстное меню
PopupMenu mnuContext1
End If
Case «C»
If Not bRightMouseDown Then
' do the normal node click,
' so you must here the code
' for the node code click
Else
' выбор узла
treeviewl.Nodes(Node.Key).Selected = True
' показать контекстное меню
PopupMenu mnuContext2
End If
' то же с остальными узлами
End Select End Sub
«Горяченькие кнопочки»
Как получить USERID
Часто вам надо получить userID текущего юзера, работающего с вашей программой. Используйте для этого модификацию одной из функций API:
Option Explicit
Private Declare Function WNetGetUserA
Lib «mpr» (ByVal IpName As String,
ByVal IpUserName As String,
IpnLength As Long) As Long
Function GetUserQ As String
Dim sUserNameBuff As String * 255
sUserNameBuff = Space(255)
Call WNetGetUserA(vbNullString,
sUserNameBuff, 255&)
GetUser = Left$(sUserNameBuff,
InStr(sUserNameBuff,
vbNullChar) - 1)
End Function
Вывод песочных часов во время обработки данных
Нижеуказанная методика упрощает переключение MousePointer, без добавления специального кода в конце каждой процедуры/функции. Когда вы создаете объект из какого-либо класса, генерируется событие Initialize. Затем исполняется код соответствующей процедуры. Это первый код, исполняемый для данного объекта, он исполняется до присвоения каких-либо свойств объекту и до выполнения методов объекта. Когда переменная выходит из области видимости, все ссылки на объект уничтожаются, и выполняется код для события Terminate.
Declare Sub Sleep Lib «kernel32»
(ByVal dwMilliseconds As Long)
' пример процедуры, использующей класс CHourGlass
Private Sub ProcessDataQ
Dim MyHourGlass As CHourGlass
Set MyHourGlass = New CHourGlass
' здесь вставляется код обработки данных
Sleep 5000 ' Это моделирует обработку данных
' продолжение кода
End Sub
' создание класса CHourGlass:
Private Sub Class_Initialize()
1 Показать HourGlass
Screen.MousePointer = vbHourglass
End Sub
Private Sub Class_Terminate()
' Восстановить MousePointer
Screen.MousePointer = vbDefault
End Sub
Быстрый «обсчет» многочленов
Хорошо известная формула Горнера позволяет быстро считать полиномиальные выражения. Для того, чтобы посчитать
A*x^N + B*x^(N-l) + ... + Y*x + Z
( ^ означает степень ), напишите :
(...((А*х + В)*х + С)*х + ... +Y)*x + Z.
Последовательные номера версий
Для слежения за последовательностью версий, используйте эту процедуру, если вы используете номер версии:
Public Function GetMyVersionO As String
' конвертирует номер версии в нечто вроде»1.02.0001»
Static strMyVer As String
If strMyVer = «» Then
strMyVer = Trim$(Str$(App.Major)) & «.» &
Format$(App.Minor, «##00»)
& «.» Format$(App.Revision, «000»)
End If
GetMyVersion = 'strMyVer
End Function
Изменение размера выпадающей области на СОМ-ВОВОХ
В Visual Basic 6.3 нет свойства ListRows, то есть если вам надо изобразить более чем восемь дефолтовых строк на выпадающем списке comboboxa, то используйте эту процедуру для увеличения размера окна comboboxa:
Option Explicit
Type POINTAPI
x As Long
у As Long
End Type
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Declare Function MoveWindow Lib
«user32» (ByVal hwnd As Long, _
ByVal x As Long, ByVal у As Long, _
ByVal nWidth As Long, _
ByVal nHeight As Long,
ByVal bRepaint As Long) As Long
Declare Function GetWindowRect Lib _
«user32» (ByVal hwnd As Long,
IpRect As RECT) As Long
Declare Function ScreenToClient Lib _
«user32» (ByVal hwnd As Long, _
IpPoint As POINTAPI) As Long
Public Sub Size_Combo(rForm As Form,
rCbo As ComboBox)
Dim pt As POINTAPI
Dim rec As RECT
Dim iltemWidth As Integer
Dim iltemHeight As Integer
Dim iOldScaleMode As Integer
' Смена Scale Mode формы на Pixels
iOldScaleMode = rForm.ScaleMode
rForm.ScaleMode = 3
iltemWidth = rCbo.Width
' Установка новой высоты comboboxa
iltemHeight = rForm.ScaleHeight - rCbo.Top - 5
rForm.ScaleMode = iOldScaleMode
' Получение координат по отношению к экрану
Call GetWindowRect(rCbo.hwnd, rec)
pt.x = rec. Left
pt.y = rec.Top
' затем координаты в форме
Call ScreenToClient(rForm.hwnd, pt)
' Изменение размера comboboxa
Call MoveWindow(rCbo.hwnd, pt.x,
pt.y, iltemWidth, iltemHeight, 1)
End Sub
Сколько вам лет?
Эта функция возвращает разницу между двумя датами в годах, месяцах и днях:
Function GetAge(dtDOB As Date,
Optional dtDateTo As Date = 0)
As String
' dtDateto передана?
If dtDateTo = 0 Then
dtDateTo = Date
End If
GetAge = Formats(dtDateTo -dtDOB, «yy - mm - dd») End Function
Создать на лету массив при помощи функции ARRAY
Метод GetRows копирует строки Recordset (JET) или rdoResultset (RDO) в массив. Этот метод использует переменную типа Variant в качестве параметра для хранения возвращаемых данных. Это двумерный массив (по внутреннему представлению VB):
Dim A As Variant
А = Array(10,2)
Упаковка значений CHECK-BOX в одну переменную типа INTEGER
Используя следующий код, можно вывести двоичное представление зачеркнутых check-box:
Function WhichCheck(ctrl As Object) As _
Integer
Эта функция возвращает двоичное представление массива контро-лов, где каждый зачеркнутый чекбокс представляется двойкой в степени своего индекса в массиве, к примеру, элемент 0 : 2^0 = 1, элементы 0 и 2 : 2^0 + 2^2 = 5
Dim i
Dim iHolder
' если некорректный параметр передан в процедуру
' возвращается О
On Error GoTo WhichCheckErr
' двоичное представление
' массива чекбоксов
For i = ctrl.LBound To ctrl.UBound
If ctrl(i) = 1 Then
' если зачеркнут, добавить его двоичное представление
iHolder = iHolder Or 2 " i
End If
Next
WhichCheckErr:
WhichCheck = iHolder
End Function
Функция вызывается следующим образом:
iCurChecked = WhichCheck(Checkl)
Checkl — массив чекбоксов, iCurChecked — переменная integer. Ниже приведена «двойственная» процедура, устанавливающая все чекбоксы согласно переменной, в которой хранятся их двоичные представления.
Sub SetChecked(ctrl As Object,_
ICurCheck%)
' This sub sets the binary value of an
' array of controls where iCurChecked is
' 2 raised to the index of each checked
' control
Dim i
in case Ctrl is not a valid object
On Error GoTo SetCheckErr
' use the binary representation to
' set individual check box controls
For i = ctrl.LBound To ctrl.UBound
If iCurCheck And (2 " i) Then
' if it is checked add in its
' binary value
ctrl(i).Value = 1
Else
ctrl(i).Value = 0
End If
Next
SetCheckErr:
End Sub
Эта процедура вызывается так:
Call SetChecked(Check1, iDesired)
Checkl — массив чекбоксов, iDesired — переменная, хранящая двоичное представление состояния чекбоксов.
Условная компиляция кода
Вы можете объявлять процедуры Windows API для 16- или 32-разрядных операционных систем при использовании Conditional Compilation из Visual Basic 6.3:
#If Win#32 then
' если 32-разрядная ОС
Declare SomeApi....
#Else
' если запущена 16-разрядная ОС
Declare SomeApi
#End IF
Это же может работать не только с функциями Windows API, но и с вашими собственными функциями:
#If Win32 Then Dim lRc&
lRc& = ReturnSomeNumber(35000) «Else
Dim IRcK
IRcX = ReturnSomeNumber(30000)
#End If
#If Win32 Then
Private Function ReturnSomeNumber_
(lVar&) As Long
ReturnSomeNumber = 399999
#Else
Private Function ReturnSomeNumber_
(IVarS!) As Integer
ReturnSomeNumber = 30000
«End If
End Function
Уменьшить мерцание во время загрузки формы
Во время загрузки формы, следующий код поможет уменьшить мерцание и мелькание GUI при помощи функций API:
'Declarations Section
#If Win32 Then
Declare Function LockWindowUpdate _
Lib «user32»_
(ByVal hwndLock As Long) As Long
#Else
Declare Function LockWindowUpdate
Lib «User»_
(ByVal hwndLock As Integer)
As Integer
#End If
Public Sub LoadSomeForm()
' Во время загрузки формы запрещает обновление состояния окна
' чтобы избавиться от мерцания.
' запрещаетобновление
GUI LockWindowUpdate frmTest.hWnd
' показывает форму frmTest.Show
' здесь код, относящийся к загрузка формы и т.п.
' Никогда не забывайте разрешить обратно обновление окна
LockWindowUpdate 0
End Sub
Спрятать указатель на текущую запись в DBGride
Для того, чтобы указатель записи на DBGride не скакал при перемещении между записями (строками grida), используйте функцию API LockWindowUpdate(gridname.hwnd) перед началом движения по gridy, и LockWindowUpdate(0) после окончания перемещений:
'Declarations Section
#If Win32 Then
Declare Function LockWindowUpdate
Lib «user32»
(ByVal hwndLock As Long) As Long
#Else
Declare Function LockWindowUpdate
Lib «User» _
(ByVal hwndLock As Integer)
As Integer
#End If
Private Sub cmdHideSelector_Click()
LockWindowUpdate DBGridl.hWnd
End Sub
Private Sub cndShowSelector_Click()
LockWindowUpdate 0
End Sub
Как узнать разделители даты и времени без функции API
Вот простой алгоритм, как узнать разделители даты, времени и десятичной точки в Windows, не залезая в Locale Settings или функции API.
DateDelimiter = Mid$(Format(Date, _
«General Date»), 3, 1)
TimeDelimiter = Mid$(Format(0.5, _
«Long Time»), 3, 1)
DecimalDelimiter = Mid$(Format(1.1,
«General Number»), 2, 1)
Дублирование строк кода без синтаксических ошибок
Часто приходится переписывать сходный по смыслу код с небольшими изменениями в каждой строке; для облегчения проблемы можно сделать шаблон того, что надо копировать, быстро вставлять копию в нужное место, и делать добавления. Однако часто шаблонный текст вызывает ошибки со стороны Visual Basic 6.3 редактора. Одолеть эту проблему можно, закомментировав шаблон перед использованием. Когда вы закончите редактирование вставленного фрагмента, раскомментируйте его и он готов. Это особенно просто под Visual Basic 6.3, в котором есть команда Block Uncomment. Ниже приведен пример добавления члена в коллекцию.
While Not mRS.EOF
oObject.FName = mRSIFName
oObject.LName = mRSILName
oObject.Phone = mRS!Phone
cCollection.Add oObject, oObject.FName
Wend
В случае, если же у вашего объекта 20 или 30 свойств, быстрее будет создать шаблон:
' oObject. = mRS!
Скопируйте его, вставьте 20 или 30 раз, вернитесь к началу и впечатайте имена свойств и полей, и уберите символ комментария. Символ комментария позволяет вам свободно бегать по всему фрагменту, не заботясь о синтаксических ошибках.
Ярлык для загрузки последнего рабочего проекта в Visual Basic 6.3
Часто вы старуете Visual Basic 6.3 и возобновляете работу с последним проектом, но вам не хочется загромождать desktop иконками для текущих работ. В качестве решения предлагается программа, которую нужно скомпилировать и запустить на вашем desktope. Эту программу можно применить и к другим, использующим INI файлы.
Option Explicit
Declare Function GetPrivateProfile_
String Lib «kernel32»
Alias «GetPrivateProfileStringA»
(ByVal IpApplicationName As
String, ByVal IpKeyName As Any,
ByVal IpDefault As String,
ByVal IpReturnedString As
String, ByVal nSize As Long,
ByVal IpFileName As String)
As Long
Public Sub Main()
Dim temp As String, rVal$, tmp
As Long
rVal$ = String$(256, 0)
tmp = GetPrivateProfileString_
(«Visual Basic»,
«vb321ocation», «», rVal$,
ByVal Len(rVal$) - 1,
«c:\windows\vb. ini»)
temp = Left$(rVal$, tmp)
rVal$ = String$(256, 0)
tmp = GetPrivateProfileString_
(«Visual Basic», «RecentFilel»,
«», rVal$, ByVal Len(rVal$)
- 1, «c:\windows\vb.ini»)
temp = temp & « «»» & Left$(rVal$,
tmp) & «»»»
Shell temp, 1
End
End Sub
Создание временных файлов
При программировании баз данных можно создавать временные файлы для, к примеру, вывода результата инструкции SQL или из временной базы данных, чтобы более эффективно работать с записями. Функция FileAux возвращает имя временного файла. В случае, если надо создать несколько временных файлов одновременно, сохраните их имена в заранее определенных переменных:
Function FileAux(Ext As String)
As String
Din i As Long, X As String
If InStr(Ext, «.») = 0 Then
Ext = «.» + Ext
End If
' Ищем уже имеющиеся файлы на винте
i = О
Do
X = «Aux» + Format$(i, «0000»)
+ Ext
If FlleExists(X) Then
i = i + 1
Else
Exit Do
End If
Loop
FileAux = X
End Function
Эта функция обращается к функции FileExists:
Function FileExist(filename As String) _
As Boolean
FileExist = Dir$(filename) о «»
End Function
А вот пример использования:
Sub Test()
Dim Filel As String, File2 As
String, FileS As String
Dim DB1 As database, DB2 As DataBase
Dim FileNum As Integer
Filel = FileAux(«MDB»)
Set DB1 = CreateDataBase(Filel)
File2 = FileAux(«MDB»)
Set DB2 = CreateDataBase(File2)
File3 = FileAux(«TXT»)
FileNum = FreeFile
Open File3 For OutPut As FileNum
' Ваш код
Close FileNum
End Sub
Filel, File2 и File3 должны быть Aux0001.MDB, Aux0002.MDB и Aux0001.TXT соответственно.
Центрировать форму с учетом TaskBar
Для центрирования формы вам надо лишь вызвать API процедуру, и завести две константы. Это решение основано на том факте, что GetSystemMetrics возвращает истинное значение параметров экрана, который может быть на самом деле занят таскбаром и Microsoft Office shortcut barом:
Public Const SM_CXFULLSCREEN = 16
Public Const SM_CYFULLSCREEN = 17
«If Win32 then
Declare Function GetSystemMetrics
Lib «user32»
(ByVal nlndex As Long) As Long
#Else
Declare Function GetSystemMetrics
Lib «User»
(ByVal nlndex As Integer)
As Integer
#End If
Public Sub CenterForm(frm As Form)
frm.Left = Screen.TwipsPerPixelX *
GetSystemMetrics_
(SM_CXFULLSCREEN) / 2
- frm.Width / 2
frm.Top = Screen.TwipsPerPixelY *_
GetSystemMetrics_
(SM_CYFULLSCREEN) / 2_
- frm.Height / 2
End Sub
Очистка строки от ненужных символов
Иногда бывает полезно иметь функцию, которая очищает строку от нежелательных символов. Эта маленькая функция принимает в качестве параметров строку для очистки и символ, от которого ее надо очистить:
Function StringCleaner(s As String,
Search As String) As String
Dim i As Integer, res As String
res = s
Do While InStr(res, Search)
i = InStr(res, Search)
res = Left(res, i - 1) & _
Mid(res, i + 1)
Loop
StringCleaner = res
End Function
Добавление строки в TEXT BOX
Используйте данный код, чтобы заставить скроллер TextBox автоматически передвинуться, когда вы добавляете новый текст:
' Переход к концу текста
MyTextBox.SelStart = Len(MyTextBox.Text)
' Новый текст будет стоять здесь
MyTextBox.SelText = NewText$
Проверка аргументов в функции VAL
При использовании функции Val, Visual Basic 6.3 капризничает, порождая ошибку несоответствия типов. Например, Val(«25%») правильно возвращает 25, тогда как Val(«2.5%») неправильно интерпретирует входной параметр и возвращает ошибку несоответствия типов. Это случается только тогда, когда в строке присутствует десятичная точка и символ «%» или «&». Для того, чтобы исправить это, уберите эти символы из строки перед ее передачей в Val.
Ярлыки для Internet
Visual Basic 6.3 умеет создавать web-форму, но она работает только с Microsoft Internet Explorer и вам приходится таскать за собой SHDOCVW.DLL при распространении программы. В случае, если вы используете функцию ShellExecute для запуска файла Internet Shortcut, то Windows запускает дефолтный браузер и переходит на указанный URL. Этот метод работает как Microsoft так и с Netscape браузерами, если они правильно прописаны в регистре, и вам не нужно перетаскивать никаких DLL при распространении программы.
Private Declare Function ShellExecute
Lib «shell32.dll» Alias
«ShellExecuteA»
(ByVal hwnd As Long,
ByVal IpOperation As String,
ByVal IpFile As String,
ByVal IpParameters As String,
ByVal IpDirectory As String,
ByVal nShowCmd As Long) As Long
Private Const SW_SHOWNORMAL = 1
' frm : ShellExecute использует обработчик окна.
1 Вы можете использовать обработчик главного окна проги
' sUrl : это имя и путь к файлу .url (файл Internet shortcut)
' указывающий на Вашу страницу , напр.
' c:\MyWebPage.url использует Internet Explorer
1для создания файла ярлыка
Public Sub GoToMyWebPage(frm as Form,
sUrl as string)
Dim IRet as Long
IRet = ShellExecute(frm. hwnd,
«open», sUrl, vbNull,
vbNullString, SW_SHOWNORMAL)
If IRet <= 32 Then
' случилась ошибка. Некоторые из ошибок,
' возвращаемых ShellExecute:
' ERROR_FILE_NOT_FOUND = 2&
' ERROR_PATH_NOT_FOUND = 3&
1 ERROR_BAD_FORMAT = 11&
' SE_ERR_NOASSOC = 31
' SE_ERR_OOM = 8
Else
' если браузер запущен!
End If
End Sub
Просмотр содержания HELP-файла
Многие программисты любят добавлять к своим приложениям и хелп-файлы. Как открыть содержание help-файла Windows из вашей программы? Вот пример кода с использованием Win32 API функции.
' --- Объявление
Const HELP_CONTENTS = &H3&
' Функции Вывода содержимого
Declare Function WinHelp Lib «user32»
Alias «WinHelpA»
(ByVal hwnd As Long,
ByVal IpHelpFile As String,
ByVal wCommand As Long,
ByVal dwData As Long) As Long
' --- Код
Sub OpenHelpFile(HelpFileName As String)
' HelpFileNane - путь к хелп-файлу.
WinHelp hwnd, HelpFileName,
HELP_CONTENTS, 0
End Sub
Быстрый поиск в базе данных
В Visual Basic 6.3 нет встроенной процедуры типа DLookUp из Access. Вы можете использовать -нижеприведенный код для получения Name объекта по его ID:
Public Function MyDLookUp(Column As
String, TableName As String,
Condition As String) As Variant
Dim Rec As Recordset
On Error GoTo MyDlookUp_Err
' gCurBase - глобальная переменая, указывающая на текущкю БД
Set Rec = gCurBase.OpenRecordset_
(«Select * From « & TableName)
Rec. FindFirst Condition
If Not Rec.NoMatch Then
' возвращает искомое поле, если найдено
MyDLookUp = Rec(Column)
Exit Function
End If
' возврат, если не найдено, или произошла другая ошибка
MyDlookUp_Err:
MyDLookUp = -1
End Function
Легкое отслеживание положения фокуса
Lost_Focus и Got_Focus events часто используются для проверки правильности ввода текста. Вы можете использовать нижеприведенный код для отслеживания фокуса на форме, не программируя каждый контрол отдельно.
Поместите timer control на форму, установите Interval property = 100 и Enabled = True.
Name the control tmrFocusTracking.
Timer event должен содердать следующий код:
Private Sub tmrFocusTracking_Timer()
Dim strControlName As String
Dim strActive As String
strControlName =
Me.ActiveCont rol.Name
Do
strActive = Me.ActiveControl.Name
If strControlName <> strActive
Then
Print strControlName &
« - Lost Focus»,
strActive & « - Got Focus»
strControlName = strActive
End If
DoEvents Loop
End Sub
To implement universal highlighting, replace the Print statement
with this code:
Me.Controls(strActive).SelStart = 0
Me.Controls(strActive).SelLength = _
Len(Me.Controls(strActive))
Для проверки (validation) правильности текста вместо Print statement используйте вызов процедуры проверки.
К моменту, когда случается команда Print, strActive равен контролу, имеющему фокус, и StrControlName содержит имя контрола, который потерял фокус.
Не размещайте эту процедуру где-либо кроме таймера.
Незакрывающаяся форма
В случае, если выставить свойство ControlBox на форме в False, то кнопки Minimize и Maximize тоже исчезнут. Предположим, что вы хотите тем не менее давать возможность юзеру использовать кнопки Minimize и Maximize, но при этом чтобы он не мог закрыть форму кнопкой с крестиком. Добавьте следующий код в событие Query_Unload:
' если у Вас VB3, раскомментируйте следующую строку
' Const vbFormControlMenu = 0
Private Sub Form_Queryl)nload(Cancel As
Integer, UnloadMode As Integer)
If UnloadMode = vbFormControl_
Menu Then
Cancel = True
End If
End Sub
Как просто отформатировать и округлить число
Пример округления с заданной точностью:
n = 12.345
Format(n, «0.00\0»)
' возвращает «12.350»
Format(n, «O.\0\0»)
' возвращает «12.00»
Format(0.55, «#.0\0») ' возвращает «.60»
Будьте осторожны, здесь вам не С!
VB программеры, привыкшие к С, могут быть введены в заблуждение следующей особенностью VB. Рассмотрим код:
Dim x As Integer
Dim y As Integer
Dim z As Integer
x = 10
у = 20
z = 0
' пусть функция max возвращает большее из двух чисел
if (z = max(x, у)) > 0 then
Msgbox CStr(z)
Else
Msgbox «How Come?»
End if
Вы ожидаете, что высветится 20, как должно бы было произойти в С? Однако, Visual Basic 6.3 сравнит z с RHS (right-hand side, правой стороной), даже перед присвоением, независимо от скобок. Будьте внимательны.
Использовать BACKQUOTES вместо апострофов
Часто при использовании Transact-SQL надо перехватывать комментарии пользователя из текстбокса и пересылать их в базу данных. Однако, если юзер нажимает апостроф в текстбоксе, происходит ошибка времени выполнения, поскольку SQL Server использует апостроф как признак конца строки. Для того, чтобы обойти эту проблему, перехватите ввод юзера в событии KeyPress и замените апостроф на вот такую кавычку «'»(ASCII(145)):
Private Sub Text1_Keypress_
(KeyAscii as Integer)
If KeyAscii = 39 Then
KeyAscii = 145
End If
End Sub
Также можно заменить все одинарные кавычки на «'» перед отсылкой в SQL Server.
Распространение новых версий программы по сети
Довольно трудно своевременно уследить за распространением каждой новой версии программы на всех машинах, поэтому необходимо использовать автоинкрементирующуюся нумерацию версий для проверки, требуется ли апгрейд программы на конкретной машине. При компиляции программы установите автоинкремент версий в On. Сохраните ваши setup/upgrade файлы на сетевом диске (настоятельно рекомендуем использовать UNC-пути (\\имя_машины\имя_лиска) нежели просто имена дисков), и положите INI-файл программы, в котором указан номер новейшей версии. Затем вставьте следующий код в программу, событие Form_Load:
Open IniFile$ For Input As #1
Line Input #1, sUpgradeVersion$
Close #1
If sUpgradeVersion > (Format(App.Major, «00») &«.»&_
Format(App.Minor, «00») & «.» &
Format(App.Revision, «0000»)) Then
1 запуск апгрейда с сетевого диска
End
End If
В случае, если версия в INI-файле выше, чем версия, записанная в .ехе, то программа автоматом запустит программу апгрейда по сети и закончит свое выполнение, то есть все нужные файлы смогут быть заменены. Это особенно полезно, когда вы только начинаете писать программу, то есть апгрейды появляются чуть ли не раз в несколько дней.
Закрыть окно программы, как это делает Windows XP
Разместите этот код в declaration section модуля:
Public Sub Win95Shrivel(xForm As Form)
' минимизирует окно xForm.WindowState = 1
End Sub
Вызывайте ее из процедуры Unload формы
Private Sub Form_Unload(Cancel As_
Integer)
Win95Shrivel Me
End Sub
Каждый раз при unloade формы она сначала быстро сворачивается к таскбару, а затем исчезает.
Excel VBA: Приёмы программирования
Как определить последнюю запись в таблице Excel?
Необходимо найти последнюю запись в электронной таблице. Это можно было бы организовать, к примеру, функцией Application.SpecialCells(xlLastCell).
Как из макроса Excel программно создать таблицу Access?
Вот фрагмент кода, который создаёт таблицу BalanceShifr базе данных MS Access:
<COLOR=»#990000»Hint: He забудьте выставить в Excel ссылки на объекты ОАО!
<COLOR=»#990000» [VBA] Tools/References/Available References/ [x] Microsoft DAO ?.? Library
' Function CreateTable
' Create temporary table «BalanceShifr» into temporary database
Public Function CreateTable(ByVal dbTemp As Database) As Boolean
Dim tdfTemp As TableDef
Dim idx As Index
Dim fid As Field
On Error GoTo errhandle
CreateTable = True
' CREATE TABLE «BalanceShifr»
Set tdfTemp = dbTemp.CreateTableDef(«BalanceShifr»)
Set fid = tdfTemp.CreateField(«ConditionId», dbLong)
fid.Required = True
tdfTemp.Fields.Append fid
Set fid = tdfTemp.CreateField(«Account», dbText, 4)
tdfTemp.Fields.Append fid
Set fid = tdfTemp.CreateField(«SubAcc», dbText, 4)
tdfTemp.Fields.Append fid
Set fid = tdfTemp.CreateField(«Shifr», dbLong)
tdfTemp.Fields.Append fid
Set fid = tdfTemp.CreateField(«Date», dbDate)
fid.Required = True tdfTemp.Fields.Append fid
' Set fid = tdfTemp.CreateField(«SaldoDeb», dbCurrericy)
tdfTemp.Fields.Append fid
Set fid = tdfTemp.CreateField(«SaldoKr», dbCurrency)
tdfTemp.Fields.Append fid
dbTemp.TableDefs.Append tdfTemp
' CREATE INDEX «BalanceShifr»
Set tdfTemp = dbTemp.TableDefs(«BalanceShifr»)
Set idx = tdfTemp.CreateIndex(«ForeignKey»)
Set fid = idx.CreateField(«Condition!d»)
idx.Fields.Append fid
tdfTemp.Indexes.Append
idx Exit Function errHandle:
MsgBox «Table creating error!», vbExclamation, «Error»
CreateTable = False
End Function
Как удалить рабочие листы листов в зависимости от даты?
Вот код функции на Excel VBA, который решает данную проблему:
' Function DelSheetByDate
' Удаляет рабочий лист sSheetName в активной рабочей книге,
' если дата dDelDate уже наступила
' В случае успеха возвращает True, иначе - False
Public Function DelSheetByDate(sSheetName As String, _
dDelDate As Date) As Boolean
On Error GoTo errHandle
DelSheetByDate = False
' Проверка даты
If dDelDate <= Date Then
' He выводить подтверждение на удаление
Application.DisplayAlerts = False
ActiveWorkbook.Worksheets(sSheetName). Delete
DelSheetByDate = True
Application.DisplayAlerts = True
End If
Exit Function
errHandle:
MsgBox Err.Description, vbCritical, «Ошибка ?» & Err.Number
End Function
Как подавить доступ по «горячим» клавишам, имеется ввиду предопределенные в Excel клавиши типа Ctrl-O и т.д.?
Вот небольшой исходник на Excel VB, который решает такую проблему.
Public Sub Auto_0pen()
' Overrride standard accelerators
With Application
.OnKey «"o», «Dummy»
.OnKey «~s», «NewAction»
.OnKey «~p», «» ' Kill hotkey !
End With
End Sub
Public Sub DummyO
MsgBox «This hotkey redefined!»
End Sub
Public Sub NewActionO
SendKeys «~n» ' Press <CTRL>+<s> for create new file
' instead of <CTRL>+<n> !
End Sub
Как сделать к «самонарисованным» кнопочкам на Toolbar's подсказки?
Сделать можно вот как:
' Создаем тулбар
Public Sub InitToolBar()
Dim cmdbarSM As CommandBar
Dim ctlNewBtn As CommandBarButton
Set cmdbarSM = CommandBars.Add(Name:=»MyToolBar»,
Position:=msoBarFloating,
temporary:=True)
With cmdbarSM
' 1) Добавляем кнопку
Set ctlNewBtn = .Controls.Add(Type:=msoControlButton)
With ctlNewBtn
. Faceld = 26
.OnAction = «OnBUtton1_CHck»
.TooltipText = «My tooltip message!»
End With
1 2) Добавляем ещё кнопку
Set ctlNewBtn = .Controls.Add(Type:=msoControlButton)
With ctlNewBtn
. Faceld = 44
.OnAction = «OnButton2_Click»
.TooltipText = «Another tooltip message!»
End With
.Visible = True
End With
End Sub
Как в макросе узнать и использовать текущее положение курсора (не мышиного, естественно)?
Очень просто!
ActiveCell.Row и ActiveCell.Column покажут координаты активной ячейки.
Как узнать есть ли хоть один Notes (комментарий) в рабочем листе, кроме как перебором по всем ячейкам?
В Excel эта проблема может быть решена вот как:
' Function IsCommentsPresent
' Возвращает TRUE, если на активном рабочем листе имеется хотя бы
' одна ячейка с комментарием, иначе возвращает FALSE
Public Function IsCommentsPresent() As Boolean
IsComitientsPresent = ( ActiveSheet.Comments.Count <> 0 )
End Function
Как сделать свой собственный Toolbar с tooltip'ами на кнопках в Excel?
Вот фрагмент кода для Excel, который создаёт toolbar с одной кнопкой с пользовательским tooltip'oM. Нажатие кнопки приводит к выполнению макроса NothingToDo().
' This example creates a new toolbar, adds the Camera button
' (button index number 228) to it, and then displays the new toolbar.
Public Sub CreateMyToolBar()
Dim myNewToolbar As Toolbar
On Error GoTo errHandle:
Set myNewToolbar = Toolbars.Add(Name:=»My New Toolbar»)
With myNewToolbar
.ToolbarButtons.Add Button:=228, StatusBar:=»Statusbar help string»
.Visible = True
With .ToolbarButtons(l)
.OnAction = «NothingToDo»
.Name = «My custom tooltip text!»
End With
End With
Exit Sub
errHandle:
MsgBox «Error number « & Err & «: « & Error(Err)
End Sub
' Toolbar button on action code
Public Sub NothingToDoQ
MsgBox «Nothing to do!», vblnformation, «Macro running»
End Sub
Как запустить .Excel, чтобы оказаться на ячейке содержимое которой известно заранее?
Вот как можно решить эту задачу:
' Sub GotoFixedCell:
' Делает активной ячейку, содержащую значение vVariant на
' рабочем листе sSheetName в активной рабочей книге.
' Note: Содержимое ячеек интерпретируется как 'значение'!
Public Sub GotoFixedCell(vValue As Variant, sSheetName As String)
Dim с As Range, cStart As Range, cForFind As Range
Dim i As Integer
On Error GoTo errhandle:
Set cForFind = Worksheets(sSheetName).Cells ' Диапазон поиска
With cForFind
Set c= .Find(What:=vValue, After:=ActiveCell, LookIn:=xlValues, _
LookAt:= xlPart, SearchOrder:=xlByRows,_
SearchDirection:=xlNext, MatchCase:=False)
Set cStart = с
While Not с Is Nothing
Set с = .FindNext(c)
If c.Address = cStart.Address Then
c.Select
Exit Sub
End If
Wend
End With
Exit Sub
errHandle:
MsgBox Err.Description, vbExclamation, «Error #» & Err.Number
End Sub
Хочу через Excel VBA задать имя листу, который будет вставлен. Но у команды Sheets.Add нет такого параметра! Как бороться?
Очень просто...
'
' Sub CreateSheet
' Вставляет активную рабочую книгу в рабочий лист с именем sSName. ' Note: В случае, если параметр bVisible имеет значение False, этот лист становится скрытым.
Public Sub CreateSheet(sSName As String, bVisible As Boolean)
Dim wsNewSheet As Worksheet
On Error GoTo errHandle
Set wsNewSheet = ActiveWorkBook.Worksheets.Add
With wsNewSheet
.Name = sSName
.Visible = bVisible
End With
Exit Sub
errHandle:
MsgBox Err.Description, vbExclamation, «Error #» & Err.Number
End Sub
Как проверить существует ли лист?
Можно поступить вот как:
' Function IsWorkSheetExist
' Проверяет, имеется ли в активной рабочей книге лист с именем
sSName.
' В случае успеха возвращает True, иначе - False
Public Function IsWorkSheetExist(sSName As String) As Boolean
Dim с As Object
On Error GoTo errHandle:
Set с = sheets(sName)
' Альтернативный вариант :
Worksheets(sSName).Cells(1, 1) = Worksheets(sSName).Cells(1, 1)
IsWorkSheetExist = True
Exit Function
errHandle:
IsWorkSheetExist = False
End Function
Как обратиться к ячейке по ее имени?
Как обратиться к ячейки по ее имени? То есть есть Лист1 и в нем ячейки с именем Дебет и Кредит. Необходимо подсчитать Дебет-Кредит средствами Excel VBA. При использовании и Range(Дебет)-Range(Kpeдит) выдается ошибка, что не описаны переменные.
Нужно разыменовать ячейку из кода Excel VBA. Вот фрагмент кода, который решает такую задачу:
' Function ValueOfNamedCell
' Возвращает значение ячейки с именем sCellName. в активной рабочей книге.
' Note: В случае, если ячейка с именем sCellName не существует -функцией возвращается ' значение Empty.
'
Public Function ValueOfNamedCell(sCellName As String) As Variant
On Error GoTo errHandle
ValueOfNamedCell =
ActiveWorkbook.Names(sCellName).RefersToRange.Value
Exit Function
errHandle:
ValueOfNamedCell = Empty
End Function
Можно ли из программы на Visual Basic создать рабочую книгу Excel?
Да, можно. Пример того, как из Visual Basic через OLE запустить Excel, и создать рабочую книгу:
'CreateXIBook
' Вызывает MS Excel, создает рабочую книгу с именем sWbName с одним
' единственным рабочим листом. Рабочая книга будет сохранена в каталоге
' sDirName. В случае успеха возвращает True, в противном случае -False.
Public Function CreateXlBook(sWbName As String, sDirName) As
Boolean
' MS Excel hidden instance
Dim objXLApp As Object
Dim objWbNewBook As Object
CreateXIBook = False
Set objXLApp = CreateObject(«Excel.Application»)
If objXLApp Is Nothing Then Exit Function
' В новой рабочей книге создавать только один рабочий лист
objXLApp.SheetsInNewWorkbook = 1
Set objWbNewBook = objXLApp.Workbooks.Add
If objWbNewBook.Is Nothing Then Exit Function
' Сохраняем книгу
If vbNullString = Dir(sDirName, vbDirectory) Then Exit Function
objWbNewBook.SaveAs (sDirName + «\» + sWbName + «.xls»)
CreateXIBook = True
' Освобождение памяти
Set objWbNewBook = Nothing
objXLApp.Quit
Set objXLApp = Nothing
CreateXIBook = True
End Function
Типы переменных
Всегда используйте Integer или Long тип переменных, где это возможно. Процессор обрабатывает эти переменные за один такт.
Используйте Singl вместо Double, они короче и быстрее. Не используйте Variant, если это возможно.
Обязательно добавьте в General каждого модуля Option Explicit или, как альтернативу — DefLong A-Z.
Локальные переменные типа Static в 2-3 раза медленнее, чем обычные локальные переменные. Также они медленнее, чем переменные уровня формы. В то же время переменные формы несколько быстрее переменных модуля.
Явные и неявные преобразования
Остерегайтесь использовать функции Int и Val — они всегда возвращают вещественное значение с плавающей точкой. В случае, если вы используете Long или Integer, применяйте CInt или CLng.
result* = Val(Textl.Text)
result* = Int(Textl.Text)
result* = Clnt(Textt.Text)
result* = Text1. Text
Каждое утверждение в этом коде немного быстрее, чем предыдущее; последнее — приблизительно на 30 процентов быстрее чем первое.
Используйте «\» вместо «/» при делении целых. Избегайте лишних неявных преобразований, так как оператор «/» возвращает значение Single.
Имейте в виду, что оператор «/» возвращает значение Double, если по крайней мере один из элементов имеет Тип Double.
Бэйсик хранит константы в наиболее простой форме: скажем, константа, несущая единицу, хранится как целое, если вы предполагаете использовать ее для действий с вещественными числами, объявите ее явно:
Const ONE As Double = 1
Математика
Самые быстрые функции те, результаты работы которых вы можете оценивать заранее. Например, сохранение факториалов всех чисел в диапазоне от одного до 100 в массив один раз в начале программы — намного быстрее, чем предположительные тысячи вызовов функции в течение жизни приложения.
Используйте оператор And вместо Mod, когда делитель — число в формате 2 ^N. Например, вы можете использовать два метода извлечь самый младший байт в Целом числе:
lowByteX = value!!! Mod 256
lowByteX = value* And 255
Второй немного быстрее
Используйте Boolean в операторах сравнения
Например
If х о 0 Ог у <> 0 Then ...
Работает так-же как это:
If х Ог у Then ...
А это:
If х = 0 And у = 0 Then ...
Так-же как это :
If (х Or у) = 0 Then ...
Даже оператор XOR может сэкономить немного времени:
If (х = 0 And у = 0) Or (х о 0 And у о 0) Then ...
А побыстрее так:
If (х = 0) Хог (у о 0) Then ..
Иногда вы можете заменить весь If...Else блок более простой операцией Boolean. Например, вы можете заменить этот код:
If х > 0 Then у = 1 Else у =0
на другой, хотя и весьма загадочный
у = -(х > 0)
При этом вы обязательно должны добавить замечание, которое ясно объясняет, что ваш код делает, во избежании проблем сопровождения в дальнейшем.
Ускорение массивов
Чтение и запись элемента массива всегда медленнее, чем доступ к простой переменной. Следовательно, если вы должны использовать тот же самый элемент массива в цикле неоднократно, вы должны назначить временную переменную, содержащую этот элемент, и использовать именно ее. В циклах повышение производительности может достигать 80 процентов.
По той же самой причине, обращение к элементамм в матрице медленнее чем доступ к элементам в моноразмерном массиве. Примите это во внимание при проектировании ваших алгоритмов.
При частом поиске в массиве, содержание которого не меняется, часто имеет смысл подумать о сортировке. Сортировка обычно — медленное занятие, но сортировка с использованием Hash tables может ускорить процесс сортировки во много раз.
В случае, если значения Integer элементов вашего массива не более 255, то имеет смысл преобразовать его в массив типа Byte. Это не ускорит код, но ресурсоемкость уменьшит, что актуально для старых машин.
Копируйте блоки данных между массивами того же самого типа, используя функцию API Copy Memory вместо For...Next loop:
Declare Sub CopyMemory Lib «kerne!32» Alias «RtlMoveMemory» (dest
As Any, As Any, ByVal bytes As Long)
' copy a() into b() - b() is of same type and properly
' DIMensioned N is the number of items
' to copy CopyMemory b(0), a(0), n * Len(a(0))
Вы можете использовать эту функцию для сдвига элементов массива при простом добавлении элемента.
При работе с матрицами используя вложенные циклы — внешний цикл должен выполнить итерации на столбцах, а внутренний — итерации на строках (VB сохраняет матрицы один столбец после другого). Программа вызывает меньшее количество «листания».
Никогда не используйте For Each на Variant массивах. Обычный For loop с Integer или Long индексом как минимум в двое быстрее.
В случае, если вы абсолютно уверены, ваша программа Visual Basic 6.3 никогда не выдает Subscript out of range error, можете компилировать нэйтив код с опцией Remove Array Bounds Check. Это может ускорить программу с работой в массивах на 50 процентов или больше.
Строковые операции
Конкатенация — это медленная операция, если вы хотите заменить 1-2 символа в строке — используйте функцию Mid$:
Mid$(a$, 1, 1) = «А»
Не используйте такой код :
а$ = «А & Mid$(a$, 2)
Строковые переменные фиксированной длины часто медленнее, чем стандартные строковые переменные. Фактически, все строковые функции работают только со строками переменной длины. Следовательно, все строки фиксированной длины сначала должны быть преобразованы, прежде, чем обработаются. Это может замедлить ваш код раза в три или четыре.
Используйте функции со знаком $, они возвращают вам строковые значения, тогда как без доллара — Variant, что приводит к лишним преобразованиям.
Использование ASCII кода чуствительно быстрее использования символа к примеру:
If Left$(a$, 1) = « « Then ... '
If Asc(a$) = 32 Then ...
Второе примено на 40 процентов быстрее.
Используйте встроенные строковые константы вместо Chr$ (). Например, используйте vbTab вместо Chr$ (9), и vbCrLf вместо Chr$ (13) и Chr$ (10).
Использование функции Len, чтобы проверить, содержит ли строка символы — приблизительно на 25 процентов быстрее, чем явное сравнение с пустой строкой:
If Len(a$) = 0 Then ...
Часто Visual Basic 6.3 программисты сравнивают строки, используя LCASE$ или UCASES, чтобы преобразовать обе строки перед сравнением. Однако, быстрее прибегнуть к редко используемой функции StrComp:
If StrComp(a$, b$, vbTextCompare) = 0 Then
' strings are equal
End If
Точно так же, когда вы должны искать подстроку в режиме без учета регистра, используйте функцию InStr с четвертым необязательным параметром:
.If Instr(1, a$, «vb», vbTextCompare) Then ...
Функция InStr позволяет вам быстро проверять содержится ли одиночный символ в списке символов.
Почти забытый оператор Like может часто сохранять много циклов процессора, выполняя сложные строковые тесты в одной операции:
If ID Like <<[A-Z]###» Then ...
Здесь IP должен содержать букву и три цифры.
Ускорение загрузки форм
В случае, если позволяет память — не выгружайте часто используемую форму — просто спрячте ее, в следующий раз она появится мгновенно. Однако при этом событие Form Load происходить не будет.
Не выполняйте много медленных процессов в событии Form Load, перенесите их в Form Activate, при этом, если форма не модальная, придется отслеживать получение формой фокуса, чтобы избежать повторной инициализации.
Private Sub Form_Activate()
Static initDone As Boolean
If Not initDone Then ' open your DB here
initDone = True
End If
End Sub
Будет работать быстрее и уменьшится потребление ресурсов, если вы используете меньшее количество и более простые контроль!. Например, не используйте маскедит, если вы можете сделать все с регулярными текстбоксом. По той же самой причине, используйте лэйбл с рамкой вместо строки состояния или текстбокса только для чтения, Имиджбокс вместо Тулбокса, маленький scrollbar вместо спинбаттон, и т.д.
Всегда сбрасывайте форму в Nothing после выгрузки. Этот шаг освобождает всю память, распределенную для переменных и массивов.
Unload Form1
Set Forml = Nothing
Помните!
Abs (функция)
Возвращает абсолютное значение числа.
And (операция)
Логическое И.
AppActivate (оператор)
Активизирует окно приложения.
Array (функция)
Создает массив из параметров и возвращает его как переменную типа Variant
Asc (функция)
Возвращает числовой код первого символа строки аргумента.
Atn (функция)
Возвращает арктангенс числа в радианах.
Веер (оператор)
Проигрывает звуковой сигнал через динамик компьютера.
Call (оператор)
Передает управление процедуре модуля (Sub), функции модуля (Function) или подпрограмме DLL.
CBool (функция)
Приводит выражение к типу Boolean.
CByte (функция)
Преобразует выражение к типу Byte.
CCur (функция)
Преобразование выражения к типу Currency.
CDate (функция)
Преобразование выражения к типу Date.
CDbl (функция)
Преобразование к типу Double.
ChDir (оператор)
Изменяет текущий каталог на устройстве.
ChDrive (оператор)
Изменяет текущее устройство.
Choose (функция)
Возвращает значение из списка аргументов с определенным порядковым номером.
Chr (функция)
Возвращает символ, связанный с определенным числовым кодом.
Clnt (функция)
Преобразование выражения к типу Integer.
CLng (функция)
Преобразование выражения к типу Long.
Close (оператор)
Закрывает файл, открытый оператором Open.
Command (функция)
Возвращает командную строку, используемую для запуска Visual Basic или приложения на Visual Basic.
Const
Оператор объявления констант.
Cos (функция)
Возвращает косинус числа.
Create Object (функция)
Создать OLE Automation объект.
CSng (функция)
Преобразование выражения к типу Single.
CStr (функция)
Преобразование выражения к типу String.
CurDir (функция)
Возвращает текущий каталог логического устройства.
CVar (функция)
Преобразование выражения к типу Variant.
CVErr (функция)
Возвращает подтип ошибки, для определенного пользователем номера ошибки.
Date (оператор)
Устанавливает значение системной даты.
Date (функция)
Возвращает значение системной даты.
DateAdd (функция)
Возвращает переменную типа Variant, содержащую дату, отличающуюся от заданной на определенный интервал времени.
DateDiff (функция)
Возвращает число временных интервалов между двумя датами.
DatePart (функция)
Возвращает определенную часть заданной даты.
DateSerial (функция)
Возвращает дату для заданного года, месяца и дня.
DateValue (функция)
Возвращает дату.
Day (функция)
Возвращает число от 1 до 31, соответствующее текущему дню месяца.
DDB (функция)
Возвращает значение амортизационных потерь за определенный период.
Declare (оператор)
На уровне модуля объявляет ссылки ко внешним подпрограммам в DLL.
Deftype (операторы)
Устанавливает тип данных по умолчанию на уровне модуля для переменных, параметров подпрограмм, а также возвращаемых значений для функций и операторов Property Get, начинающихся с определенных символов.
Dim (оператор)
Объявляет переменные и выделяет память под них.
Dir (функция)
Возвращает имя файла или каталог, подходящий для данного шаблона или атрибута файла, или метку тома устройства.
DoEvents (функция)
Прерывает выполнение приложения.
Do... Loop (оператор)
Повторяет блок команд до тех пор, пока условие верно или до тех пор, пока условие не станет верным.
End (оператор)
Заканчивает подпрограмму или блок команд.
Environ (функция)
Возвращает строку, связанную с переменной окружения операционной системы
EOF (функция)
Возвращает значение, указывающее, достигнут ли конец файла.
Eqv (оператор)
Проверяет логическое равенство двух выражений.
Erase(оператор)
Повторно инициализирует элементы массивов фиксированного размера и перераспределяет память под динамические массивы.
Error (оператор)
Эмулирует возникновение ошибки.
Error (функция)
Возвращает текст сообщения данного номера ошибки.
Exit (операторы)
Осуществляет выход из циклов Do ... Loop, For ... Next, функции и процедур.
Ехр (функция)
Возвращает экспоненту числа.
FileAttr (функция)
Возвращает режим открытия или номер (handle) файла.
FileCopy(onepamop)
Копирует файл.
FileDateTime (функция)
Возвращает дату и время создания или последней модификации файла.
FileLen (функция)
Возвращает длину файла в байтах.
Fix (функция)
Возвращает целую часть числа.
For Each...Next (оператор)
Повторяет одну и ту же последовательность команд для каждого элемента массива или коллекции.
For...Next (оператор)
Повторяет последовательность команд определенное число раз.
Format (функция)
Форматирует выражение в соответствии с заданным форматом.
FreeFile (функция)
Возвращает следующий не занятый номер файла для использования в операторе Open.
Function (оператор)
Объявляет имя, аргументы и код подпрограммы, возвращающей значение (функции).
FV (функция)
Возвращает значение ренты, основываясь на периодических взносах и постоянной норме капиталовложений.
Get (оператор)
Читает данные из открытого файла в переменную.
GetAttr (функция)
Возвращает атрибуты файла, каталога или метки тома.
GetObject (функция)
Возвращает OLE Automation объект для файла с данным расширением.
GoSub... Return (оператор)
Выполняет подпрограмму.
GoTo (оператор)
Передает управление определенной строке подпрограммы без возврата контроля.
Hex (функция)
Возвращает строку, представляющую шестнадцатеричное значение числа.
Hour (функция)
Возвращает целое число в диапазоне 0-23 включительно, представляющее определенный час дня.
If...Then... Else (оператор)
Выполнение групп команд в зависимости от значения выражения.
Iff (функция)
Возвращает одно из двух значении в зависимости от значения выражения.
Imp (операция)
Импликация двух выражений.
Input (функция)
Возвращает символы из файла, открытого для последовательного доступа или как двоичный файл.
Input # (оператор)
Считывает данные из открытого файла в переменные.
InputBox (функция)
Показывает диалоговое окно ввода, ожидает ввода текста и возвращает содержимое введенного текста, после закрытия окна.
InStr (функция)
Возвращает позицию первой найденной подстроки в строке.
Int (функция)
Возвращает целую часть числа.
Is (операция)
Сравнение двух ссылок на объекты.
Is Array (функция)
Возвращает булево значение, указывающее, является ли данная переменная массивом.
IsDate (функция)
Возвращает булево значение, указывающее, может ли выражение быть преобразовано к типу Date.
IsEmpty (функция)
Возвращает булево значение, указывающее, инициализировано ли значение данной переменной.
IsError (функция)
Возвращает булево значение, указывающее, является ли выражение значением кода ошибки.
IsMissing (функция)
Возвращает булево значение, указывающее, был ли передан данный необязательный параметр в подпрограмму.
IsNull (функция)
Возвращает булево значение, указывающее, не содержит ли выражение недопустимое (Null) значение.
IsNumeric (функция)
Возвращает булево значение, указывающее, может ли данное выражение рассматриваться как число.
IsObject (функция)
Возвращает булево значение, указывающее, является ли выражение объектом OLE Automation.
Kill (оператор)
Удаляет файл.
LBound (функция)
Возвращает значение нижней границы индекса массива.
LCase (функция)
Возвращает строку в нижнем регистре.
Left (функция)
Возвращает определенное число символов с начала строки.
Len (функция)
Возвращает число символов строки или число байт, необходимых для хранения переменной.
Let(оператор)
Присваивает значение выражения переменной или свойству.
Like (операция)
Сравнение двух строк.
Line Input # (оператор)
Считывает строку из файла в переменную.
Load (оператор)
Загружает в память форму или элемент управления.
LoadPicture (функция)
Загружает графический образ в объекты: Form, Picture Box u Image.
Loc (функция)
Возвращает текущую позицию чтения/записи в открытом файле.
Lock(оператор)
Контролирует доступ других процессов ко всему или части открытого файла.
LOF (функция)
Возвращает размер в байтах открытого файла.
Log (функция)
Возвращает натуральный логарифм числа.
LSet(оператор)
Копирует строку в строковую переменную, а также копирует значение переменной одного специализированного типа в переменную другого специализированного типа.
LTrim (функция)
Возвращает копию строки без лидирующих пробелов.
Mid (оператор)
Замещает определенное число символов в строке на символы из другой строки.
Mid (функция)
Возвращает определенное число символов с определенной позиции строки.
Minute (функция)
Возвращает целое число в диапазоне 0—59, представляющее минуту часа.
MkDir (оператор)
Создает новый каталог.
Mod (операция)
Возвращает остаток от деления двух чисел.
Month (функция)
Возвращает целое число в диапазоне 1-12, представляющее номер месяца.
MsgBox (функция)
Показывает сообщение в диалоговом окне, ожидает выбор одной из кнопок пользователем и возвращает значение, указывающее, какая кнопка была выбрана.
Name (оператор)
Переименовывает файл или каталог.
Not (операция)
Логическое отрицание.
Now (функция)
Возвращает текущие значения даты и времени.
Oct (функция)
Возвращает строку, представляющую восьмеричное представление числа.
On Error (оператор)
Устанавливает обработчик ошибок и задает местоположение подпрограммы обработки; используется также для отмены обработки ошибок подпрограммой обработчика.
On..GoSub, On...GoTo (операторы)
Передача управления на одну из нескольких определенных строк (меток), в зависимости от значения выражения.
Open (оператор)
Скрывает файл для ввода/вывода.
Option Base (оператор)
Используется для объявления значения нижней границы размерности индексов массивов по умолчанию.
Option Compare (оператор)
Используется на уровне модуля для объявления метода сравнения по умолчанию при сравнении строк.
Option Explicit (оператор)
Используется на уровне модуля для установки проверки наличия объявлений для всех переменных в данном модуле.
Option Private (оператор)
Используется на уровне модуля для указания, что весь модуль является Private.
Or (операция)
Логическое ИЛИ.
Partition (функция)
Возвращает строку, указывающую, сколько раз встретились числа из заданного диапазона.
Print # (оператор)
Записывает форматированные данные в файл. Private (оператор)
Используется на уровне модуля для объявления Private переменных и выделяет место в памяти для их хранения.
Property Get (оператор)
Объявляет имя, аргументы и код подпрограммы получения значения свойства.
Property Let (оператор)
Объявляет имя, аргументы и код процедуры установки значения свойства.
Property Set (оператор)
Объявляет имя, аргументы и код процедуры установки ссылки на объект.
Public (оператор)
Используется на уровне модуля для объявления Public переменных и выделяет место в памяти для их хранения.
Put (оператор)
Записывает переменную в файл.
QSColor (функция)
Возвращает RGB код, соответствующий номеру цвета.
Randomize (оператор)
Инициализирует генератор случайных чисел.
RGB (функция)
Возвращает целое число, представляющее значение RGВкода.
ReDim (оператор)
Используется на уровне подпрограммы для переопределения размера динамических массивов и выделения под них места в памяти.
Rem (оператор)
Вставка комментариев в программу.
Reset (оператор)
Закрывает все открытые программой файлы.
Resume (оператор)
Продолжает выполнение программы после завершения процедуры обработчика ошибок.
Right (функция)
Возвращает определенное число символов с правой стороны строки.
RmDir (оператор)
Удаляет каталог.
Rnd (функция)
Возвращает случайное число.
RSet (оператор)
Копирует правую часть строки в строковую переменную.
RTrim (функция)
Возвращает копию строки без конечных пробелов.
SavePicture (оператор)
Сохраняет в файл графический образ объекта Form, элементов управления Picture Box или Image.
Second (функция)
Возвращает целое значение в диапазоне 0—59, представляющее секунду в минуте.
Seek (оператор)
Устанавливает позицию для следующей операции чтения или записи в открытый файл.
Seek (функция)
Возвращает текущую позицию чтения/записи открытого файла.
Select Case (оператор)
Выполняет одну или несколько команд, в зависимости от значения выражения.
SendKeys (оператор)
Посылает одно или несколько нажатий клавиш активному окну, как если бы они были введены пользователем с клавиатуры.
Set (оператор)
Связывает ссылку на объект с переменной или свойством.
SetAttr (оператор)
Устанавливает атрибуты файла.
Sgn (функция)
Возвращает знак числа.
Shell (функция)
Запускает внешнюю программу на выполнение.
Sin (функция)
Возвращает значение синуса угла.
Space (функция)
Возвращает строку, содержащую определенное число пробелов.
Spc (функция)
Позиционирование в строке вывода.
Sqr (функция)
Подсчет значения квадратного корня числа.
Static (оператор)
Используется на уровне модуля для объявления переменных и выделяет место в памяти для их хранения. Переменные сохраняют значения до завершения программы.
Stop (оператор)
Приостанавливает выполнение программы.
Str (функция)
Возвращает строковое представление числа.
StrComp (функция)
Возвращает результат сравнения строк.
StrConv (функция)
Возвращает преобразованную строку.
String (функция)
Возвращает строку заданной длины из одинаковых символов.
Sub (оператор)
Объявляет имя, параметры и тело процедуры.
Switch (функция)
Подсчитывает значения списка выражении и возвращает значение или выражение, связанное с выражением из списка, значение которого равно True.
Tab (функция)
Позиционирование в строке вывода.
Tan (функция)
Возвращает значение тангенса угла.
Time (оператор)
Устанавливает значение системных часов.
Time (функция)
Возвращает значение типа Date, указывающее текущее системное время.
Timer (функция)
Возвращает число секунд, прошедших после полуночи.
TimeSerial (функция)
Возвращает значение типа Date, содержащее время для заданного часа, минуты и секунды.
Time Value (функция)
Возвращает значение типа Date, содержащее время суток.
Trim (функция)
Возвращает копию строки без начальных и конечных пробелов.
Туре(оператор)
Объявляет на уровне модуля специализированный тип данных.
TypeName (функция)
Возвращает строку информации о заданной переменной.
UBound (функция)
Возвращает значение наибольшего индекса для данной размерности массива.
UCase (функция)
Возвращает строку, преобразованную в верхний регистр.
Unload (оператор)
Выгружает форму или элемент управления из памяти.
Unlock (оператор)
Контролирует доступ других процессов ко всему или части открытого файла.
Val (функция)
Возвращает числовое представление строки.
VarType (функция)
Возвращает значение, указывающее тип переменной.
Weekday (функция)
Возвращает целое число, представляющее день недели.
While...Wend (оператор)
Выполняет в цикле последовательность команд до тех пор, пока верно условие.
Width # (оператор)
Назначает ширину строки вывода для операции записи в открытый файл.
With (оператор)
Выполняет последовательность команд для конкретного объекта или переменной специализированного типа.
Write # (оператор)
Записывает данные в файл.
Хог(операция)
Исключающее ИЛИ.
Year (функция)
Возвращает целое число, представляющее год.
Использование ресурсов
В программировании на C++, да и не только на нем, вы можете использовать не «зашитые» в сам файл проекта ресурсы, но и подгружаемые, то есть оформленные в виде отдельного файла. Ничего в этом необычного нет. Оказывается Бэйсик это тоже может. Единственное «но» — этот самый файл, содержащий ресурсы должен быть оформлен в виде отдельного проекта VB. Например, ActiveX DLL.
То есть получающийся файл не совсем стандартен «снаружи» (для файла ресурсов). И хотя это несколько неприятно, но не смертельно — мы всегда можем изменять это наш «ресурсосодержащий проект» таким образом, как нам удобнее.
Теперь коротенько о ресурсах. Зачем они вообще нужны? Первое, и наиболее серьезное применение — это многоязычные программы, то есть вы пишите программу, которая имеет больше чем один языковый интерфейс. Например, русский и английский. Для этого наименования всех кнопок, пунктов меню, меток и подписей (строковые наименования) заносятся в ресурс-файл в двух вариантах, на разных языках. Далее программа, в зависимости от выбранного языка интерфейса, подставляет эти наименования. Удобство такого подхода очевидно.
Следующий случай — это картинки. В случае, если в вашем проекте 50 форм, и на каждой пятой форме одинаковая иконка, то используя ресурс-файл можно немного (9 раз по размеру иконки) сэкономить на размере конечного *.ехе. Также ресурс-файл удобен для показа коротенькой анимации, когда по таймеру в пикчербоксе меняются 5-6 картинок. Да и вообще, когда не очень большое количество картинок нужно для использования в проекте, но распостранять их как отдельные файлы не хочется.
Итак, как же работать с ресурсами? Первое — хорошая новость. В VB6 встроен ADD-IN Resource Editor. Достаточно загрузить этот модуль из ADD-INs меню и можно легко создавать и редактировать ресурсы. Вторая новость для тех, у кого стоит пятый бэйсик — этот модуль распостраняется Микрософтом бесплатно, и легко устанавливается.
После установки этого модуля просто щелкните пару раз мышкой на иконке редактора ресурсов на панели бэйсика, и, в открывшемся окне, легко добавляйте или редактируйте строки, картинки, курсоры, иконки, или даже custom resources.
Несколько тонкостей — каждый добавляемый ресурс имеет наименование — оно устанавливается автоматически — причем наименование может быть номером или строкой. Так вот, автоматически это будет номер. Достать в последствии ресурс можно только зная его наименование. И тип этого наименования должен быть соблюден. Второе — добавив необходимые ресурсы — не забудьте сохранить файл с ними. После этого ссылка на этот файл появится в проекте в разделе Related Documents. Ну и последнее — к сожалению в проекте не может быть более одного файла ресурсов. Есть еще некоторые ограничения, в частности на длину текста.
В случае, если вы уже попробовали сделать файл ресурсов, то конечно, горите желанием узнать, как их оттуда извлекать. Очень просто. В Бэйсике уже сидят функции для работы с ними.
Это LoadResPicture, LoadResData, LoadResString,
Рассмотрим простейший пример загрузки картинки из ресурса в пикчербокс.
Откройте проект, добавьте в него форму с кнопкой и пикчербоксом, подключите resource editor, и добавьте какую-либо картинку, как ресурс. После добавления картинки переименуйте получившийся ресурс, к примеру в MyPic. На Click event на кнопке добавьте:
Picturel.Picture = LoadResPicture(«MyPic», 0)
Последний аргумент означает, что извлекаемый ресурс — именно битмап картинка. Посмотрите в хелпе остальные типы извлекаемых ресурсов. В частности для иконки это будет 1.
Ну, и напоследок — как же создать отдельный файл, содержащий ресурсы? Опять таки очень просто — создайте компоненту — к примеру dll-проект, в который добавлен ресурс файл. Для этого надо выбрать тип проекта ActiveX DLL, переименовать класс Classl в MyResource, добавить в этот проект ресурс-файл, наполнить его ресурсами и создать функцию:
Public Function LoadMyPic(ResName As String) As Object
Set LbadMyPic = LoadResPicture(ResName, 0)
End Function
Обратите внимание, картинка — это объект, а не число, или строка. Variant здесь тоже не подходит. Так же обратите внимание что ResName (имя ресурса, ID) может быть как String так и просто номер, однако номер 1 зарезервирован для иконки проекта.
Итак, в начале надо отметить ссылку на эту DLL. Для этого идем в Project ->Referens и кликаем возле имени нашей DLL. После этого в основном проекте можно создать переменную:
Dim obj as new MyResource
И показать картинку:
Picturel.Picture = obj.LoadMyPic («MyPic»)
'любое существующее ID ресурса
Ну и последнее замечание — так как мы договорились хранить ресурсы прямо в dll-файле, то нам не интересно открывать этот проект каждый раз, когда подзабылись имена ресурсов, сохраненных в этой dll. Xo-честя видеть эти имена (а лучше описания) прямо в среде разработки. Для этого нужно сделать всего 2 вещи:
1
Внутри класса MyResource мы объявляем следущее перечисление:
Public Enum MyRecourcelDDescription
SigRedPicture = 101
SmollBlueBall= 102
End Enum
2
На форме, в General Declaration пишем:
Dim ResIO as MyRecourcelDDescription
Этим, мы объявляем переменную, принимающую значения только из описанного в классе перечисления.
Далее, при обращении к этой переменной — к примеру, вот так:
ResID = ' Вот тут должна появится подсказка, показывающая
BigRedPicture и SmollBlueBall
Вызов картинки из нашего файла ресурса легко осуществить так:
Picturel.Picture = obj.LoadMyPic (ResID )
И последняя приятная новость. В объект браузере тоже видны эти самые константы — описания ресурсов.
Потоки
С появлением оператора AddressOf, часть индустрии ПО стала ориентироваться на авторов, показывающих как с использованием Visual Basic решать ранее невозможные задачи. Другая часть быстро охватила консультантов, помогающих пользователям, имеющим проблемы при решении таких задач.
Проблема не в Visual Basic или в технологии. Проблема в том, что большинство авторов применяют одно и тоже правило к AddressOf методикам, что большинство компаний по разработке ПО считают, что если вы должны что-то сделать, то вы сможете. Идея о том, что применение самой новой и последней технологии должно, по определению, быть самым лучшим решением проблемы, широко распространена в индустрии ПО. Эта идея неверна. Развертывание технологии должно управляться прежде всего проблемой, которую необходимо решить, а не технологией, которую кто-то пробует продать.
Очень плохо, что из-за того, что компании часто пренебрегают упоминанием об ограничениях и недостатках их инструментальных средств, авторы иногда бывают не в состоянии обратить внимание читателей на следствия некоторых методик, которые они описывают. И журналы и книги иногда пренебрегают своей ответственностью, чтобы удостовериться, что практика программирования, которую они описывают, является приемлемой.
Программисту очень важно выбрать необходимый инструмент для своей работы. Это — ваша задача, чтобы разработать код, который работает теперь не только на одной специфической платформе, но также работает на разных платформах и системных конфигурациях. Ваш код должен быть хорошо документирован и поддержан другими программистами, участвующими в проекте. Ваш код должен следовать правилам, продиктованным операционной системой или стандартами, которые вы используете. Отказ так делать может привести к проблемам в будущем, поскольку системы и программное обеспечение постоянно совершенствуются.
Каждый, кто использует Windows XP, знает, что система способна делать больше, чем одну вещь одновременно. Может одновременно выполнять несколько программ, при одновременном проигрывании компакт-диска, посылке факса и пересылке файлов. Каждый программист знает (или должен знать) что центральный процессор компьютера может только выполнять одну команду одновременно (проигнорируем существование многопроцессорных машин). Как единственный центральный процессор может выполнять множество задач?
Это делается быстрым переключением между многими задачами. Операционная система содержит в памяти все программы, которые запущены в настоящий момент. Это позволяет центральному процессору выполнять программы по очереди. Каждый раз происходит переключение между программами, при этом меняется содержимое внутренних регистров, включая указатель команды и указатель вершины стека. Каждая из таких «задач» называется потоком выполнения (thread of execution).
В простой многозадачной системе, каждая программа имеет имеет единственный поток. Это означает, что центральный процессор начинает выполнение команд в начале программы и продолжает, следуя инструкциям, в последовательности, определенной программой до тех пор, пока программа не завершается.
Скажем, программа имеет пять команд: А, В, С, D и Е, которые выполняются последовательно (никаких переходов нет в этом примере). Когда приложение имеет один поток, команды будут всегда выполняться в точно том же самом порядке: А, В, С, D и Е. Действительно, центральный процессор может потребовать времени для выполнения других команд в других программах, но они не будут влиять на это приложение, если не имеется конфликт над общими ресурсами системы, но это уже отдельная тема для разговора.
Продвинутая многопоточная операционная система типа Windows позволяет приложению выполнять больше чем один поток одновременно. Скажем, команда D в нашем типовом приложении могла создать новый поток, который стартовал командой В и далее выполнял последовательность команд С и Е. Первый поток был бы все еще А, В, С, D, E, но когда команда D выполнится, возникнет новый поток, который выполнит команды В, С, Е (здесь команды D уже не будет, иначе мы получим еще один поток).
Другими словами, когда вы начинаете новый поток выполнения в приложении, вы никогда не можете знать точный порядок, в котором команды в двух потоках выполнятся относительно друг друга. Два потока полностью независимы.
Имитатор Многопоточного режима
Рассмотрим проект MTDemo.
Проект содержит один модуль кода, в котором содержится две глобальных переменных:
' MTDemo - Multithreading Demo program
' Copyright © 1997 by Desaware Inc. All Rights Reserved
Option Explicit
Public GenericGlobalCounter As Long
Public Totallncrements As Long
Этот проект содержит одну форму — frmMTDemol, которая содержит следующий код:
' MTDemo - Multithreading Demo program
' Copyright © 1997 by Desaware Inc. All Rights Reserved
Option Explicit Dim State As Integer
' State = 0 - Idle
'State = 1 - Loading existing value
'State = 2 - Adding 1 to existing value
'State = 3 - Storing existing value
' State = 4 - Extra delay
Dim Accumulator As Long
Const OtherCodeDelay = 10
Private Sub Command1_Click()
Dim f As New frmMTDemol
f.Show
End Sub
Private Sub Form_Load()
TimeM. Interval = 750 + Rnd * 500
End Sub
Private Sub Timer1_Timer()
Static otherdelay&
Select Case State
Case 0
IblOperation = «Idle»
State = 1
Case 1
IblOperation = «Loading Ace»
Accumulator = GenericGlobalCounter '
State = 2
Case 2
IblOperation = «Incrementing»
Accumulator = Accumulator + 1
State = 3
Case 3
IblOperation = «Storing»
GenericGlobalCounter = Accumulator
Totallncrements = Totallncrements + 1
State = 4
Case 4
IblOperation = «Generic Code»
If otherdelay >= OtherCodeDelay
Then State = 0
otherdelay = 0
Else
otherdelay = otherdelay + 1
End If
End Select
UpdateDisplay
End Sub
Public Sub UpdateDisplay()
IblGlobalCounter = Str$(GenericGlobalCounter)
IblAccumulator = Str$(Accumulator)
IblVerification = Str$(TotalIncrements)
End Sub
Эта программа для моделирования многопоточного режима использует таймер и простой конечный автомат. Переменная State описывает пять команд, которые эта программа выполняет.
State = 0 — неактивное состояние.
State = 1 загружает локальную переменную глобальной переменной GenericGlobalCounter.
State = 2 увеличивает на единицу локальную переменную.
State = 3 запоминает результат в переменной GenericGlobalCounter и увеличивает переменную Totallncrements (которая считает количество приращений переменной GenericGlobalCounter).
State = 4 добавляет дополнительную задержку, представляющую собой время, затраченное на выполнение других команд в программе.
Функция UpdateDisplay обновляет три метки на форме, которые показывают текущее значение переменной GenericGlobalCounter, локального сумматора и общего количества приращений.
Каждый сигнал таймера моделирует цикл центрального процессора в текущем потоке. В случае, если вы запустите программу, то увидите, что значение переменной GenericGlobalCounter будет всегда точно равно переменной Totallncrements, потому что переменная Totallncrements показывает количество увеличений счетчика GenericGlobalCounter потоком.
Но что случится, когда вы нажимаете кнопку Commandl и запустите второй экземпляр формы? Эта новая форма смоделирует второй поток.
Время от времени, команды выстроятся в линию таким образом, что обе формы загрузят одинаковое значение GenericGlobalCounter, увеличат и сохранят его. В результате, значение счетчика увеличится только на единицу, даже при том, что каждый поток полагал, что он независимо увеличивает значение счетчика. Другими словами, переменная была увеличена дважды, но значение увеличилось только на единицу. В случае, если вы запускаете несколько форм, то сразу заметите, что число приращений, представляемых переменной Totallncrements, растет намного быстрее, чем счетчик GenericGlobalCounter.
Что, если переменная представляет объектный счет блокировки — который следит, когда объект должен быть освобожден? Что, если она представляет собой сигнал, который указывает, что ресурс находится в использовании?
Такая проблема может привести к появлению ресурсов, постоянно недоступных в системе, к объекту, блокируемому в памяти, или преждевременно освобожденному. Это может привести к сбоям приложения.
Этот пример был разработан, чтобы достаточно просто увидеть проблему, но попробуйте поэкспериментировать со значением переменной OtherCodeDelay. Когда опасный код относительно небольшой по сравнению со всей программой, проблемы будут появляться менее часто. Хотя это и звучит обнадеживающе, но истина состоит в следующем. Проблемы многопоточного режима могут быть чрезвычайно неустойчивы и их трудно обнаружить. Это означает, что многопоточный режим требует осторожного подхода к проектированию приложения.
Решение проблем Многопоточного режима
Имеются два относительно простых способа избежать проблем многопоточного режима. Избегайтете всеобщего использования глобальных переменных. Добавьте код синхронизации везде, где используются глобальные переменные.
Первый подход используется в основном в Visual Basic. Когда вы включаете многопоточный режим в Visual Basic приложения, все глобальные переменные станут локальными для специфического потока. Это свойственно способу, с которым Visual Basic выполняет apartment model threading.
Visual Basic 6.3 в Microsoft Visio 2002 позволяет использовать многопоточность только в компонентах, которые не имели никаких элементов пользовательского интерфейса. Так было, потому что они не имели безопасного потока управления формами. Например: когда вы создаете форму в Visual Basic, VB дает ей имя глобальной переменной (таким образом, если вы имеете форму, именованную Forml, вы можете непосредственно обращаться к ее методам, используя Forml-метод вместо того, чтобы объявить отдельную переменную формы). Этот тип глобальной переменной может вызывать проблемы многопоточного режима, которые вы видели ранее. Имелись несомненно другие проблемы внутри управления формами.
Почему многопоточность?
Откуда вся суета относительно многопоточного режима, если он включает так много потенциальной опасности? Потому что, в некоторых ситуациях, многопоточный режим может значительно улучшать эффективность приложения. В некоторых случаях — это может улучшать эффективность некоторых операций синхронизации типа ожидания завершения приложения. Это позволяет сделать архитектуру приложения более гибкой. Например, операция Add a long в форме MTDEMO2 со следующим кодом:
Private Sub cmdLongOp_Click()
Dim 1&
Dim s$
For 1 = 1 To 1000000
s = Chr$(l And &H7F)
Next 1
End Sub
Запустите несколько экземпляров формы, используя кнопку cmdLaunch1 Когда вы нажимаете на кнопку cmdLongOp на любой из форм, то увидите, что это действие замораживает операции на всех других формах. Так происходит, потому что все формы выполняются в одиночном потоке — и этот поток занят выполнением длинного цикла. В случае, если вы запустите несколько экземпляров формы кнопкой cmdLaunch2 и нажмете кнопку cmdLongOp на форму, то только эта форма будет заморожена — другие формы будут активными. Они выполняются в собственных потоках, и длинный цикл будет выполняться только в собственном потоке.
Конечно, в любом случае, вы вероятно не должны размещать длительные операции такого типа в ваших формах.
Дальше краткое резюме, когда важен многопоточный режим.
Сервер ActiveX EXE без общих ресурсов
Когда вы имеете ActiveX EXE сервер, который вы собираетесь совместно использовать среди нескольких приложений, многопоточный режим предотвращает приложения от нежелательных взаимодействий с друг другом. В случае, если одно приложение выполняет длинную операцию на объекте в однопоточном сервере, другие приложения будут вытеснены, то есть будут ждать, когда освободится сервер. Многопоточный режим решает эту проблему. Однако, имеются случаи, где вы можете хотеть использовать ActiveX EXE сервер, чтобы регулировать доступ к общедоступнному ресурсу (shared resource). Например, сервер stock quote выполняется в одиночном потоке, который доступен для всех приложений, использующих сервер по очереди.
Многопоточный клиент, выполняемый как ActiveX EXE сервер
Простая форма этого подхода продемонстрирована в приложении MTDEMO2. Этот подход используется, когда приложение поддерживает множественные окна, которые должны исходить из одного приложения, но работать полностью независимо. Internet-браузер — хороший пример такого многопоточного клиента, где каждое окно выполняется в собственном потоке. Здесь следует обратить внимание на то, что многопоточный режим не должен использоваться как замена для хорошего событийно управляемого проекта.
Многопоточные серверы DLL или ЕХЕ
В архитектуре клиент-сервер, многопоточный режим может увеличить эффективность, если вы имеете смесь длинных и коротких клиентских запросов. Будьте внимательным, хотя — если все ваши клиентские запросы имеют подобную длину, многопоточный режим может фактически замедлять среднее время ответа сервера! Никогда не принимайте на веру тот факт, что если ваш сервер является многопоточным, то обязательно его эффективность увеличится.
Соглашение о потоках
Многопоточный режим является потенциально опасным вообще, и особенно попытки многопоточного кодирования приложений, которые не разработаны для поддержки многопоточного режима, скорее всего приведут к фатальным ошибкам и сбоям системы.
Как это возможно, что Visual Basic позволяет вам создавать объекты и использовать их с одиночными и многопоточными средами безотносительно к тому, разработаны ли они для одиночного или многопоточного использования?
Другими словами — как многопоточные Visual Basic приложения могут использовать объекты, которые не разработаны для безопасного выполнения в многопоточной среде? Как могут другие многопоточные приложения использовать однопоточные объекты Visual Basic? Kaк COM поддерживает потоки?
В случае, если вы знаете СОМ, то вы знаете, что СОМ определяет структуру соглашения. Объект СОМ соглашается следовать некоторым правилам так, чтобы этим можно было успешно пользоваться из любого приложения или объекта, который поддерживает СОМ.
Большинство людей сначала думает о интерфейсной части соглашения — о методах и свойствах, которые предоставляет объект.
Но вы не можете не знать того, что СОМ также определяет поточность, как часть соглашения. И подобно любой части соглашения СОМ
— если вы нарушаете эти условия, то будете иметь проблемы.
Visual Basic, естественно, скрывает от вас большинство механизмов СОМ, но чтобы понять как использовать многопоточность в Visual Basic, вы должны разобраться в СОМ модели потоков.
Модель одиночного потока
Однопоточный сервер — самый простой тип реализации сервера. И самый прости для понимания. В этом случае ЕХЕ сервер выполняется в одиночном потоке. Все объекты создаются в этом потоке. Все вызовы методов каждого объекта, поддерживаемого сервером должны прибыть в этот поток.
Но что будет, если клиент выполняется в другом потоке? В том случае, для объекта сервера должен быть создан промежуточный объект -(proxy object). Этот промежуточный объект выполняется в потоке клиента и отражает методы и свойства фактического объекта. Когда вызывается метод промежуточного объекта, он выполняет операции, необходимые для подключения к потоку объекта, а затем вызывает метод фактического объекта, используя параметры, переданные к промежуточному объекту. Естественно, что этот подход требует значительного времени на выполнение задачи, однако он позволяет выполнить все соглашения. Этот процесс переключения потоков и пересылки данных от промежуточного объекта к фактическому объекту и обратно называется marshalling.
В случае DLL серверов, одиночная потоковая модель требует, чтобы все объекты в сервере создавались и вызывались в том же самом потоке что и первый объект, созданный сервером.
Модель Apartment Threading
Модель Apartment Threading как определено COM не требует, чтобы каждый поток имел собственный набор глобальных переменных.
Какую модель поддерживает ваш сервер?
Как приложение или сама Windows узнает, которую модель потоков использует сервер? Эта информация включена в системный реестр (registry). Когда Visual Basic создает объект, он проверяет системный реестр, чтобы определить, в каких случаях требуется использовать промежуточный объект (proxy object) и в каких — marshalling.
Эта проверка является обязанностью клиента и необходима для строгой поддержки требований многопоточности для каждого объекта, который он создает.
Функция API CreateThread
Теперь давайте посмотрим, как с Visual Basic может использоваться функция API CreateThread.
Скажем, вы имеете класс, который вы хотите выполнять в другом потоке, к примеру, чтобы выполнить некоторую фоновую операцию. Характерный класс такого типа мог бы иметь следующий код (из примера MTDemo 3):
' Class clsBackground
' MTDemo 3 - Multithreading example
' Copyright © 1997 by Desaware Inc. All Rights Reserved.
Option Explicit Event DoneCounting()
Dim 1 As Long
Public Function DoTheCount(ByVal finalval&) As Boolean
Dim s As String If 1 = 0 Then
s$ = «In Thread « & App.threadid
Call MessageBox(0, s$, «», 0)
End If
1 = 1 + 1
If 1 >= finalval Then
1 = 0
DoTheCount = True
Call MessageBox(0, «Done with counting», «», 0)
RaiseEvent DoneCounting
End If
End Function
Класс разработан так, чтобы функция DoTheCount могла неоднократно вызываться из непрерывного цикла в фоновом потоке. Мы могли бы поместить цикл непосредственно в сам объект, но вы вскоре увидите, что были веские причины для проектирования объекта, как показано в примере. При первом вызове функции DoTheCount появляется MessageBox, в котором показан идентификатор потока, по которому мы можем определить поток, в котором выполняется код. Вместо Visual Basic 6.3 команды MessageBox используется MessageBox API, потому что функция API, как известно, поддерживает безопасное выполнение потоков. Второй MessageBox появляется после того, как закончен подсчет и сгенерировано событие, которое указывает, что операция закончена.
Фоновый поток запускается при помощи следующего кода в форме frmMTDemo3:
Private Sub cmdCreateFree_Click()
Set с = New clsBackground
StartBackgroundThreadFree с
End Sub
Функция StartBackgroundThreadFree определена в модуле modMTBack следующим образом:
Declare Function CreateThread Lib «kerne!32» (ByVal
IpSecurityAttributes
As Long, ByVal dwStackSize As Long, ByVal IpStartAddress As Long,
ByVal IpParameter As Long, ByVal dwCreationFlags As Long,
IpThreadld As Long) As Long
Declare Function CloseHandle Lib «kerne!32» (ByVal hObject As
Long) As Long
' Start the background thread for this object
' using the invalid free threading approach.
Public Function StartBackgroundThreadFree(ByVal qobj As
clsBackground)
Dim threadid As Long
Dim hnd&
Dim threadparam As Long
' Free threaded approach
threadparam = ObjPtr(qobj)
hnd = CreateThread(0, 2000, AddressOf BackgroundFuncFree,
threadparam 0, threadid)
If hnd = 0 Then
' Return with zero (error) Exit Function
End If
We don't need the thread handle
CloseHandle hnd
StartBackgroundThreadFree = threadid End Function
Функция CreateThread имеет шесть параметров:
Функция возвращает дескриптор потока.
В этом случае мы передаем указатель на объект clsBackground, который мы будем использовать в новом потоке. ObjPtr восстанавливает значение указателя интерфейса в переменную qobj. После создания потока закрывается дескриптор при помощи функции CloseHandle. Это действие не завершает поток, — поток продолжает выполняться до выхода из функции BackgroundFuncFree. Однако, если мы не закрыли дескриптор, то объект потока будет существовать даже после выхода из функции BackgroundFuncFree. Все дескрипторы потока должны быть закрыты и при завершении потока система освобождает занятые потоком ресурсы.
Функция BackgroundFuncFree имеет следующий код:
' A free threaded callback.
' A free threaded callback.
' This is an invalid approach, though it works
' in this case.
Public Function BackgroundFuncFree(ByVal param As ILInknown) As
Long
Dim qobj As clsBackground
Dim res&
' Free threaded approach
Set qobj = param
Do While Not qobj.DoTheCount(100000)
Loop
' qobj.ShowAForm ' Crashes!
' Thread ends on return End Function ''
Параметром этой функции является указатель на интерфейс (ByVal param As lUnknown). При этом мы можем избежать неприятностей, потому что под СОМ каждый интерфейс основывается на lUnknown, так что такой тип параметра допустим независимо от типа интерфейса, передаваемого функции. Мы, однако, должны немедленно определить param как тип объекта, чтобы затем его использовать. В этом случае qobj устанавливается как объект clsBackground, который был передан к объекту StartBackgroundThreadFree.
Функция затем выполняет бесконечный цикл, в течение которого может выполняться любая требуемая операция, в этом случае используется повторный счет. Подобный подход мог бы использоваться здесь, чтобы выполнить операцию ожидания, которая приостанавливает поток пока не произойдет системное событие (типа завершения процесса). Поток затем мог бы вызвать метод класса, чтобы сообщить приложению, что событие произошло.
Доступ к объекту qobj чрезвычайно быстр из-за использования подхода свободного потока (free threading) — никакая переадресация (marshalling) при этом не используется.
Обратите внимание на то, что если вы попробуете использовать объект clsBackground, который показывает форму, то это приведет к сбоям приложения. Обратите также внимание на то, что событие завершения никогда не происходит в клиентской форме.
Действительно, даже Microsoft Systems Journal, который описывает этот подход, содержит очень много предупреждений о том, что при использовании этого подхода есть некоторые вещи, которые не работают.
Является ли это дефектом в Visual Basic?
Некоторые разработчики, кто пробовали развертывать приложения, применяющие этот тип многопоточности, обнаружили, что их приложения вызывают сбои после обновления.
Это означает, что Microsoft не сумел правильно обеспечивать обратную совместимость?
Ответ на оба вопроса: Нет Проблема не в Microsoft или Visual Basic.
Проблема состоит в том, что вышеупомянутый код является мусором. Проблема проста — Visual Basic поддерживает объекты и в модели одиночного потока и в apartment model.
Что это означает?
Это означает, что поведение объекта подчиненно изменениям, так как Visual Basic постоянно модифицируется.
Это означает, что любая попытка объекта обратиться к другим объектам или формам может потерпеть неудачу и что причины отказов могут изменяться, поскольку эти объекты модифицируются.
Это означает, что даже код, который сейчас работает, может внезапно вызвать сбой, поскольку другие объекты добавляются, удаляются или изменяются.
Это означает, что невозможно характеризовать поведение приложения или предсказать, будет ли оно работать или может ли работать в любой данной среде.
Это означает, что невозможно предсказать, будет ли код работать на любой данной системе, и что его поведение может зависеть от используемой операционной, числа используемых процессоров и других проблем конфигурации системы.
Вы видите, что как только вы нарушаете соглашение СОМ, вы больше не защищены теми возможностями СОМ, которые позволяют объектам успешно взаимодействовать друг с другом и с клиентами.
Этот подход является программной алхимией. Это безответственно и ни один программист не должен когда-либо использовать это.
Обратно к функции API CreateThread
Теперь, когда стало понятно, почему подход к использованию CreateThread API является мусором, покажем, как можно использовать эту API функцию безопасно.
Прием прост — вы должны просто твердо придерживаться соглашения СОМ о потоках. Это займет немного больше времени и усилий, но практика показала, что получаются очень надежные результаты.
Пример MTDEMO3 демонстрирует этот подход в форме frmMTDemo3, имеющей код, который запускает класс фона в apartment model следующим образом:
Private Sub cmdCreateApt_Click()
Set с = New clsBackground
StartBackgroundThreadApt с
End Sub
Пока это выглядит очень похоже на подход свободных потоков. Вы создаете экземпляр класса и передаете его функции, которая запускает фоновый поток. В модуле modMTBack появляется следующий код:
' Structure to hold IDispatch QUID
Type QUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Public IID_IDispatch As QUID
Declare Function CoMarshallnterThreadlnterfacelnStream Lib
«ole32.dll»_
(riid As QUID, ByVal pUnk As lUnknown, ppStm As Long) As Long
Declare Function CoGetlnterfaceAndReleaseStream Lib «ole32.dll» _
(ByVal pStrn As Long, riid As QUID, pUnk As lUnknown) As Long
Declare Function Colnitialize Lib «ole32.dll» (ByVal pvReserved As
Long) As Long
Declare Sub CoUninitialize Lib «ole32.dll» ()
' Start the background thread for this object
' using the apartment model
' Returns zero on error
Public Function StartBackgroundThreadApt(ByVal qobj As
clsBackground)
Dim threadid As Long
Dim hnd&, res&
Dim threadparam As Long
Dim tobj As Object
Set tobj = qobj
' Proper marshaled approach InitializellD
res = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, qobj, threadparam)
If res <> 0 Then
StartBackgroundThreadApt = 0
Exit Function
End If
hnd = CreateThread(0, 2000, AddressOf BackgroundFuncApt,
threadparam, 0, threadid)
If hnd = 0 Then
' Return with zero (error)
Exit Function
End If
' We don't need the thread handle
CloseHandle hnd
StartBackgroundThreadApt = threadid
End Function
Функция StartBackgroundThreadApt немного более сложна, чем ее эквивалент при применении подхода свободных потоков. Первая новая функция называется InitializellD. Она имеет следующий код:
' Initialize the QUID structure
Private Sub InitializeIID()
Static Initialized As Boolean
If Initialized Then Exit Sub
With IID_IDispatch
.Datal = &H20400
.Data2 = 0
. Data3 = 0
.Data4(0) = &HCO
.Data4(7) = &H46
End With
Initialized = True
End Sub
Вы видите, нам необходим идентификатор интерфейса — 16 байтовая структура, которая уникально определяет интерфейс. В частности нам необходим идентификатор интерфейса для интерфейса IDispatch. Функция InitializeID просто инициализирует структуру IID_IDISPATCH к корректным значениям для идентификатора интерфейса IDispatch. Это значение получается с помощью использования утилиты просмотра системного реестра.
Почему нам необходим этот идентификатор? Потому что, чтобы твердо придерживаться соглашения СОМ о потоках, мы должны создать промежуточный объект (proxy object) для объекта cIsBackground. Промежуточный объект должен быть передан новому потоку вместо первоначального объекта. Обращения к новому потоку на промежуточном объекте будут переадресованы (marshaled) в текущий поток.
CoMarshallnterThreadlnterfacelnStream выполняет интересную задачу. Она собирает всю информацию, необходимую при создании промежуточного объекта, для определенного интерфейса и загружает ее в объект потока (stream object). В этом примере мы используем интерфейс IDispatch, потому что мы знаем, что каждый класс Visual Basic поддерживает IDispatch и мы знаем, что поддержка переадресации (marshalling) IDispatch встроена в Windows — так что этот код будет работать всегда. Затем мы передаем объект потока (stream object) новому потоку. Этот объект разработан Windows, чтобы быть передаваемым между потоками одинаковым способом, так что мы можем безопасно передавать его функции CreateThread. Остальная часть функции StartBackgroundThreadApt идентична функции StartBackgroundThreadFree.
Функция BackgroundFuncApt также сложнее, чем ее эквивалент при использовании модели свободных потоков и показана ниже:
' A correctly marshaled apartment model callback.
' This is the correct approach, though slower.
Public Function BackgroundFuncApt(ByVal param As Long) As Long
Dim qobj As Object
Dim qobj2 As cIsBackground
' Dim res&
' This new thread is a new apartment, we must
' initialize OLE for this apartment (VB doesn't seem to do it)
res = CoInitialize(O)
' Proper apartment modeled approach
res = CoGetInterfaceAndReleaseStream(param, IID_IDispatch, qobj)
Set qobj2 = qobj
Do While Not qobj2.DoTheCount(10000)
Loop
qoij2.ShowAForm
' Alternatively, you can put a wait function here,
' then call the qobj function when the wait is satisfied
' All calls to Colnitialize must be balanced
CoUninitialize
End Function
Первый шаг должен инициализировать подсистему OLE для нового потока. Это необходимо для переадресации (marshalling) кода, чтобы работать корректно. CoGetlnterfaceAndReleaseStream создает промежуточный объект для объекта clsBackground и реализует объект потока (stream object), используемый для передачи данных из другого потока. Интерфейс IDispatch для нового объекта загружается в переменную qobj. Теперь возможно получить другие интерфейсы — промежуточный объект будет корректно переадресовывать данные для каждого интерфейса, который может поддерживать.
Теперь вы можете видеть, почему цикл помешен в эту функцию вместо того, чтобы находиться непосредственно в объекте. Когда вы впервые вызовите функцию qobjl.DoTheCount, то увидите, что код выполняется в начальном потоке! Каждый раз, когда вы вызываете метод объекта, вы фактически вызываете метод промежуточного объекта. Ваш текущий поток приостанавливается, запрос метода переадресовывается первоначальному потоку и вызывается метод первоначального объекта в той же самом потоке, который создал объект. В случае, если бы цикл был в объекте, то вы бы заморозили первоначальный поток.
Хорошим результатом применения этого подхода является то, что все работает правильно. Объект ->может безопасно показывать формы и генерировать события. Недостатком этого подхода является, конечно, его более медленное исполнение. Переключение потоков и переадресация (->) — относительно медленные операции. Вы фактически никогда не захотите выполнять фоновую операцию как показано здесь.
Но этот подход может чрезвычайно хорошо работать, если вы можете помещать фоновую операцию непосредственно в функцию ->! Например, вы могли бы иметь фоновый поток, выполняющий фоновые вычисления или операцию ожидания системы. Когда они будут завершены, вы можете вызывать метод объекта, который сгенерирует событие в клиенте. Храня количество вызовов метода, небольшое относительно количества работы, выполняемой в фоновой функции, вы можете достигать очень эффективных результатов.
Что, если вы хотите выполнить фоновую операцию, которая не должна использовать объект? Очевидно, проблемы с соглашением СОМ о потоках исчезают. Но появляются другие проблемы. Как фоновый поток сообщи! о своем завершении приоритетному потоку? Как они обмениваются данными? Как два потока будут синхронизированы? Все это возможно выполнить с помощью соответствующих вызовов API.
Вы сможете использовать глобальные переменные, чтобы обмениваться данные, но надо знать, что такое поведение не гарантируется Visual Basic (другими словами, даже если это сейчас работает, не имеется никаких гарантий, что это будет работать в будущем). В этом случае можно использовать для обмена данными методики, основанные на API. Однако, преимуществом показанного здесь подхода, основанного на объектах, является то, что этот подход делает проблему обмена данными между потоками тривиальной, просто делайте это через объект.
Рано или поздно встает необходимость создания справочной системы для вашего приложения. В конце концов, наличие справочной системы придает программе завершенность и, в некотором роде, поднимает ваш имидж, как программиста-профессионала.
Приготовления
В большинстве случаев работа с WinHelp из вашего приложения осуществляется посредством вызовов функций WinAPI, являющихся частью операционной системы Windows. Перед использованием WinAPI функций их следует объявить в BAS модуле вашего проекта. Например, можно использовать следующие объявления:
Public Declare Function WinHelpStr Lib «user32» Alias «WinHelpA»
(ByVal hWnd As Long, ByVal IpHelpFile As String,
_ByVal wCommand As Long, ByVal dwData As String) As Long Public
Declare Function WinHelpNum Lib «user32» Alias «WinHelpA»
_(ByVal hWnd As Long, ByVal IpHelpFile As String,
_ByVal wCommand As Long, ByVal dwData As Long) As Long
Как вы видите, функция объявлена дважды. Последний параметр в списке объясняет причину. В зависимости от параметра wCommand параметр dwData может быть либо типа integer (32 битное значение типа long), либо строкой или структурой (32 битный указатель). Несмотря на то, что вы можете использовать одно и тоже объявление для обоих типов вызова, вам придется явно преобразовывать передаваемые параметры к соответствующему типу. Легче иметь два разных вызова для разных параметров (один для параметра типа integer и другой для строк). Вам также понадобятся следующие константы, которые следует поместить в тот же модуль, где объявлена функция WinHelpAPublic
Const HELP_CONTEXT = &H1Public
Const HELP_QUIT = &H2 Public
Const HELP_INDEX = &H3Public
Const HELP_CONTENTS = &H3& Public
Const HELP_HELPONHELP = &H4 Public
Const HELP_SETINDEX = &H5 Public
Const HELP_SETCONTENTS = &H5& Public
Const HELP_CONTEXTPOPUP = &H8& Public
Const HELP_FORCEFILE = Ш& Public
Const ,HELP_KEY = &H101 Public
Const HELP.COMMAND = &H102& Public
Const HELP_PARTIALKEY = &H105& Public
Const HELP_MULTIKEY = &H201& Public
Const HELP_SETWINPOS = &H2034 Public
Const HELP_FINDER = &HB
Вызов справочной системы из меню
Стандартный интерфейс приложений для Windows ХР предполагает (в соответствии с требованиями фирмы Microsoft), что единственной точкой для вызова справочной системы, является пункт меню Help одноименного меню Help (в приложениях MS Office пункт меню Help заменен на?).
После этого должно отобразится окно с содержанием файла справки. Эти действия могут быть выполнены вот таким кодом:
!Return=WinHelpNum (hWnd, sHelpFile, HELP_FINDER, 0&)
Первый параметр, hWnd, должен быть указателем (handle) на главное окно вашего приложения (или MDI формы в случае MDI интерфейса). Вам следует использовать одно и тоже значение hWnd при всех вызовах. Второй параметр, sHelpFile, — это строка, содержащая имя файла справочной системы (расширение HLP). Обычно, файл справки находится в том же каталоге, в котором находится ЕХЕ файл вашего приложения. В связи с этим вы можете не указывать путь к файлу, либо использовать следующие строки для формирования полного пути к HLP файлу:
sHelpFile=App.Path & «\myfile.hlp»
Вызов справки по нажатию кнопки
В сообществе разработчиков справочных систем такой вид помощи принято называть «контекстно-зависимая помощь на уровне диалога». Обычно, каждое окно диалога (BorderStyle-Fixed Dialog) должно содержать кнопку Помощь. После нажатия кнопки пользователь получает справку, описывающую отображаемый в настоящий момент диалог. В те времена, когда справочной системы What's This (справка, отображаемая после того, как пользователь нажимает кнопку ?, расположенную в правом верхнем углу диалога, а затем щелкает по непонятному элементу формы) не было, отображаемое окно помощи содержало экранный снимок диалога.
В случае, если вы используете режим What's This, то нет необходимости помещать в файл справки экранный снимок окна диалога. Вот такой пример кода может использоваться для вызова справки:
lReturn=WinHelpNum (hWnd, sHelpFile, HELP_CONTEXT, IContextID)
Параметр hWnd мы уже рассмотрели, sHelpFile содержит имя файла справочной системы (и может также указывать наименование окна, в котором следует отобразить помощь). Параметр IContextID содержит номер топика, который обычно указан в свойстве HelpContextID формы.
Справочная система What's This
Справочная система What'sThis — это новшество, представленное впервые в новом пользовательском интерфейсе Windows XP. В сообществе разработчиков справочных систем такой вид помощи принято называть «контекстно-зависимая помощь на уровне элемента формы». Вы можете использовать этот вид помощи в качестве расширенных tooltip (подсказки, возникающие при задержке указателя мыши над каким-либо элементом на панели инструментов). Однако, в отличие от tooltip, справка What's This может содержать графические изображения. Для Visual Basic 6.3 программистов нет необходимости использовать WinAPI для реализации помощи типа What's This. Следует лишь выполнить ряд простых действий.
В событие Form_Load (или в процедуре Sub Main) добавьте вот такой код:
App.HelpFile=App.Path & «\helpfile.hlp»
Для каждого элемента экранной формы создайте топик, описывающий его назначение. Каждому топику следует присвоить уникальный номер. Некоторые элементы формы (к примеру, кнопки ОК или Отмена) могут иметь одинаковые топики. Как вы знаете, некоторые элементы формы могут выступать в качестве контейнеров (к примеру, элемент FRAME). Каждый элемент, помещенный в контейнер, может иметь свой собственный контекстный ID, а контейнер — свой. Свойству WhatThisHelpID (есть практически у всех элементов) каждого элемента формы присвойте номер топика, в котором описывается данный элемент формы. Затем установите свойства формы:
WhatThisHelp=True WhatThisButton=True
Для вызова подсказки What's This программно, используйте такой код:
lReturn=WinHelpNum (hWnd, sHelpFile, _HELP_CONTEXTPOPUP,
IContextID)
IContextID указывает номер топика в HLP файле. Этот код можно использовать при нажатии кнопки F1. Есть одна маленькая неприятность, которую придется обойти. В случае, если вы установили свойство формы WhatThisHelp=True, то клавиша F1 (в соответствии с документацией) должна перестать работать. Однако, она продолжает работать и при нажатии отображает контекстную подсказку по элементу формы, над которым расположен указатель мыши. Поэтому, если вы хотите обеспечить контекстную подсказку именно по тому элементу формы, на который установлен фокус (а не по тому, над которым указатель мыши), то следует поступить так:
Form.ActiveControl.WhatsThisHelpID
ActiveForm.ActiveControl.WhatsThisHelpID Screen.
ActiveControl.WhatsThisHelpID
В случае, если вы хотите полностью запретить справку What'sThis для некоторого элемента формы, то установите WhatsThisHeIpID=-l
Завершаем работу
Когда приложение завершает работу, вам следует вызвать функцию WinAPI, в последний раз для завершения работы справочной системы. В случае, если пользователь ни разу не обращался к системе помощи во время работы приложения, то ничего и не произойдет. В случае, если же к справочной системе обращались, и окно программы WinHelp открыто, то следующий код завершит работу справочной системы:
IReturn=WinHelpNum (hWnd, sHelpFile, HELP_QUIT, 0&)
В этом вызове важно учитывать, что значения hWnd и sHelpFile должны быть теми же, что и при вызове WinAPI функций.
Добавление иконки в SystemTray средствами Visual Basic 6.3
Единственная функция для работы с иконкой Shell_NotifyIcori. Ее описание на Visual Basic 6.3 выглядит так:
Declare Function Shell_NotifyIcon Lib «shell32.dll» Alias
«ShellJIotifylconA» _ (ByVal dwMessage As dwMess, IpData As NOTI-
FYICONDATA) As Long
Возвращает ноль в случае ошибки.
Тип dwMess описывается так:
Public Enum dwMess
NIM_ADD = &HO ' Добавление иконки
NIM_DELETE = &H2 ' Удаление иконки
NIM_MODIFY = &Н1 ' Изменение параметров иконки
End Enum
Переменная dwMessage должна иметь одно из этих значений.
Тип NOTIFYICONDATA имеет следующую структуру:
Type NOTIFYICONDATA
cbSize As Long ' Размер переменной типа NOTIFYICONDATA
hwnd As Long ' Указатель окна создающего иконку
uID As Long ' Указатель на иконку в пределах приложения
uFlags As uF ' Маска для следующих параметров
uCallbackMessage As CallMess ' Возвращаемое событие
hlcon As Long ' Указатель на изображение для иконки
szTip As String * 64 ' Всплывающий над иконкой текст
End Type
Где тип uF имеет вид:
Public Enum uF
NIF_MESSAGE = &H1 ' Значение имеет uCallbackMessage
NIF_ICON = &H2 ' Значение имеет hlcon
NIF_TIP = &H4 ' Значение имеет szTip
End Enum
Эти константы можно применять в любых сочетаниях, для определения какой из параметров имеет значение.
Тип ->:
Public Enum CallMess
WM_MOUSEMOVE = &H200
WM_LBUTTONDOWN = &H201
WM.LBUTTONUP = &H202
WM.LBUTTONDBLCLK = &H203
WM.RBUTTONDOWN = &H204
WM_RBUTTONUP = &H205
WM_RBUTTONDBLCLK = &H206
WM_MBUTTONDOWN = &H207
WM_MBUTTONUP = &H208
WM_MBUTTONDBLCLK = &H209
WM_SETFOCUS = &H7
WM_KEYDOWN = &H100
WM_KEYFIRST = &H100
WM_KEYLAST = &H108
WM.KEYUP = &H101
End Enum
Эти константы обозначают, какое событие возвращается вызывающей форме. Буквально, все, что будет происходить с иконкой, будет вызывать у формы одно из перечисленных событий. Ясно, что самое частое
событие самой иконки это MouseMove, но для формы оно будет выглядеть как событие заданное переменной uCallbackMessage. Как же узнать, что в действительности произошло с иконкой? Это можно узнать через переменные X и Y событий MouseMove, MouseDown и MouseUp вызывающей формы. При этом Y, если событие произошло с иконкой, а не формой, всегда будет равно нулю, а X несет информацию о событии с иконкой.
О параметре X следует сказать отдельно. Действительно, он передает информацию о событиях с иконкой, однако эти значения зависят от масштабного коэффициента системного шрифта, но не напрямую, а через параметр свойства TwipsPerPixelX объекта Screen. To есть, для одной и той же системы, при разных величинах системного шрифта, значения будут разными. Начальными значениями событий являются следующие:
Для того, чтобы узнать действующие в данной системе значения, их следует умножить на Screen.
Как же узнать, что событие произошло с иконкой, а не с формой? Просто, по значению Y, равному нулю. Но есть и другой способ, если используется двухкнопочная мышь, то параметр Button в событиях MouseDown и MouseUp формы, будет принимать значения 1 и 2, и при b равно
WM_MBUTTONDOWN=&H207 или WM_MBUTTONUP = &H208
Button равен 4, если событие с иконкой. Само собой разумеется, что возвращаемые X значения следуют одно за другим, как и события (Down -> Up ->DbClick),поэтому невозможно на одну кнопку мыши назначить два события, к примеру, Click и DbClick. События, не связанные с мышью, не несут практически никакой информации, и обычно не используются, следует также отметить, что количество констант uCallbackMessage намного больше и здесь приведена лишь небольшая часть.
Из описанного видно, что с иконкой можно совершить одно из следующих действий: добавить, модифицировать и удалить, при этом, модифицируя можно заменить возвращаемое событие, картинку (указатель при этом останется тем же) и всплывающую надпись (ToolTips).
Следующий момент, который нужно осветить — это получение hIсоn (указателя на картинку). Предполагается, что иконка будет наодит-ся в исполняемом файле или в DLL с ресурсами, но ни в коем случае не является в виде ICO файла. В случае, если иконка запакована в DLL, то нам понадобятся две функции:
Declare Function LoadLibrary Lib «kernel32» Alias «LoadLibraryA»
(ByVal _ IpLibFileName As String) As Long
возвращающая hInstance библиотеки с именем IpLibFileName. Достаточно указать только имя файла с расширением, без пути. Возвращает ноль в случае ошибки.
Declare Function LoadlconA Lib «user32» (ByVal hlnstance As Long,
ByVal _ IpIconName As String) As Long
возвращающая hlcon для иконки указанной параметром IpIconName в библиотеке. Этот параметр может быть String или Long, в зависимости от данного вами наименования в Res файле, соответственно надо изменить декларацию. Можно передать и число как строку, для этого перед числом ставится знак #, а все это берется в кавычки. Следует заметить, что использование срокового параметра не желательно из-за значительно большего размера занимаемой памяти и соответственно, большего времени на передачу параметра. Функция возвращает ноль в случае ошибки.
Понадобится так же функция:
Declare Function FreeLibrary Lib «kerne!32» (ByVal hLibModule As Long) As Long
выгружающая библиотеку из памяти. Параметр hLibModule — это hlnstanse, возвращаемое LoadLibrary. Возвращает ноль в случае ошибки.
Обязательно надо не забыть выгрузить из памяти библиотеку, для освобождения памяти. Выгрузку можно произвести сразу же после добавления иконки в SystemTray:
Declare Function GetModuleHandle Lib «kerne!32» Alias «GetModuleHandleA» _ (ByVal IpModuleName As String) As Long
возвращающей hInstanse нашего приложения. В качестве IpModuleName передается имя EXE файла с расширением. Следует быть внимательным, так как имя процесса в TaskMenager не всегда соответствует имени процесса для Windows. Модно использовать для определения имени DLLView, можно воспользоваться встроенным в Visual Basic 6.3 System Information. Функция возвращает действительное значение только при работе скомпилированного приложения, а в режиме отладки возвращает ноль, ведь реального процесса при отладке не существует. Свойство hlnstanse объекта Арр всегда возвращает действительное значение, однако при отладке из-за отсутствия процесса Loadlcon возвращает 0, и создается «пустая» иконка, тем не менее годная для отладки (реагирующая на все события).
Полученное hlnstanse передаем LoadlconA, в качестве IpIconName указываем номер или имя иконки в Res файле, как и в случае с DLL. Выгружать в этом случае ничего не надо.
Создание иконки можно проиллюстрировать следующим примером. Считается, что иконка с номером 101 находится в файле Projectl.exe. Понятно, что пока мы его не скомпилировали, ее там нет (да и самого файла нет). Форма приложения называется Form 1.
Dim NID As NOTIFYICONDATA
Sub Addlcon()
Dim IDLib As Long ' Указатель на библиотеку
Dim IDIcon As Long ' Указатель на иконку
Const IDMylcon =101 ' Идентификатор иконки внутри приложения
Dim AddResult As Long ' Результат добавления иконки
IDLib = GetModuleHandle(«Project1.exe») ' Получаем hlnstanse
IDIcon = LoadIcon(IDLib, «#101») ' Получаем hlcon
' Заполняем структуру NID типа NOTIFYICONDATA
NID.cbSize = Len(NID) ' Размер структуры
NID.hwnd = Forml.hWnd ' Указатель на форму
NID.uID = IDMylcon ' Идентификатор иконки
NID.uFlags = NIF_MESSAGE + NIF_ICON + NIF_TIP
' Указываем, что действующими являются поля uCallBackMessage, hlcon и szTip
NID.uCallbackMessage = WM_LBUTTONDOWN
' Указываем, что событием возвращаемым в форму является MouseDown с параметром Button = 2
NID.hlcon = IDIcon ' Указатель на иконку в файле
NID.szTip = Left$(«MyIcon», 63) & Chr(O)
' Передаем всплывающую фразу «Mylcon», при этом обрезаем ее до 63 символов и добавляем 64-й символ с кодом ноль
AddResult = Shell_NotifyIcon(NIM_ADD, NID)
' Вызываем функцию, через параметр dwMessage указываем, что следует добавить иконку, и передаем заполненный NID
End Sub
Удаление созданной иконки можно сделать так:
Sub Deletelcon()
Dim DeletResult As Long
DeleteResult = Shell_NotifyIcon(NIM_DELETE, NID)
' Вызываем функцию, через dwMessage указываем, что следует удалить
иконку, при этом, раз переменная NID описана на уровне модуля, не ,
следует заполнять ее заново
End Sub
Размер структуры достаточно указывать один раз, так как за время жизни переменной он измениться не может, и в данном виде составляет 88 байт.
Даже при изменении всплывающей строки ее длина (строки) не будет больше 64 байт.
Для модификации иконки надо вызвать Shell_NotifyIcon с параметром dwMessage равным NIM_MODIFY и NID с внесенными изменениями, при этом параметр uFlags будет указывать, какие из параметров изменены.
В форме Forml для обработки, к примеру, DbClick левой кнопкой мыши по иконке можно применить следующий код:
Private Sub Form_MouseDown(Button As Integer, Shift As Integer _
X As Single, Y As Single)
' Событие MouseDown происходит не потому, что пользователь нажал
на кнопку мыши над иконкой, а из-за того, что параметр
uCallbackMessage имеет значение WM_LBUTTONDOWN
If Y = 0 Then
' Y = 0 если событие с иконкой Select Case X
Case 515*Screen.TwipsPerPixelX
' Значение X при LeftDblClick
' Код, выполняемый в случае LeftDblClick
End Select
End If
End Sub
Понятие же "физического вакуума" в релятивистской квантовой теории поля подразумевает, что во-первых, он не имеет физической природы, в нем лишь виртуальные частицы у которых нет физической системы отсчета, это "фантомы", во-вторых, "физический вакуум" - это наинизшее состояние поля, "нуль-точка", что противоречит реальным фактам, так как, на самом деле, вся энергия материи содержится в эфире и нет иной энергии и иного носителя полей и вещества кроме самого эфира.
В отличие от лукавого понятия "физический вакуум", как бы совместимого с релятивизмом, понятие "эфир" подразумевает наличие базового уровня всей физической материи, имеющего как собственную систему отсчета (обнаруживаемую экспериментально, например, через фоновое космичекое излучение, - тепловое излучение самого эфира), так и являющимся носителем 100% энергии вселенной, а не "нуль-точкой" или "остаточными", "нулевыми колебаниями пространства". Подробнее читайте в FAQ по эфирной физике.
|
![]() |