Предисловие MS Windows и новый метод разработки программ Динамический
обмен данными OLE-технология Заключение Список литературы Приложение 1.Пример
использования OLE-технологии Предисловие Наиболее распространенным языком программирования
последне-го десятилетия безусловно является С. Этому способствовали
такиеего особенности, как лаконичность, мощность, гибкость, мо-бильность. Вместе
с тем, стремительное усложнение приложений, дляреализации которых применяются
традиционные процедурно-ориентиро-ваннные языки программирования и, в частности
С, заставляют гово-рить об определенном кризисе в их использовании, связанном
преж-де всего с недостаточной надежностью и выразительной способностью. Подобных
недостатков во многом лишены языки объектно-ориен-тированнго программирования
(ООП), в сонове которыхлежит идея мо-делирования объектов посредством иерархически
связанных классов.Отдельно взятый класс рассматривается как совакупность
множестваданных и операций над ними, причем доступ к элементам данныхкласса возможен
только посредством операций этого класса. Уста-новление четкой взаимозависимости
между данными и операциями ве-дет к большой целостности данных и значительно
повышает надеж-ность программ по сравнению с традиционными языками программиро-вания.
Кроме того, идея программирования с помощью классов вомногом использует
тот же подход, который позволяет людям формиро-вать модели объектов реального
мира. Впервые идеи ООП были реализованы в середине 60-х годов вязыке программирования
Симула-67. Последний, однако, не нашел вто время широкого распространения
как в силу своей относительноменьшей производительности по сравнению с
традиционными языкамитипа FORTRAN, ALGOL, PL/1 так и, возможно, неадекватности
предла-гаемых средств решаемым в то время задачи. Еще одним важным огра-ничением
для распространеия Симулы-67 стали трудности, с которы-ми пришлось столкнуться
большинству программистов при его изуче-нии. Дело в том, что наряду с целым рядом
безусловных достоинств,идеи ООП обладают и одним существенным недостатком
- они далеконе просты для понимания и особенно для освоения с целью практи-ческого
использования. С++ - развитие С. С++ - это объектно-ориентированыый язык, то
есть язык, поз-воляющий программисту оперировать объектами некоторых типов,предварительно
им определенным. Название языка "С++" отражаетэволюционный характер
изменения языка С (запись "++", в языке С,означает, что к какой-то переменной
прибавляется единица). Онимеет еще более мощные и гибкие средства для написания
эффектив-ных программ, чем С, от которого он произошел. Человек, програм-мирующий
на традиционных языках, может просто потерять голову оттех возможностей, которые
предоставляет С++. Но не менее важным является то, что такой распространенный
иуниверсальный язык, как С, сохранен в качестве основы. С прост,эффективен,
переносим. Чего только нет в языке С: строковых дан-ныхнет, матриц нет, средств
параллельного программирования тоженет. Нет даже ввода-вывода. Типы, операции
и операторы С очень близки к тому, с чем мыимеем дело в Ассемблере,- числа,
адреса, арифметические и логи-ческие действия, циклы... Кроме того, многие особенности
С нед-вусмысленно намекаю компилятору, как сократить код и время испол-нения
программы. Эти характерные черты языка С позволяют напи-сать эффективно работающий
и не слишком сложный компилятор. И хо-тя в машинных кодах на разных компьютерах
элементарные операцииобозначаютс по-разному, вряд ли разработчику компилятора
придет вголову интерпретировать простейшие выражения каким-нибудь ориги-нальным
способом. Именно поэтому язык С "идет везде и на всем",программы, написанные
на нем, работают эффективно, и их можно пе-реносить с одного компьютера
на другой. Большинство языков программирования созданы для решения оп-ределенного
круга задач. В них не только не хватает определенныхтипов данных и функций,
но и много лишнего с точки зрения челове-ка, далекого от области, на которую
ориентирован язык. Специали-зированные типы данных или операторы, требующие нетривиальнойподдержки,
затрудняют изучение языка и мешают вашей работе, есливы ими
не собираетесь пользоваться. Поэтому С, в котором нет ни-чего лишнего, популярен
среди широкого круга программистов. Соот-ветствующие библиотеки могут добавить
к средствам языка специали-зированные функции для работы со строками, файлами,
списками, ус-тройствами ввода-вывода, математическими объектами и т.д. Остает-ся
только выбрать то, что нужно лично вам. Заголовочные файлы об-легчают использование
библиотек, предоставляют полезные типы дан-ных, глобальные переменные,
макроопределения... Они во многом ус-траняют противоречие между эффективностью
программы и удобствомиспользования библиотечных функций. Они также позволяют
не повто-рятся и не писать по нескольку раз одно и тоже в различных прог-раммах.
Поскольку С был создан специально для системного програм-мирования, он имеет
возможности низкого уровня, позволяющие "иг-рать без правил". В зависимости от
устройства и операционной сис-темы вашей машины вы можете "влезть" в видеопамять
или использо-вать системные программы, находящиеся в оперативной памяти. В любом
случае вы можете рассматривать код собственной прог-раммы как данные, а массив
данных как код функции, квадратнуюматрицу как вектор, а текст как бинарное
дерево. Что бы ни нахо-дилось в памяти - это всего лишь последовательная цепочка
чисел.Если вы не боитесь риска - можете делать все, что вам вздумается. Современные
прграммисты выбирают С не только из-за его преи-муществ. В настоящее время
мы имеем дело с цепной реакцией: чембольше написано на С, тем больше на нем
напишут еще. Это одна изпричин, почему язык С++ сохраняет С в качестве подмножества.
По мнению автора С++, Бьерна Страуструпа, различие междуидеологией С и С++
заключается примерно в следующем: программ наС отражает "способ мышления" процессора,
а С++ - способ мышленияпрограммиста. Отвечая требованиям современного
программирования,С++ делает акцент на разработке новых типов данных, наиболее
пол-но соответствующих концепциям выбранной области знаний и задачамприложения.
На С пишут библиотеки функций, С++ позволяет созда-вать библиотеки классов. Класс
является ключевым понятием С++.Описание класса содержит описание данных, требующихся
для пред-ставления объектов этого типа, и набор операций для работы с
по-добными объектами. В отличие от традиционных структур С или Паскаля, членамикласса
являются не только данные, но и функции. Функции-членыкласса имеют привилегированный
доступ к данным внутри объектовэтого класса и обеспечивают интерфейс
между этими объектами и ос-тальной программой. При дальнейшей работе совершенно
не обяза-тельно помнить о внутренней структуре класса и мехагизме работы"встроенных
функций". В этом смысле класс подобен электрическомуприбору - мало кто
знает о его устройстве, но все знают, как импользоваться. Часто в целях повышения
эффективности и упрощения структурыпрограммы приходится заставлять ее работать
с разнородными объек-тами так, как если бы они имели один и тотже тип. Например,
ок-ружность и квадрат естественно рассматривать как варианты геомет-рической
фигуры. Полезно составлять списки фигур, нарисованных наэкране, или функций,
которые их размножают, двигают и т.д. О точ-ном типе объекта приходится порой
забывать. Список геометричес-ких фигур "не знает", что в нем находится - отрезки
или звездоч-ки. Не знает этого и компилятор. Но все время, пока вы рисуетеэти
объекты, неизбежно приходится "помнить", что они из себяпредставляют. Конечно,
возможности низкого уровня позволяют "за-бывать" и "вспоминать" когда и как
нам заблагорассудится, но приэтом компилятор теряет контроль над осмысленностью
действий. Использование производных классов и виртуальных функций поз-воляет избежать
рискованной техники и не заботится о том, в ка-кой форме объект типа "геометрическая
фигура" хранит информацию отом, круг он или квадрат. (Кроме возможностей
ООП, создание ти-пов данных "треугольник" или "квадрат" как производные
от базово-го класса "геометрическая фигура" отражает логическую связь поня-тий).
Виртуальные функции, по существу, определяют, что именноможно делать с объектом,
а не то, как это делать. Создавая класс"геометрическая фигура", мы можем
включить в него виртуальныефункции рисования, увеличения, поворота. С использованием
этихфункций можно создать еще один член класса. Затем можно разработать библиотеку
программ интерактивнойграфики, снабдив ее средствами диалого, функциями
вроде дополне-ния некоторой области экрана одинаковыми геометрическими фигура-ми
и т.д. Библиотечные функции будут вызывать функции-члены клас-са "геометрическая
фигура": рисования, движения, поворота,увели-чения. А после того, как мы
все это напишем, откомпилируем, спря-чем текст функций, которые считаем своей
интеллектуальной соб-ственностью, начинается самое интересное. Теперь мы можем
опи-сать сколько угодно новых типов фигур - многоугольников, звездо-чек, эллипсов
- производных от класса "геометрическая фигура" иобъяснить, как их рисовать,
увеличивать и поворачивать. Как дви-гать - объяснять не надо. Это уже есть в базовом
классе. Функциинашей библиотеки могут работать собъектами вновь созданных
типов,для них это варианты геометричесой фигуры. Следует отметить, чтов производных
классах могут (и, как правило, должны) появлятсяданные и функции, которых
нет в базовом классе. Однако ни одна изфункций, обрабатывающих "геометрические
фигуры", никогда не уз-нает о специфических свойствах многоугольника или эллипса,
крометого, что они по-своему рисуются, увеличиваются и поворачиваются.Производный
класс сам может быть базовым для других классов, апоздние версии С++ позволяют
сделать один класс производным отнескольких других. При написании программы
часто допускаются досадные оплошнос-ти, обнаруживающиеся только на стадии выполнения
и, увы, слишкомпоздно. Например, если переменная по смыслу - знаменатель
дроби,хотелось бы получить сообщение об ошиюке тогда, когда ей присваи-вается
ноль, а не тогда, когда на этот ноль что-нибудь делится.Или, скажем, функция
рисования точки. Невозможно удержаться отсоблазна вызвать ее хотя бы раз без проверки
выхода за границыэкрана. В то же время , если мы пишем программу рисования
линии,обязательно нужна функция, которая тупо ставит точку - и как мож-но быстрее.
Существует много ситуаций, когда функции и данныеследует разрешить использовать
только привилегированным функциям,над которыми ва "хорошо подумали". В
С++ этого можно добиться,сделав "опасные" данные и функции защищенными членами
какого-ни-будь класса. К ним имеют доступ только функции-члены этого жекласса,
а так же друзья класса. Напротив, если данные или фун-кции-члены объявлены public,
они являются общедоступными. С++ предоставляет в распоряжение программиста
сложные типыданных. Однако ни аппарат классов, ни перегрузка операций невлияют
на эффективность. То, что класс - это класс, известнотолько компилятору. Если
функции-члены классов объявлены inline,на их вызов не требуется время. Фактически
это не функции, а под-становки. Лишь виртуальные функции оставляют относительно
не-большой след в оперативной памяти. Из всего выше сказанного вытекает логичный
вывод: С++ наибо-лее удобный, универсальный и необходимый язык. Но все же
возни-кает вопрос, что же было написано на этом языке, используя прин-ципы ООП,
что можно было бы "потрогать" любому программисту илипользователю. Ответ очевиден
- это Microsoft Windows.MS Windows и новый метод разработки программ. Одним
из наиболее важных механизмов взаимодействия программявляется обмен данными.
В MS Windows существует несколько способоввзаимодействия приложений: - почтовый
ящик; - динамический обмен данными; - встраивание объектов. Специальный почтовый
ящик (clipboard) Windows позволяетпользователю переносить информацию из одного
приложения в другое,не заботясь об ее форматах и представлении. В отличие от
профессиональных операциональных операционныхсистем, где механизм обмена данными
между программами доступентолько программисту, в Windows это делается очень
просто и наг-лядно для пользователя. Механизм обмена данных между приложениями
- жизненно важноесвойство многозадачной среды. И в настоящее время производителипрограммного
обеспечения пришли уже к выводу, что для переносаданных из одного
приложения в другое почтового ящика уже недоста-точно. Появился новый, более
универсальный механизм - OLE (Object Linking and Embedding ) - Встроенная объектная
связь, который позволяет пе-реносить из одного приложения в другое разнородные
данные. Напри-мер, с помощью этого механизма данные, подготовленные в системесетевого
планирования Time Line for Windows ( Symantec ), можнопереносить в текстовый
процессор Just Write ( Symantec ), а за-тем, скажем, в генератор приложений
Object Vision (Borland).Правда, это уже нестандартное средство Microsoft
Windows, но темне менее реализация OLE стала возможной именно в Windows. Кроме
механизма почтового ящика, предназначенного, в основ-ном, для пользователя, программисту
в Windows доступны спе-циальные средства обмена данными между приложениями.
Программным путем можно установить прямую связь между зада-чами, например,
принимая данные из последовательного порта, авто-матически помещать их, скажем,
в ячейки электронной таблицыExcel, средствами которой можно тут же отображать
сложные зависи-мости в виде графиков или осуществлять их обработку в реальномрежиме
времени (этот механизм носит название динамического обме-на данными - Dynamic
Data Exchange, DDE ). Основные термины Клиентское приложение DDE - приложение,
которому необходи-мо установить диалог с сервером и получить данные от
сервера впроцессе диалога. DDE-диалог - взаимосвязь между клиентским и серверным
при-ложениями. Сервер-приложение - DDE приложение, которое передает дан-ные клиенту
в процессе диалога. DDE-Транзакция -обмен сообщениями или данными между
клиен-том и сервером. Item имя - строка, идентифицирующая некоторое множестводанных,
которое сервер в состоянии передать клиенту в процесседиалога. Service имя
- строка, генерируемая сервером и используе-мая клиентом для установления диалога.
Строковый указатель - двойное слово, генерируемое опера-ционной системой,
идентифицирующее строку, передающуюся в процес-се динамического обмена данными.
Topic имя - строка, которая идентифицирует тип данных,необходимых клиентскому
приложению при динамическом обмене данных. Фильтр транзакции - флаг, который препятствует
передаченежелательных типов транзакций в функцию обратного вызова.
В Microsoft Windows динамический обмен данных является фор-мой связи, которая использует
общие области памяти для обменаданными между приложениями. Приложение
может использовать DDE внекоторый момент времени для передачи и получения новых
данных отсервера. Механизм DDE схож с механизмом почтового ящика, который яв-ляется
частью операционной системы WINDOWS. Существует лишь нез-начительная разница
в том, что почтовый ящик, в большинстве слу-чае, используется как буфер временного
хранения информации. DDEможет быть инициализирован пользователем и в большинстве
случаевпродолжать работать без его вмешательства. Библиотека DDEML обеспечивает
пользователя набором средств,которые упрощают использование механизма
DDE в WINDOWS приложе-ниях. Вместо того, чтобы обрабатывать, получать и передавать
DDEсообщения напрямую, приложения используют функции DDEML библиоте-ки. Библиотека
DDEML также обеспечивает работу со строками и раз-деляемыми данными,
генерируемыми DDE приложениями. Вместо того,чтобы использовать указатели на общие
области памяти, DDE прило-жения создают и обмениваются строковыми указателями,
которыеидентифицируют строки и данные. Уже существующие приложения, использующие
протокол DDE, ос-нованный на сообщениях полностью совместимы с теми, которые
ис-пользуют библиотеку DDEML. Вот почему приложение, использующееDDE-протокол
могут установить диалог и выполнять транзакции сприложениями, использующими библиотеку
DDEML. Взаимосвязь между клиентом и сервером. DDE возникает всегда между
клиентским приложением и сервер-ным. Клиентское приложение инициализирует обмен
данными путем ус-тановления диалога с сервером и передачи транзакции. Транзакциянеобходима
для данных и обслуживания. Сервер отвечает на транзак-цию и обеспечивает
клиента данными. Сервер может иметь сразу нес-колько клиентов в одно
и тоже время, в свою очередь, клиент мо-жет получать данные сразу от нескольких
серверов. Некоторое при-ложение одновременно может быть и клиентом и сервером.
В добавокк вышесказанному, клиент и сервер могут оборвать диалог в любоеудобное
для них время. DDE сервер использует три зарезервированных типа имен, рас-положенных
иерархично: service, topic item - уникально идентифи-цируют некоторое
множество данных, которое сервер может передатьклиенту в процессе диалога. Service
имя - это строка, которую генерирует сервер в тепромежутки времени, в которые
клиент может установить диалог ссервером. Topic имя - это строка, которая идентифицирует
логичес-кий контекст данных. Для сервера, который манипулирует файлами,topic
имена это просто названия файлов; для других серверов - этоспецифические
имена конкретного приложения. Клиент обязательнодолжен указывать topic имя
вместе с service именем, когда он хо-чет установить диалог с сервером. Item имя
- это строка, которая идентифицирует некото-рое множество данных, которое сервер
может передать клиенту впроцессе транзакции. Например, item имя может идентифицироватьЦЕЛОЕ
( int, integer ), СТРОКУ ( string, char * ), несколько па-раграфов
текста, или BITMAP образ. Все вышеуказанные имена позволяют клиенту установить
диа-лог с сервером и получить от него данные. Системный режим Системный режим
работы обеспечивает клиента всей необходи-мой информцией о сервере. Для того,
чтобы определить, какие серверы доступны в дан-ный момент времени, а также какой
информацией они могут обеспе-чить клиента, последний, находясь в начальном
режиме работы, дол-жен установить имя устройства, равное NULL. Такой шаблон диалогамаксимально
увеличивает эффективность работы, а также работу ссервером в системном
режиме. Сервер, в свою очередь, должен под-держивать нижеописанные item
имена, а также другие, часто ис-пользуемые клиентом: SZDDESYS ITEM TOPICS - список
item имен, с которыми можетработать сервер в данный момент времени. Этот
список может изме-няться время от времени. SZDDESYS ITEM SYSITEMS - список item
имен, с которыми мо-жет работать сервер в системном режиме. SZDDDESYS ITEM STATUS
- запросить текущий статус сервера.Обычно, данный запрос поддерживается только
в формате CF_TEXT исодержит строку типа Готов/Занят. SZDDE ITEM ITEMLIST -
список item имен, поддерживаемых сер-вером в несистемном режиме работы. Этот список
может менятьсявремя от времени. SZDDESYS ITEM FORMATS - список строк, представляющий
собойсписок всех форматов почтового ящика, поддерживаемых сервером
вданном диалоге. Например, CF_TEXT формат представлен строкой TEXT. Основное назначение
и работа функции обратного вызова Приложение, которое использует DDEML,
должно содержать фун-кцию обратного вызова, которая обрабатывает события, полученныеприложением.
DDEML уведомляет приложение о таких событиях путемпосылки транзакций
в функцию обратного вызова данного приложения. В зависимости от флага
фильтра транзакции, сформированногопри вызове функции DdeInitialize, функция обратного
вызова полу-чает отсортированные транзакции вне зависимости от того, являет-ся
ли данное приложение клиентом, сервером или тем и другим од-новременно.
Следующий пример демонстрирует наиболее типичное ис-пользование функции обратного
вызова. HDDEDATA CALLBACK DdeCallback( uType, uFmt, hconv, hsz1,hsz2, hdata,
dwData1, dwData2 ) UINT uType; // Тип транзакции UINT uFmt; // Формат почтого
ящика HCONV hconv; // Идентификатор диалога HSZ hsz1; // Идентификатор строки
#1 HSZ hsz2; // Идентификатор строки #2 HDDEDATA hdata; // Идентификатор глобального
объек- та памяти DWORD dwData1; // Данные текущей транзакции #1 DWORD dwData2;
// Данные текущей транзакции #2 { switch (uType) { case XTYP_REGISTER: case
XTYP_UNREGISTER: . . . return (HDDEDATA) NULL; case XTYP_ADVDATA: . . . return
(HDDEDATA) DDE_FACK; case XTYP_XACT_COMPLETE: . . . return (HDDEDATA) NULL;
case XTYP_DISCONNECT: . . . return (HDDEDATA) NULL; default: return (HDDEDATA)
NULL; } } Параметр uType идентифицирует тип посланной транзакции вфункцию обратного
вызова при помощи DDEML. Значения оставшихсяпараметров зависят от типов транзакции.
Типы транзакций будут об-суждены нами в разделе "Обработка Транзакций".
Диалог между приложениями Диалог между клиентом и сервером всегда устанавливается
потребованию клиента. Когда диалог установлен, оба партнера полу-чают идентификатор,
который описывает данный диалог. Партнеры используют этот идентификатор
в большинстве фун-кций DDEML для посылки транзакций и для их обработки. Клиенту
мо-жет потребоваться диалог как с одним сервером, так и с нескольки-ми.
Рассмотрим подробно как приложение устанавливает диалог иполучает информацию о уже
существующих каналах связи. Простой Диалог Клиентское приложение устанавливает
простой диалог с серве-ром путем вызова функции DdeConnect и определяет идентификаторыстрок,
которые содержат всю необходимую информацию о service име-ни текущего
сервера и интересущем клиента в данный момент topicимени. DDEML отвечает
на вызов этой функции посылкой соответствую-щей транзакции XTYP_CONNECT в функцию
обратного вызова каждогодоступного в данный момент времени сервера, зарегистрированноеимя
которого совпадает с именем, переданным при помощи функцииDdeConnect
при условии, что сервер не отключал фильтр serviceимени вызовом функции DdeServiceName.
Сервер может также установить фильтр на XTYP_CONNECT тран-закцию
заданием соответствующего флага CBF_FAIL_CONNECTIONS привызове функции DdeInitialize.
В процессе обработки транзакции типа XTYP_CONNECT DDEML пе-редает полученные
от клиента service и topic имена серверу. Сер-вер должен проверить эти имена
и возвратить TRUE, если он в сос-тоянии работать с такими именами, и FALSE
в противном случае.Если ни один из существующих серверов не отвечает на CONNECT-зап-рос
клиента, функция DDeConnect возвращает ему NULL с информа-цией о том,
что в данный момент времени НЕ возможно установитьдиалог. Однако, если сервер возвратил
TRUE, то диалог был успешноустановлен и клиент получает идентификатор
диалога - двойное слово, посредством которого и ведетсяобмен данными с сервером.
Затем сервер получает транзакцию вида XTYP_CONNECT_CONFIRM(в случае, если он
НЕ описывал флаг фильтра CBF_FAIL_CONFIRMS привызове соответствующей функции).
В нижеприведенном примере производится попытка установитьдиалог с сервером, который
в состоянии работать с service именем'My Server' в системном режиме. Считаем,
что параметрыhszSysTopic и hszServName уже предварительно созданы нами ранее.
HCONV hConv; HWND hwndParent; HSZ hszServName; HSZ hszSysTopic; . . . hConv
= DdeConnect( idInst, // Копия приложения hszServName, // Идентификатор service-имени
handle hszSysTopic,// Идентификатор system-topic-имени (PCONVCONTEXT) NULL);
// Используем контекст по умолчанию if( hConv == NULL ) { MessageBox( hwndParent,
"MyServer НЕ доступен!", (LPSTR) NULL, MB_OK ); return FALSE; } . . .
В этом примере функция DdeConnect заставляет DDEML посы-лать транзакцию вида XTYP_CONNECT
в функцию обратного вызова сер-вера MyServer. А теперь приведем пример
функции обратного вызова сервера,который обрабатывает транзакцию XTYP_CONNECT
и сравнивает своезарегистрированное имя с именем, полученным от клиента. Как
ужебыло отмечено ранее, если они совпадают, то сервер в состоянииустановить диалог
с клиентом. #define CTOPICS 5 HSZ hsz1; // Идентификатор строки, полученный
от DDEML. HSZ ahszTopics[CTOPICS]; // Массив поддреживаемых topic имен int i;
// Счетчик цикла . . // Для обработки транзакций используем стандартную ANSI C
. // конструкцию switch --> case --> default. . case XTYP_CONNECT: for (i = 0; i
AM , 1 --> PM } TIME; HDDEDATA EXPENTRY DdeCallback (uType, uFmt, hconv, hsz1,
hsz2, hdata, dwData1, dwData2) UINT uType; UINT uFmt; HCONV hconv; HSZ hsz1;
HSZ hsz2; HDDEDATA hdata; DWORD dwData1; DWORD dwData2; { CHAR szBuf[32]; switch
(uType) { case XTYP_ADVREQ: case XTYP_REQUEST: if ((hsz1 == hszTime &hsz2 == hszNow)
&(uFmt == CF_TEXT)) { // Копируем строку в буфер. itoa(tmTime.hour, szBuf,
10); lstrcat(szBuf, ":"); if (tmTime.minute DDE-->OLE является продолжением
воплощенияидеи "сам изобрел - сам внедряй". Естесственно, наибольшие на-дежды сейчас
возлагаются на OLE (ее новый стандарт OLE.2), так какэтот стандарт позволяет
включать в себя очень мощные средства, такиекак Multimedia. В одном файле может
находится не только текст,рисунок, а и даже целый фильм, полностью озвученный
и готовыйк показу.СПИСОК ЛИТЕРАТУРЫ 1. Гладков С.А. Фролов Г.В. Программирование
в Microsoft Windows: В 2-х частях. М.:"ДИАЛОГ-МИФИ", 1992. 2. Фойц С. Windows
3.1 для пользователя. Пер. с немецкого Киев:BHV, 1992. 3. Microsoft Windows
Software Development Kit. Version 3. Programmer's Reference, Programming Tools,
Windows Extensions. 4. Charles Petzold. Programming Windows. Microsoft Press.
5. Библия Windows 3.X. М.: И.В.К. - Софт, 1992. 6. Borland C++. Usres manual.Приложение
1. Пример использования OLE технологии// ObjectWindows - (C) Copyright
1992 by Borland International//// oleclnt.cpp// Пример Ole Client программы,
испльзующей OWL. Она показывает// пример использования Ole functions, и C++ классов
.// Основное окно позволяет пользователю создать paint brush// object, или
копировать его из clipboard.#include #include #include #include #include #include
#include #include #pragma hdrstop#include "oleclnte.h"#include "oleclntr.h"#include
"oleclnt.h"// статические данные классаLPOLECLIENTVTBL TOwlClient::lpClientVtbl
= NULL;int TOleDocWindow::nNextObjectNum = 0;void TOleApp::InitInstance(){
TApplication::InitInstance(); vcfLink = RegisterClipboardFormat( "ObjectLink"
); vcfNative = RegisterClipboardFormat( "Native" ); vcfOwnerLink = RegisterClipboardFormat(
"OwnerLink" ); // comments in owlole.h mention these ole clipboard
formats}// описание функций OWL Object, которые// позволяют хранить описание
Ole Objectint FAR PASCAL _export StdCallBack(LPOLECLIENT lpClient, OLE_NOTIFICATION
notification, LPOLEOBJECT lpObject ){ return (( PTOwlClient )lpClient)->TOleDocWindowThis->
CallBack( lpClient , notification, lpObject );}TOwlClient::TOwlClient(
PTOleDocWindow owner , HINSTANCE hInst ){ TOleDocWindowThis = owner;
if ( !lpClientVtbl ) { lpClientVtbl = new OLECLIENTVTBL; if ( hInst == 0 )
{ lpClientVtbl->CallBack = StdCallBack; } else { lpClientVtbl->CallBack = (TCallBack)MakeProcInstance(
(FARPROC)StdCallBack, hInst ); } } lpvtbl = lpClientVtbl;}void
TOleDocWindow::WMURedraw( RTMessage ){ bObjectLoaded = TRUE; InvalidateRect(
HWindow, NULL, TRUE ); UpdateWindow( HWindow );}#pragma argsusedint TOleDocWindow::CallBack(
LPOLECLIENT lpOleClient ,OLE_NOTIFICATION oleNot,LPOLEOBJECT
lpOleObject ){ switch ( oleNot ) { case OLE_CHANGED: case OLE_SAVED: PostMessage(
HWindow , WM_U_REDRAW, 0, 0L ); break; case OLE_CLOSED: break; case OLE_QUERY_PAINT:
break; case OLE_RELEASE: break; case OLE_RENAMED: break; default:
break; } return TRUE;}void TOleDocWindow::CMAbout( RTMessage ){ MessageBox( HWindow
, "OLE Client ProgramnWritten using ObjectWindowsnCopyright (c) 1992 Borland",
GetApplication()->Name, MB_OK );}// создание новой paint brushvoid TOleDocWindow::CMPBrush(
RTMessage ){ BackupObject(); bObjectLoaded = FALSE; lstrcpy(
lpszObjectName, GetNextObjectName() ); ret = OleCreate( "StdFileEditing", (LPOLECLIENT)pOwlClient,
"PBRUSH", lhClientDoc, GetApplication()->Name, &pObject,
olerender_draw, 0 ); // Создание Ole Object - асинхронная операция. // Необходимо
ожидать его создание, иначе // могут возникнуть ошибки при обработке // сообщений
этого объекта. wait( ret , lpObject ); // OleSetHostNames устанавливает имя
в сервере OLE. ret = OleSetHostNames( lpObject, GetApplication()->Name,lpszObjectName
); wait( ret , lpObject );}void TOleDocWindow::CMUndo( RTMessage msg){
if ( lpUndoObject ) if ( lpUndoObject != lpObject ) { LPOLEOBJECT lpObjectToDelete
= lpObject; lpObject = lpUndoObject; lpUndoObject = NULL; ret = OleDelete(
lpObjectToDelete ); wait( ret , lpObjectToDelete ); bObjectLoaded = bUndoObjectLoaded;
WMURedraw( msg ); }}void TOleDocWindow::CMCut( RTMessage msg){ CMCopy(
msg ); CloseCurrentOle();}void TOleDocWindow::CMCopy( RTMessage ){ if ( OpenClipboard(
HWindow ) &EmptyClipboard() ) { ret = OleCopyToClipboard( lpObject );
check( ret ); CloseClipboard(); }}void TOleDocWindow::BackupObject(){ if ( lpObject
) { ret = OleClone( lpObject, (LPOLECLIENT)pOwlClient, lhClientDoc, GetApplication()->Name,
&pUndoObject ); wait( ret, lpObject ); lstrcpy( lpszLastObjectName,
lpszObjectName ); lstrcpy( lpszObjectName , GetNextObjectName() ); bUndoObjectLoaded
= bObjectLoaded; }}void TOleDocWindow::CMPaste( RTMessage ){ if (
OpenClipboard( HWindow ) ) { BackupObject(); lstrcpy( lpszObjectName, GetNextObjectName()
); ret = OleCreateFromClip( "StdFileEditing", (LPOLECLIENT)pOwlClient,
lhClientDoc, lpszObjectName, &pObject, olerender_draw, 0 ); check( ret ); ret
= OleSetHostNames( lpObject, GetApplication()->Name, lpszObjectName ); wait(
ret , lpObject ); bObjectLoaded = TRUE; CloseClipboard(); PostMessage( HWindow
, WM_U_REDRAW, 0, 0L ); }}LPSTR TOleDocWindow::GetNextObjectName(){ static char
buffer[ MAXPATH ]; wsprintf( buffer, "object #%03d", nNextObjectNum++ ); return
buffer;}void TOleDocWindow::Paint( HDC hdc, PAINTSTRUCT _FAR )
{
LPOLEOBJECT
lpObjectToDraw = NULL; if ( bObjectLoaded ) lpObjectToDraw = lpObject; else if
( lpUndoObject ) lpObjectToDraw = lpUndoObject; if ( lpObjectToDraw ) { RECT rect;
GetClientRect( HWindow, rect);// Замечание по OleDraw:// OleDraw должен возвращать
OLE_ERROR_OBJECT, если// object был нарисован неверно. ret = OleDraw( lpObjectToDraw
, hdc, rect, NULL, 0 ); wait( ret, lpObjectToDraw ); }}TOleDocWindow::TOleDocWindow(
PTWindowsObject parent, LPSTR title ) : TWindow( parent, title
){ ret = OLE_OK; lhClientDoc = 0; bObjectLoaded = FALSE; bUndoObjectLoaded
= FALSE; bUndoObjectLoaded = FALSE; pOwlClient = NULL; lpObject = NULL; lpUndoObject
= NULL; strcpy( lpszDocName , "noname.ole" ); *lpszLastObjectName = 0; *lpszObjectName
= 0; bDefDocName = TRUE;}void TOleDocWindow::SetupWindow() { TWindow::SetupWindow();
RegisterClientDoc(); pOwlClient = new TOwlClient( this );}void
TOleDocWindow::RegisterClientDoc() { ret = OleRegisterClientDoc( GetApplication()->Name,
lpszDocName, 0, &hClientDoc ); check( ret );}void TOleDocWindow::ShutDownWindow(){
CloseCurrentOle(); if ( pOwlClient ) delete pOwlClient; TWindow::ShutDownWindow();}void
TOleDocWindow::RegFileName( LPSTR FileName ){ lstrcpy(
lpszDocName , FileName ); ret = OleRegisterClientDoc( GetApplication()->Name,
lpszDocName , 0, &hClientDoc ); check ( ret );}void TOleDocWindow::CMActivate(
RTMessage ){ BackupObject(); RECT rect; GetClientRect( HWindow, rect); ret =
OleActivate( lpObject , OLEVERB_PRIMARY, TRUE, TRUE , HWindow , rect); wait ( ret,
lpObject ); PostMessage( HWindow , WM_U_REDRAW, 0, 0L );}void TOleDocWindow::WMInitMenu(
RTMessage msg ){ HMENU hMenu = (HMENU)msg.WParam; WORD wEnableUndo;
if ( (lpObject != lpUndoObject) &
( lpUndoObject != NULL )) wEnableUndo = MF_ENABLED;
else wEnableUndo = MF_GRAYED; EnableMenuItem( hMenu, CM_UNDO , wEnableUndo
); EnableMenuItem( hMenu, CM_COPY , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED
)); EnableMenuItem( hMenu, CM_CUT , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED
)); ret = OleQueryCreateFromClip( "StdFileEditing", olerender_draw, 0 ); EnableMenuItem(
hMenu, CM_PASTE , (( ret == OLE_OK ) ? MF_ENABLED : MF_GRAYED ));
EnableMenuItem( hMenu, CM_ACTIVATE , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));
EnableMenuItem( hMenu, CM_CLEAR , ( bObjectLoaded ? MF_ENABLED : MF_GRAYED ));
DrawMenuBar( HWindow );}LPSTR TOleDocWindow::GetClassName() { return "OLEDOCWINDOW";
}void TOleDocWindow::GetWindowClass(WNDCLASS _FAR wc){ TWindow::GetWindowClass(
wc ); wc.lpszMenuName = "MENU_DOCWINDOW";}void TOleDocWindow::CMClear(
RTMessage ){ CloseCurrentOle();}void TOleDocWindow::CloseCurrentOle(){ // окончательное
сохранение if ( lpObject ) { ret = OleDelete( lpObject ); wait( ret ,
lpObject ); } if ( lpUndoObject ) { ret = OleDelete( lpUndoObject ); wait( ret
, lpObject ); } lpObject = lpUndoObject = NULL; bObjectLoaded = FALSE; InvalidateRect(
HWindow , NULL, TRUE ); UpdateWindow( HWindow );}void TOleApp::InitMainWindow(){
MainWindow = new TOleDocWindow(NULL, "OWL OLE Application" );}int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmd, int nCmdShow){
TOleApp OleApp ("OleApp", hInstance, hPrevInstance, lpCmd, nCmdShow);
OleApp.Run(); return (OleApp.Status);}