Основы офисного программирования и документы Word


Организация взаимодействия в системе проектов


Теперь, когда мы умеем программно включать в системе проектов ссылку на новый программный проект, осталось рассмотреть, как обращаться к элементам этого проекта, его общедоступным переменным, объектам и процедурам. Для обращения к элементу проекта, на который есть ссылка, необходимо указать его полное имя, включающее имя проекта, имя модуля и собственное имя элемента. В моем примере я буду вызывать процедуру с одним и тем же собственным именем TestPrint, которая есть в обоих подключаемых проектах DocTwo и DocThree. Чтобы вызвать процедуру того проекта, который подключен в текущий момент, очевидно, необходимо организовать разбор случаев. Это нетрудно сделать, зная имя (имена) подключенных проектов. Здесь есть правда некоторый подводный камень. Дело в том, что в соответствующем операторе Case только имя одного элемента будет определено, того элемента, который принадлежит подключенному проекту, имена вызываемых элементов из других, неподключенных проектов будут неопределенными, более того, считаться необъявленными, что немедленно приведет к обычной синтаксической ошибке. Из этой ситуации есть простой выход. Но прежде, чем рассказать о нем, давайте взглянем на текст соответствующей процедуры, занимающейся разбором случаев:

Public Sub CallProcFromProject() 'Вызов процедуры подключаемого проекта 'Разбор случаев, какой проект подключен If NoP = "DocTwo" Then Proc2 ElseIf NoP = "DocThree" Then Proc3 End If End Sub

Public Sub Proc2() 'Вызов процедуры проекта 2 DocTwo.DocTwoModule.TestPrint End Sub Public Sub Proc3() 'Вызов процедуры проекта 2 DocThree.DocThreeModule.TestPrint End Sub

Листинг 4.11.

(html, txt)

В нашей простой ситуации разбором занимается простейший оператор IF, который в зависимости от имени проекта вызывает процедуру TestPrint того или иного проекта. Но заметьте, я не стал в текст этой процедуры вставлять явный вызов процедуры проекта, заменив его вызовом внутренней процедуры, являющейся оберткой настоящего вызова. Таким образом, процедура CallProcFromProject является синтаксически корректной, более того она вызывает синтаксически корректную процедуру, если, конечно, разбор работает корректно. Синтаксически некорректные процедуры не вызываются, их существование в проекте не мешает его нормальной работе. Это, конечно, "уловка", но позволяющая достигнуть заданной цели. Хочу обратить внимание на еще один аспект, связанный с работой этой процедуры. Вы, возможно, заметили, эта процедура вызывается в теле процедуры CreateRef, сразу же после создания ссылки на подключаемый проект. Давайте еще раз вернемся к рассмотрению метода AddFromFile, создающего ссылку в коллекции References. Конечно, главным итогом работы этого метода будет не только создание ссылки, но и явное подключение нового документа с его проектом. Это означает, что, если соответствующий документ не был открыт к моменту создания ссылки, то он автоматически откроется. Необходимо иметь в виду еще одно следствие этого процесса, после завершения процедуры, создающей ссылки в связи с подключением нового проекта, происходит сброс выполнения текущего проекта. Это означает, в частности, что все глобальные переменные будут обнулены, и, следовательно, будут потеряны значения переменных, хранящих имена подключаемых проектов. Именно поэтому вызов процедуры CallProcFromProject делается в той же процедуре, в которой новый проект подключается. В противном случае необходимо было повторно запрашивать у пользователя имена подключаемых проектов.

Прежде, чем поставить точку на описании решения рассматриваемой задачи, хочу обсудить еще один важный ее аспект.

Будет ли задача успешно решаться, если подключаемые проекты закрыты для просмотра и защищены паролем?

Дело в том, что программно снять защиту с проектов нельзя, поскольку свойство Protection является свойством "только для чтения" и позволяет лишь узнать, закрыт проект или нет. К счастью, программный вызов элементов проекта возможен и для закрытых проектов. Таким образом, с закрытыми проектами можно работать точно также как и с открытыми.

В заключение хочу высказать несколько общих замечаний. Прежде всего, следует отметить, что программное подключение и отключение ссылок может быть полезным не только при работе с подключаемыми программными проектами. Список возможных Com-объектов, подключаемых к документу через меню References велик, и зачастую возникает необходимость в программном их подключении.

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

Следует также сказать, что реальная проблема С. Шершнева оказалась сложнее описанной и не укладывается ни в один из двух рассматриваемых вариантов. В его ситуации нельзя было заранее написать процедуру CallProcFromProject, поскольку разбираемые в ней случаи появлялись динамически. Надеюсь, понятно, что выходом в такой ситуации является корректировка кода этой процедуры программно, "на лету" в тот момент, когда появляется новый "случай", новый клиент в системе, создаваемой С.Шершневым. Но, как было показано, сделать это возможно и не так уж и сложно.


Теперь, когда мы умеем программно включать в системе проектов ссылку на новый программный проект, осталось рассмотреть, как обращаться к элементам этого проекта, его общедоступным переменным, объектам и процедурам. Для обращения к элементу проекта, на который есть ссылка, необходимо указать его полное имя, включающее имя проекта, имя модуля и собственное имя элемента. В моем примере я буду вызывать процедуру с одним и тем же собственным именем TestPrint, которая есть в обоих подключаемых проектах DocTwo и DocThree. Чтобы вызвать процедуру того проекта, который подключен в текущий момент, очевидно, необходимо организовать разбор случаев. Это нетрудно сделать, зная имя (имена) подключенных проектов. Здесь есть правда некоторый подводный камень. Дело в том, что в соответствующем операторе Case только имя одного элемента будет определено, того элемента, который принадлежит подключенному проекту, имена вызываемых элементов из других, неподключенных проектов будут неопределенными, более того, считаться необъявленными, что немедленно приведет к обычной синтаксической ошибке. Из этой ситуации есть простой выход. Но прежде, чем рассказать о нем, давайте взглянем на текст соответствующей процедуры, занимающейся разбором случаев:

Public Sub CallProcFromProject() 'Вызов процедуры подключаемого проекта 'Разбор случаев, какой проект подключен If NoP = "DocTwo" Then Proc2 ElseIf NoP = "DocThree" Then Proc3 End If End Sub

Public Sub Proc2() 'Вызов процедуры проекта 2 DocTwo.DocTwoModule.TestPrint End Sub Public Sub Proc3() 'Вызов процедуры проекта 2 DocThree.DocThreeModule.TestPrint End Sub

Листинг 4.11.

В нашей простой ситуации разбором занимается простейший оператор IF, который в зависимости от имени проекта вызывает процедуру TestPrint того или иного проекта. Но заметьте, я не стал в текст этой процедуры вставлять явный вызов процедуры проекта, заменив его вызовом внутренней процедуры, являющейся оберткой настоящего вызова. Таким образом, процедура CallProcFromProject является синтаксически корректной, более того она вызывает синтаксически корректную процедуру, если, конечно, разбор работает корректно. Синтаксически некорректные процедуры не вызываются, их существование в проекте не мешает его нормальной работе. Это, конечно, "уловка", но позволяющая достигнуть заданной цели. Хочу обратить внимание на еще один аспект, связанный с работой этой процедуры. Вы, возможно, заметили, эта процедура вызывается в теле процедуры CreateRef, сразу же после создания ссылки на подключаемый проект. Давайте еще раз вернемся к рассмотрению метода AddFromFile, создающего ссылку в коллекции References. Конечно, главным итогом работы этого метода будет не только создание ссылки, но и явное подключение нового документа с его проектом. Это означает, что, если соответствующий документ не был открыт к моменту создания ссылки, то он автоматически откроется. Необходимо иметь в виду еще одно следствие этого процесса, после завершения процедуры, создающей ссылки в связи с подключением нового проекта, происходит сброс выполнения текущего проекта. Это означает, в частности, что все глобальные переменные будут обнулены, и, следовательно, будут потеряны значения переменных, хранящих имена подключаемых проектов. Именно поэтому вызов процедуры CallProcFromProject делается в той же процедуре, в которой новый проект подключается. В противном случае необходимо было повторно запрашивать у пользователя имена подключаемых проектов.

Прежде, чем поставить точку на описании решения рассматриваемой задачи, хочу обсудить еще один важный ее аспект.

Будет ли задача успешно решаться, если подключаемые проекты закрыты для просмотра и защищены паролем?

Дело в том, что программно снять защиту с проектов нельзя, поскольку свойство Protection является свойством "только для чтения" и позволяет лишь узнать, закрыт проект или нет. К счастью, программный вызов элементов проекта возможен и для закрытых проектов. Таким образом, с закрытыми проектами можно работать точно также как и с открытыми.

В заключение хочу высказать несколько общих замечаний. Прежде всего, следует отметить, что программное подключение и отключение ссылок может быть полезным не только при работе с подключаемыми программными проектами. Список возможных Com-объектов, подключаемых к документу через меню References велик, и зачастую возникает необходимость в программном их подключении.

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

Следует также сказать, что реальная проблема С. Шершнева оказалась сложнее описанной и не укладывается ни в один из двух рассматриваемых вариантов. В его ситуации нельзя было заранее написать процедуру CallProcFromProject, поскольку разбираемые в ней случаи появлялись динамически. Надеюсь, понятно, что выходом в такой ситуации является корректировка кода этой процедуры программно, "на лету" в тот момент, когда появляется новый "случай", новый клиент в системе, создаваемой С.Шершневым. Но, как было показано, сделать это возможно и не так уж и сложно.

Содержание раздела