Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Леонтьев Б.К. Я изучаю Microsoft Office Visio 2003 (PDF)

.pdf
Скачиваний:
681
Добавлен:
02.05.2014
Размер:
919.27 Кб
Скачать

Использование редактора Visual Basic

341

 

 

 

lpThreadId — переменная, в которую загружается уникальный идентификатором нового потока.

Функция возвращает дескриптор потока.

В этом случае мы передаем указатель на объект 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 IUnknown) 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 IUnknown). При этом мы можем избежать неприятнос тей, потому что под COM каждый интерфейс основывается на IUnknown, так что такой тип параметра допустим независимо от типа интерфейса, передаваемого функции. Мы, однако, должны немедленно определить param как тип объекта, чтобы затем его использовать. В этом случае qobj устанавливается как объект clsBackground, который был передан к объек ту StartBackgroundThreadFree.

Функция затем выполняет бесконечный цикл, в течение которого может выполняться любая требуемая операция, в этом случае использу ется повторный счет. Подобный подход мог бы использоваться здесь, чтобы выполнить операцию ожидания, которая приостанавливает поток

342

Использование редактора Visual Basic

 

 

 

пока не произойдет системное событие (типа завершения процесса). По ток затем мог бы вызвать метод класса, чтобы сообщить приложению, что событие произошло.

Доступ к объекту qobj чрезвычайно быстр из за использования подхода свободного потока (free threading) — никакая переадресация (marshalling) при этом не используется.

Обратите внимание на то, что если вы попробуете использовать объект clsBackground, который показывает форму, то это приведет к сбо ям приложения. Обратите также внимание на то, что событие заверше ния никогда не происходит в клиентской форме.

Действительно, даже Microsoft Systems Journal, который описы вает этот подход, содержит очень много предупреждений о том, что при использовании этого подхода есть некоторые вещи, которые не работают.

Является ли это дефектом в Visual Basic?

Некоторые разработчики, кто пробовали развертывать приложе ния, применяющие этот тип многопоточности, обнаружили, что их при ложения вызывают сбои после обновления.

Означает ли эта проблема, что Microsoft не сумел правильно

обеспечить обратную совместимость?

Ответ на оба вопроса: Нет Проблема не в Microsoft или Visual

Basic.

Вышеупомянутый код является мусором. Проблема проста — Visual Basic поддерживает объекты и в модели одиночного потока и в apartment model.

Что это означает?

Поведение объекта подчиненно изменениям, так как Visual Basic постоянно модифицируется.

Это означает, что любая попытка объекта обратиться к другим объектам или формам может потерпеть неудачу и что причины отказов могут изменяться, поскольку эти объекты модифицируются.

Даже код, который сейчас работает, может внезапно вызвать сбой, поскольку другие объекты добавляются, удаляются или изменяются.

Это означает, что невозможно характеризовать поведение прило жения или предсказать, будет ли оно работать или может ли работать в любой данной среде.

Невозможно предсказать, будет ли код работать на любой данной системе, и что его поведение может зависеть от используемой операци

Использование редактора Visual Basic

343

 

 

 

онной, числа используемых процессоров и других проблем конфигура ции системы.

Вы видите, что как только вы нарушаете соглашение COM, вы больше не защищены теми возможностями COM, которые позволяют объектам успешно взаимодействовать друг с другом и с клиентами.

Этот подход является программной алхимией. Это безответствен но и ни один программист не должен поступать таким образом.

Обратно к функции API CreateThread

Теперь, когда стало понятно, почему подход к использованию CreateThread API является мусором, покажем, как можно использовать эту API функцию безопасно.

Прием прост — вы должны просто твердо придерживаться согла шения COM о потоках. Это займет немного больше времени и усилий, но практика показала, что получаются очень надежные результаты.

Пример MTDEMO3 демонстрирует этот подход в форме frm# MTDemo3, имеющей код, который запускает класс фона в apartment model следующим образом:

Private Sub cmdCreateApt_Click() Set c = New clsBackground StartBackgroundThreadApt c

End Sub

Пока этот процесс очень похож на подход свободных потоков. Вы создаете экземпляр класса и передаете его функции, которая запускает фоновый поток. В модуле modMTBack появляется следующий код:

' Structure to hold IDispatch GUID Type GUID

Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte

End Type

Public IID_IDispatch As GUID

Declare Function CoMarshalInterThreadInterfaceInStream Lib "ole32.dll" _

(riid As GUID, ByVal pUnk As IUnknown, ppStm As Long) As Long Declare Function CoGetInterfaceAndReleaseStream Lib "ole32.dll" _ (ByVal pStm As Long, riid As GUID, pUnk As IUnknown) As Long Declare Function CoInitialize Lib "ole32.dll" (ByVal pvReserved As Long) As Long

Declare Sub CoUninitialize Lib "ole32.dll" ()

344

Использование редактора Visual Basic

 

 

 

'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 InitializeIID

res = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, qobj, threadparam)

If res <> 0 Then StartBackgroundThreadApt = 0 Exit Function

End If

hnd = CreateThread(0, 2003, AddressOf BackgroundFuncApt, thread param, 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 немного более сложна, чем ее эквивалент при применении подхода свободных потоков. Первая новая функция называется InitializeIID. Ее код таков:

' Initialize the GUID structure Private Sub InitializeIID()

Static Initialized As Boolean If Initialized Then Exit Sub With IID_IDispatch

.Data1 = &H20400

.Data2 = 0

.Data3 = 0

.Data4(0) = &HC0

.Data4(7) = &H46

End With

Использование редактора Visual Basic

345

 

 

 

Initialized = True End Sub

Вы видите, нам необходим идентификатор интерфейса — 16 бай товая структура, которая уникально определяет интерфейс. В частности, нам нужен идентификатор для интерфейса IDispatch. Функция InitializeIID инициализирует структуру IID_IDISPATCH к корректным значениям для идентификатора интерфейса IDispatch. Это значение по лучается с помощью использования утилиты просмотра системного рее стра.

Зачем нам этот идентификатор? Чтобы твердо придерживаться со глашения COM о потоках, мы должны создать промежуточный объект (proxy object) для объекта clsBackground. Промежуточный объект должен быть передан новому потоку вместо первоначального объекта. Обраще ния к новому потоку на промежуточном объекте будут переадресованы (marshaled) в текущий поток.

CoMarshalInterThreadInterfaceInStream выполняет интересную за дачу. Она собирает всю информацию, необходимую при создании про межуточного объекта, для определенного интерфейса и загружает ее в объект потока (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 clsBackground 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(0)

'Proper apartment modeled approach

res = CoGetInterfaceAndReleaseStream(param, IID_IDispatch, qobj) Set qobj2 = qobj

Do While Not qobj2.DoTheCount(10000)

346

Использование редактора Visual Basic

 

 

 

Loop qobj2.ShowAForm

'Alternatively, you can put a wait function here,

'then call the qobj function when the wait is satisfied

'All calls to CoInitialize must be balanced CoUninitialize

End Function

Первый шаг должен инициализировать подсистему OLE для но вого потока. Это необходимо для переадресации (marshalling) кода, что бы работать корректно. CoGetInterfaceAndReleaseStream создает проме жуточный объект для объекта clsBackground и реализует объект потока (stream object), используемый для передачи данных из другого потока. Интерфейс IDispatch для нового объекта загружается в переменную qobj. Теперь возможно получить другие интерфейсы — промежуточный объ ект будет корректно переадресовывать данные для каждого интерфейса, который может поддерживать.

Теперь вы можете видеть, почему цикл помещен в эту функцию вместо того, чтобы находиться непосредственно в объекте. Когда вы впервые вызовите функцию qobj2.DoTheCount, то увидите, что код вы полняется в начальном потоке. Каждый раз, кликая на метод объекта, вы фактически вызываете метод промежуточного объекта. Текущий поток приостанавливается, запрос метода переадресовывается первоначально му потоку и вызывается метод первоначального объекта в той же самом потоке, который создал объект. В случае, если бы цикл был в объекте, то вы бы заморозили первоначальный поток.

Хорошим результатом применения этого подхода является то, что все работает правильно. Объект clsBackground может безопасно показы вать формы и генерировать события. Недостатком этого подхода являет ся, конечно, его более медленное исполнение. Переключение потоков и переадресация (marshalling) — относительно медленные операции. Вы фактически никогда не захотите выполнять фоновую операцию как по казано здесь.

Но этот подход может чрезвычайно хорошо работать, если вы мо жете помещать фоновую операцию непосредственно в функцию BackgroundFuncApt! Например, вы могли бы иметь фоновый поток, вы полняющий фоновые вычисления или операцию ожидания системы. Когда они будут завершены, вы можете вызывать метод объекта, кото рый сгенерирует событие в клиенте. Храня количество вызовов метода, небольшое относительно количества работы, выполняемой в фоновой функции, вы можете достигать очень эффективных результатов.

Что, если вы хотите выполнить фоновую операцию, которая не должна использовать объект? Очевидно, проблемы с соглашением COM

Использование редактора Visual Basic

347

 

 

 

о потоках исчезают. Но появляются другие проблемы. Как фоновый по ток сообщит о своем завершении приоритетному потоку? Как они обме ниваются данными? Как два потока будут синхронизированы? Все это возможно выполнить с помощью соответствующих вызовов API.

Вы сможете использовать глобальные переменные, чтобы обме ниваться данные, но надо знать, что такое поведение не гарантируется Visual Basic (другими словами, даже если это сейчас работает, не имеется никаких гарантий, что это будет работать в будущем). В этом случае мож но использовать для обмена данными методики, основанные на API. Од нако, преимуществом показанного здесь подхода, основанного на объек тах, является то, что этот подход делает проблему обмена данными между потоками тривиальной, просто делайте это через объект.

Глава 14.

Создание справочных систем

Рано или поздно встает необходимость создания справочной сис темы для вашего приложения. В конце концов, наличие справочной си стемы придает программе завершенность и, в некотором роде, поднима ет ваш имидж, как программиста профессионала.

Приготовления

В большинстве случаев работа с WinHelp из вашего приложения осуществляется посредством вызовов функций WinAPI, являющихся ча стью операционной системы Windows. Перед использованием WinAPI функций их следует объявить в BAS модуле вашего проекта. Например, можно использовать следующие объявления:

Public Declare Function WinHelpStr Lib "user32" Alias "WinHelpA" _ (ByVal hWnd As Long, ByVal lpHelpFile 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 lpHelpFile As String,

_ByVal wCommand As Long, ByVal dwData As Long) As Long

Как вы видите, функция объявлена дважды. Последний параметр в списке объясняет причину. В зависимости от параметра wCommand па раметр dwData может быть либо типа integer (32 битное значение типа long), либо строкой или структурой (32 битный указатель). Несмотря на то, что вы можете использовать одно и тоже объявление для обоих типов вызова, вам придется явно преобразовывать передаваемые параметры к соответствующему типу. Легче иметь два разных вызова для разных пара метров (один для параметра типа integer и другой для строк). Вам также

348

Использование редактора Visual Basic

 

 

 

понадобятся следующие константы, которые следует поместить в тот же модуль, где объявлена функция 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 = &H9& Public

Const HELP_KEY = &H101 Public

Const HELP_COMMAND = &H102& Public

Const HELP_PARTIALKEY = &H105& Public

Const HELP_MULTIKEY = &H201& Public

Const HELP_SETWINPOS = &H203& Public

Const HELP_FINDER = &HB

Вызов справочной системы из меню

Стандартный интерфейс приложений для Windows XP предпола гает (в соответствии с требованиями фирмы Microsoft), что единствен ной точкой для вызова справочной системы, является пункт меню Help одноименного меню Help (в приложениях MS Office пункт меню Help за менен на ?).

После этого должно отобразится окно с содержанием файла справки. Эти действия могут быть выполнены вот таким кодом:

lReturn=WinHelpNum (hWnd, sHelpFile, HELP_FINDER, 0&)

Первый параметр, hWnd, должен быть указателем (handle) на глав ное окно вашего приложения (или MDI формы в случае MDI интерфей са). Вам следует использовать одно и тоже значение hWnd при всех вызо вах. Второй параметр, sHelpFile, — это строка, содержащая имя файла справочной системы (расширение HLP). Обычно, файл справки нахо дится в том же каталоге, в котором находится EXE файл вашего приложе ния. В связи с этим вы можете не указывать путь к файлу, либо исполь зовать следующие строки для формирования полного пути к HLP файлу:

sHelpFile=App.Path & "\myfile.hlp"

Вызов справки по нажатию кнопки

В сообществе разработчиков справочных систем такой вид помо щи принято называть «контекстно зависимая помощь на уровне диало га». Обычно, каждое окно диалога (BorderStyle#Fixed Dialog) должно со держать кнопку Помощь. После нажатия кнопки пользователь получает

Использование редактора Visual Basic

349

 

 

 

справку, описывающую отображаемый в настоящий момент диалог. В те времена, когда справочной системы What's This (справка, отображаемая после того, как пользователь нажимает кнопку ?, расположенную в пра вом верхнем углу диалога, а затем щелкает по непонятному элементу формы) не было, отображаемое окно помощи содержало экранный сни мок диалога.

В случае, если вы используете режим What's This, то нет необходи мости помещать в файл справки экранный снимок окна диалога. Вот та кой пример кода может использоваться для вызова справки:

lReturn=WinHelpNum (hWnd, sHelpFile, HELP_CONTEXT, lContextID)

Параметр hWnd мы уже рассмотрели, sHelpFile содержит имя фай ла справочной системы (и может также указывать наименование окна, в котором следует отобразить помощь). Параметр lContextID содержит но мер топика, который обычно указан в свойстве 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, а контейнер — свой. Свойству WhatThis# HelpID (есть практически у всех элементов) каждого элемента формы присвойте номер топика, в котором описывается данный элемент фор мы. Затем установите свойства формы:

WhatThisHelp=True WhatThisButton=True

350

Использование редактора Visual Basic

 

 

 

Для вызова подсказки What's This программно, используйте такой

код:

lReturn=WinHelpNum (hWnd, sHelpFile, _HELP_CONTEXTPOPUP, lContextID)

lContextID указывает номер топика в HLP файле. Этот код можно использовать при нажатии кнопки F1. Есть одна маленькая неприят ность, которую придется обойти. В случае, если вы установили свойство формы WhatThisHelp=True, то клавиша F1 (в соответствии с документа цией) должна перестать работать. Однако, она продолжает работать и при нажатии отображает контекстную подсказку по элементу формы, над которым расположен указатель мыши. Поэтому, если вы хотите обеспечить контекстную подсказку именно по тому элементу формы, на который установлен фокус (а не по тому, над которым указатель мыши), то следует поступить так:

Установите свойство формы KeyPreview=True. Таким образом, форма первой получает возможность обработать нажатие кнопки.

В событии KeyDown формы отследите клавишу F1. Как только клавиша перехвачена, установите свойство key pre# view в значение 0. Затем используйте вызов WinAPI функции, в котором значение lContextID можно определить следующим образом:

Form.ActiveControl.WhatsThisHelpID ActiveForm.ActiveControl.WhatsThisHelpID Screen. ActiveControl.WhatsThisHelpID

Если вы хотите полностью запретить справку What'sThis для некоторого элемента формы, то установите

WhatsThisHelpID=#1

Завершаем работу

Когда приложение завершает работу, вам следует вызвать функ цию WinAPI, в последний раз для завершения работы справочной систе мы. В случае, если пользователь ни разу не обращался к системе помощи во время работы приложения, то ничего и не произойдет. В случае, если же к справочной системе обращались, и окно программы WinHelp откры то, то следующий код завершит работу справочной системы:

lReturn=WinHelpNum (hWnd, sHelpFile, HELP_QUIT, 0&)

В этом вызове важно учитывать, что значения hWnd и sHelpFile должны быть теми же, что и при вызове WinAPI функций.

Использование редактора Visual Basic

351

 

 

 

Добавление иконки в SystemTray средствами Visual Basic 6.3

Единственная функция для работы с иконкой Shell_NotifyIcon. Ее описание на Visual Basic 6.3 выглядит так:

Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA" _ (ByVal dwMessage As dwMess, lpData As NOTI FYICONDATA) As Long

Возвращает ноль в случае ошибки. Тип dwMess описывается так:

Public Enum dwMess

NIM_ADD = &H0 ' Добавление иконки

NIM_DELETE = &H2 ' Удаление иконки

NIM_MODIFY = &H1 ' Изменение параметров иконки End Enum

Переменная dwMessage должна иметь одно из этих значений. Тип NOTIFYICONDATA имеет следующую структуру:

Type NOTIFYICONDATA

cbSize As Long ' Размер переменной типа NOTIFYICONDATA hwnd As Long ' Указатель окна создающего иконку

uID As Long ' Указатель на иконку в пределах приложения uFlags As uF ' Маска для следующих параметров uCallbackMessage As CallMess ' Возвращаемое событие hIcon As Long ' Указатель на изображение для иконки szTip As String * 64 ' Всплывающий над иконкой текст End Type

Где тип uF имеет вид:

Public Enum uF

NIF_MESSAGE = &H1 ' Значение имеет uCallbackMessage NIF_ICON = &H2 ' Значение имеет hIcon

NIF_TIP = &H4 ' Значение имеет szTip End Enum

Эти константы можно применять в любых сочетаниях, для опре деления какой из параметров имеет значение.

Тип CallMess:

Public Enum CallMess

WM_MOUSEMOVE = &H200

WM_LBUTTONDOWN = &H201

WM_LBUTTONUP = &H202

WM_LBUTTONDBLCLK = &H203

WM_RBUTTONDOWN = &H204

352 Использование редактора Visual Basic

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. То есть, для одной и той же системы, при разных величинах системного шрифта, значения будут разными. Начальными значениями событий являются следующие:

MouseMove — 512

LeftButtonDown — 513

LeftButtonUp — 514

LeftButtonDblClick — 515

RightButtonDown — 516

RightButtonUp — 517

RightButtonDblClick — 518

Для того, чтобы узнать действующие в данной системе значения, их следует умножить на Screen.

Как же узнать, что событие произошло с иконкой, а не с формой? Просто, по значению Y, равному нулю. Но есть и другой способ, если ис пользуется двухкнопочная мышь, то параметр Button в событиях Mo#

Использование редактора Visual Basic

353

 

 

 

useDown и MouseUp формы, будет принимать значения 1 и 2, и при uCallbackMessage равно

WM_MBUTTONDOWN=&H207 или WM_MBUTTONUP = &H208

Button равен 4, если событие с иконкой. Само собой разумеется, что возвращаемые X значения следуют одно за другим, как и события (Down Up DbClick),поэтому невозможно на одну кнопку мыши на значить два события, к примеру, Click и DbClick. События, не связанные с мышью, не несут практически никакой информации, и обычно не ис пользуются, следует также отметить, что количество констант uCallbackMessage намного больше и здесь приведена лишь небольшая часть.

Из описанного видно, что с иконкой можно совершить одно из следующих действий: добавить, модифицировать и удалить, при этом, модифицируя можно заменить возвращаемое событие, картинку (указа тель при этом останется тем же) и всплывающую надпись (ToolTips).

Следующий момент, который нужно осветить — это получение hIcon (указателя на картинку). Предполагается, что иконка будет нахо диться в исполняемом файле или в DLL с ресурсами, но ни в коем случае не является в виде ICO файла. В случае, если иконка запакована в DLL, то нам понадобятся две функции:

Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal _ lpLibFileName As String) As Long

возвращающая hInstance библиотеки с именем lpLibFileName. До статочно указать только имя файла с расширением, без пути. Возвраща ет ноль в случае ошибки.

Declare Function LoadIconA Lib "user32" (ByVal hInstance As Long, ByVal _ lpIconName As String) As Long

возвращающая hIcon для иконки указанной параметром lpIcon# Name в библиотеке. Этот параметр может быть String или Long, в зависи мости от данного вами наименования в Res файле, соответственно надо изменить декларацию. Можно передать и число как строку, для этого пе ред числом ставится знак #, а все это берется в кавычки. Следует заме тить, что использование строкового параметра не желательно из за зна чительно большего размера занимаемой памяти и соответственно, большего времени на передачу параметра. Функция возвращает ноль в случае ошибки.

Понадобится так же функция:

Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long

выгружающая библиотеку из памяти. Параметр hLibModule — это hInstanse, возвращаемое LoadLibrary. Возвращает ноль в случае ошибки.

354

Использование редактора Visual Basic

 

 

 

Обязательно надо не забыть выгрузить из памяти библиотеку, для освобождения памяти. Выгрузку можно произвести сразу же после до бавления иконки в SystemTray:

Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" _ (ByVal lpModuleName As String) As Long

возвращающей hInstanse нашего приложения.

В качестве lpModuleName передается имя EXE файла с расшире нием. Следует быть внимательным, так как имя процесса в TaskMenager не всегда соответствует имени процесса для Windows. Модно использо вать для определения имени DLLView, можно воспользоваться встроен ным в Visual Basic 6.3 System Information. Функция возвращает действи тельное значение только при работе скомпилированного приложения, а в режиме отладки возвращает ноль, ведь реального процесса при отладке не существует. Свойство hInstanse объекта App всегда возвращает дейст вительное значение, однако при отладке из за отсутствия процесса LoadIcon возвращает 0, и создается «пустая» иконка, тем не менее годная для отладки (реагирующая на все события).

Полученное hInstanse передаем LoadIconA, в качестве lpIconName указываем номер или имя иконки в Res файле, как и в случае с DLL. Вы гружать в этом случае ничего не надо.

Создание иконки можно проиллюстрировать следующим приме ром. Считается, что иконка с номером 101 находится в файле Pro ject1.exe. Понятно, что пока мы его не скомпилировали, ее там нет (да и самого файла нет). Форма приложения называется Form1.

Dim NID As NOTIFYICONDATA Sub AddIcon()

Dim IDLib As Long ' Указатель на библиотеку Dim IDIcon As Long ' Указатель на иконку

Const IDMyIcon = 101 ' Идентификатор иконки внутри приложения Dim AddResult As Long ' Результат добавления иконки

IDLib = GetModuleHandle("Project1.exe") ' Получаем hInstanse IDIcon = LoadIcon(IDLib, "#101") ' Получаем hIcon

'Заполняем структуру NID типа NOTIFYICONDATA NID.cbSize = Len(NID) ' Размер структуры NID.hwnd = Form1.hWnd ' Указатель на форму NID.uID = IDMyIcon ' Идентификатор иконки NID.uFlags = NIF_MESSAGE + NIF_ICON + NIF_TIP

'Указываем, что действующими являются поля uCallBackMessage, hIcon и szTip

NID.uCallbackMessage = WM_LBUTTONDOWN

'Указываем, что событием возвращаемым в форму является MouseDown с параметром Button = 2

Использование редактора Visual Basic

355

 

 

 

NID.hIcon = IDIcon ' Указатель на иконку в файле

NID.szTip = Left$(«MyIcon», 63) & Chr(0)

'Передаем всплывающую фразу "MyIcon", при этом обрезаем ее до 63 символов и добавляем 64 й символ с кодом ноль

AddResult = Shell_NotifyIcon(NIM_ADD, NID)

'Вызываем функцию, через параметр dwMessage указываем, что сле дует добавить иконку, и передаем заполненный NID

End Sub

Удаление созданной иконки можно сделать так:

Sub DeleteIcon()

Dim DeletResult As Long

DeleteResult = Shell_NotifyIcon(NIM_DELETE, NID)

' Вызываем функцию, через dwMessage указываем, что следует уда лить иконку, при этом, раз переменная NID описана на уровне моду ля, не следует заполнять ее заново

End Sub

Размер структуры достаточно указывать один раз, так как за время жизни переменной он измениться не может, и в данном виде составляет 88 байт.

Даже при изменении всплывающей строки ее длина (строки) не будет больше 64 байт.

Для модификации иконки надо вызвать Shell_NotifyIcon с параме тром dwMessage равным NIM_MODIFY и NID с внесенными изменени ями, при этом параметр uFlags будет указывать, какие из параметров из менены.

В форме Form1 для обработки, к примеру, 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

356

Формулы Microsoft Office Visio 2003

 

 

 

Часть 13.

Формулы Microsoft Office Visio 2003

Глава 1.

Окно ShapeSheet

Объекты Microsoft Office Visio сохраняются «внутри» в виде набо ра формул. Например, когда вы смотрите на образ в окне чертежа, вы ви дите его воспроизведенным графически, и он при этом ведет себя соглас но своим формулам.

Когда вы смотрите на тот же образ в окне ShapeSheet, вы видите лежащие в его основе формулы, которые определяют вид и поведение данного образа на чертежной странице. Эти два окна просто дают вам разные взгляды на один и тот же образ. В окне чертежа некоторые изме нения, которые вы производите с объектом, вызываются его формулами. Например, когда вы перемещаете образ Инструментом Указатель, прило жение Microsoft Office Visio пересчитывает формулы, определяющие центр вращения образа pin на странице, поскольку эти формулы отвеча ют за расположение образа на странице.

Однако окно ShapeSheet дает вам более точный контроль над ви дом и поведением объекта, поэтому для изменений его поведения вы мо жете редактировать формулы объекта. Где бы вы ни меняли объект — в окне чертежа или в окне ShapeSheet — все видоизменения автоматичес ки сохраняются, когда вы сохраняете документ Microsoft Office Visio, ко торый содержит данный объект.

Глава 2.

Вывод на экран окна ShapeSheet

Многие объекты Microsoft Office Visio — образы, группы, направ ляющие линии и точки, страницы, документы, стили, вставленные объ екты из других приложений — имеют лежащие в их основе формулы, ко торые вы можете редактировать и тем самым менять поведение объекта.

Формулы Microsoft Office Visio 2003

357

 

 

 

Для того, чтобы отредактировать формулы объекта, вы должны сначала вывести на экран окно ShapeSheet для этого объекта.

Когда окно ShapeSheet активно, поля меню содержат команды для работы с формулами объекта.

Вы можете редактировать формулы в выбранной ячейке или в по ле формулы.

Каждая часть ShapeSheet изображает набор связанных формул.

Вывод на экран окно ShapeSheet для объекта на чертежной странице

1.Укажите объект в окне чертежа.

2.Для того, чтобы указать образ внутри группы (если настройка его группового поведения позволяет это), сначала укажите группу, а за тем — образ.

3.Выберите Window Show ShapeSheet.

4.Для того, чтобы вывести на экран инструментальную панель разработчика, выберите View Toolbars, а затем нажмите Developer.

Совет: Для того, чтобы добавить команду Show ShapeSheet к всплывающему меню образов (щелчок правой кнопкой мыши), выбери те Tools Options, перейдите к следующей вкладке и укажите параметр

Run In Developer Mode.

Этот параметр добавляет подменю расширений Add#Ons к меню инструментов.

Чертежные страницы, стили, документы Microsoft Office Visio и шаблоны в трафаретах тоже имеют формулы, которые вы можете редак тировать.

Вывод на экран окно ShapeSheet для страницы, стиля или документа

1.Выберите View Windows Drawing Explorer. Появится окно Drawing Explorer.

2.Нажмите, чтобы открыть или закрыть папку.

3.В окне Drawing Explorer нажмите правой кнопкой мыши на до кументе, странице или желаемом стиле, а затем выберите из ниспадаю щего меню команду Show ShapeSheet.

Совет: Вы можете вывести на экран и окно ShapeSheet для страни цы, выбрав Window Show ShapeSheet и ничего не указав на странице.

358

Формулы Microsoft Office Visio 2003

 

 

 

Вывод на экран окно ShapeSheet для шаблона на трафарете

1.В случае, если шаблон находится в самостоятельном трафарете, то выберите File Stencils Open Stencil и укажите трафаретный файл, который содержит необходимый вам шаблон. Убедитесь, что в диалого вом окне Open Stencil указан параметр Original или Copy. В случае, если шаблон находится в трафарете открытого документа Microsoft Office Visio, то выберите Window Show Document Stencil.

2.В трафаретном окне Microsoft Office Visio нажмите правой кноп кой мыши на шаблоне и выберите всплывающего меню команду Редак# тировать шаблон (Edit Master). Кроме того, вы можете вывести на экран чертежное окно шаблона, щелкнув правой кнопкой мыши на шаблоне в окне проводника Drawing Explorer и выбрав из всплывающего меню ко манду Edit Master.

3.В чертежном окне шаблона, ничего не указывая, выберите

Window Show ShapeSheet. В случае, если указан параметр Developer Mode, то вы можете щелкнуть правой кнопкой мыши»на чертежном ок не шаблона и выбрать команду Show ShapeSheet.

Вывод на экран различных разделов окна ShapeSheet

Окно ShapeSheet поделено на секции ячейками, которые содержат формулы, определяющие взаимосвязанные аспекты вида и поведения объекта. Внутри себя приложение Microsoft Office Visio не изображает на экране все возможные секции окна ShapeSheet. Некоторые секции оста ются скрытыми просто для того, чтобы сберечь пространство экрана, другие появляются только тогда, когда они необходимы. Например, что бы создать команду, которая будет появляться в меню образа, вы должны дополнить образ секцией действия Actions либо с помощью команды Insert Section в окне ShapeSheet, либо через режим автоматизации.

Показ или сокрытие секции в окне ShapeSheet

1.Нажмите на титульной панели окна ShapeSheet и выберите View

Sections.

2.В диалоговом окне Разделы (Sections) укажите секции, которые хотите выбрать и отключите секции, которые хотите скрыть, а затем на жмите OK. В случае, если секция выглядит тусклой, то она не доступна, поскольку не существует для этого объекта.

Формулы Microsoft Office Visio 2003

359

 

 

 

Добавление секции, используя окно ShapeSheet

1.Выберите Insert Section.

2.В диалоговом окне Вставить секцию (Insert Section) укажите сек ции, которые хотите добавить к объекту, а затем нажмите OK.

Секции отображаются в окне ShapeSheet в фиксированном поряд ке (не выше или ниже секции, которую вы можете видеть), поэтому вам придется прокрутить окно ShapeSheet, чтобы найти вновь вставлен ные секции.

Геометрические секции не похожи на другие типы тем. Объект может иметь несколько геометрических секций и только одну секцию других типов. Указав параметр Geometry в диалоговом окне Insert Section, вы добавите «пустую» геометрическую секцию, которая содержит ряд

MoveTo и LineTo. Указав параметр Эллипс (Ellipse) или Бесконечная ли# ния (Infinite Line), вы добавите геометрическую секцию, которая содер жит ряд одного эллипса или бесконечной линии.

Совет: Вы можете расширить или сжать секцию в окне Shape# Sheet, просто щелкнув на имени секции.

Что «контролируют» секции ShapeSheet

Каждая секция ShapeSheet контролирует некоторый аспект объек та Microsoft Office Visio. Как разработчику образов, вам необходимо знать, какие секции контролируют поведение, которое вы хотите изме нить.

Проверка образа в окне ShapeSheet

1.Укажите образ в чертежном окне.

2.Выберите Window Show ShapeSheet, чтобы вывести на экран окно ShapeSheet.

3.Выберите Window Tile, чтобы расположить бок о бок окно ShapeSheet и чертежное окно.

Выбор определенных рядов или ячеек в окне ShapeSheet выявляет соответствующий вертекс в чертежном окне.

Для этого, нарисуйте простой образ с прямыми линейными сег ментами (к примеру, прямоугольник или многоугольник), а затем выве дите на экран окно ShapeSheet. В окне ShapeSheet опробуйте любое из следующих действий и оцените воздействие на образ в чертежном окне:

Измените значения ячеек PinX и PinY в секции Shape Transform. Образ будет перемещаться на чертежной странице.

360

Формулы Microsoft Office Visio 2003

 

 

 

Измените значения ширины (Width), высоты (Height) или угла (Angle). Образ будет уменьшаться, расти или «вращаться» в соответствии с новыми значениями.

Нажмите на метке геометрического ряда, чтобы выбрать ряд. В чертежном окне на соответствующем вертексе появится черный указатель.

Укажите геометрический ряд и выберите Edit Delete Row. Соответствующий вертекс переместится по прямой линии сегмента.

Укажите геометрический ряд и выберите Insert Row или Insert Row After. Вводите свои значения в ячейки вставленного ряда. На образе появится новый вертекс с назначенными вами координатами.

Совет: В случае, если окно ShapeSheet изображает в ячейках зна чения, а не формулы (что часто случается с шаблонами, поставляемыми вместе с Microsoft Office Visio), то выберите View Formulas, чтобы выве сти на экран формулы. Вы можете вывести на экран ту секцию, которая была невидимой, или скрыть ту секцию, которая вас не интересует.

В чертежном окне вы можете поменять образ, используя чертеж ные инструменты и команды Microsoft Office Visio, а затем посмотреть, что происходит с формулами образа. Попробуйте любое из следующих действий:

Перемещайте образ Инструментом Указатель. Формулы образа PinX и PinY будут меняться, отражая новое положение образа на чертежной странице.

«Подтащите» любой указатель выбора и измените размеры образа. Формулы образа для ширины и высоты будут меняться, отражая новый размер образа.

Используйте Инструмент Карандаш, чтобы выбрать вертекс и удалить его (или добавить и переместить). Обратите внимание на то, какой эффект это вызовет в секции образа Геометрия (Geometry).

Измените формат заполнения образа или линейный формат. Соответствующие формулы в секции образа Fill Format или Line Format также изменятся.

Выберите Format Protection и укажите различные параметры в диалоговом окне. Соответствующие ячейки в секции образа Protection изменятся с 0 на 1.