Реферат по предмету "Информатика, программирование"


Обмен данными в Windows

Буфер обмена (CLIPBOARD)
В Windows предусмотрен специальный механизм обмена даннымимежду разными приложениями, называемый буфер обмена (clipboard). Буфер обмена представляет собойбуфер, в который могут быть помещены данные каким-либо приложением. Всеостальные приложения Windows могут прочитать эти данные или разместить в этомбуфере свои.
Для того, что бы не возникало путаницы при использованиибуфера обмена, Windows предполагает применение определенных форматов данных,размещаемых в буфере.
В некоторых случаях бывает удобно просмотреть данные,размещенные в буфере обмена – для этих целей Windows содержит специальнуюпрограмму просмотра содержимого буфера обмена, (Clipboard Viewer). Не надосмешивать между собой сам буфер обмена и программу его просмотра. Буфер обменареализован несколькими функциями Windows и специальными данными.
При рассмотрении буфера обмена нам надо будет рассмотреть тривопроса:
1) как можно самимкласть или читать данные из буфера обмена
2) как можноиспользовать буфер обмена со стандартным окном–редактором
3) как написатьсобственную программу просмотра содержимого буфера обмена.
Предварительно мы разберемся с некоторыми основнымипонятиями, связанными с применением буфера обмена.
Как мы уже сказали, Windows предполагает использованиеопределенных форматов данных для передачи через буфер обмена. Конечно, у Васесть возможность передавать данные в собственном формате, только дляиспользования собственным приложением, однако рекомендуется придерживатьсяобщепринятых стандартов, так как возможность передачи данных между самымиразнообразными приложениями является очень удобной.
Каждому применяемому формату данных буфера обмена в Windowsпоставлен в соответствие определенный номер. Windows определяет несколькостандартных форматов и предоставляет для них определенные символические имена:
CF_TEXT                  соответствует ASCIIZтексту
CF_BITMAP             обычный битмап
CF_DIB                     битмап,независящий от устройства
CF_PALETTE                палитра (обычноприменяется вместе с CF_DIB)
CF_METAFILEPICT          метафайл
При отображении данных этих форматов в программе просмотрабуфера обмена не возникает никаких проблем, так как Windows содержит всенеобходимые средства для отображения этих данных. Однако Вы можете класть вбуфер обмена данные в собственном формате. Если Вы хотите, что бы их отображаластандартная программа просмотра, то Вы должны их объявить как
CF_OWNERDISPLAY       данные, отображаемыепользователем
В этом случае программа просмотра будет посылать специальныесообщения Вашему окну для отображения этих данных в окне.
Несколько дополнительных форматов, являясь обычными форматамиданных, имеют отличные от них номера. В символических именах таких данныхприсутствует аббревиатура ‘DSP’
CF_DSPTEXT                соответствует ASCIIZтексту
CF_DSPBITMAP           обычный битмап
CF_DSPMETAFILEPICT  метафайл
данные этих форматов отображаются в программе просмотра какданные соответствующих форматов, но обычно не используются другимиприложениями, кроме Вашего.
Помимо рассмотренных, Windows дополнительно определяетбольшое количество других стандартных форматов данных, однако они используютсясравнительно редко. В большинстве случаев это специфические форматы данныхразных популярных программ, которые было решено включить в стандарт Windows.
При необходимости использования собственного формата данныхдля буфера обмена надо получить номер этого формата. Для того, что бы избежатьвозможных накладок, Вы должны зарегистрировать Ваш формат данных с помощьюфункции
UINT   RegisterClipboardFormat(lpszFormatName );
для уже зарегистрированного формата Вы можете узнать его имя:
int         GetClipboardFormatName(nFormat, lpsBuffer, nMaxCount );
Буфер обмена содержит не более одного блока данных каждогоформата, причем все данные, находящиеся в буфере должны быть положены однимокном. Так как данные, передаваемые в буфер обмена, должны быть доступны всемприложениям, то для их передачи используются только блоки глобальной памяти.Записьи чтение данных из буфера обмена
Общие правила работы с буфером обмена сводятся к следующему:
1) Вся работа с буфером обмена должна проводиться за времяобработки одного сообщения. Во время работы с буфером Вы не должны вызыватьникаких функций, которые могут передать управление другому приложению. То естьВы не должны использовать функций типа: DialogBox, MessageBox, GetMessage,PeekMessage.
Данные должны размещаться только в перемещаемом блокеглобальной памяти.
Когда буфер обмена получает данные, он объявляет себя владельцемэтих данных, так что приложение больше не должно использовать переданныеданные. Более того, эти данные нельзя удалять при завершении работы Вашегоприложения — когда будет надо, буфер обмена сам удалит их.
Когда Вы читаете данные из буфера обмена, то Вы получаетехендл блока данных. Так как эти данные закреплены за буфером, то Вы не должны сними работать, Вам необходимо скопировать их к себе.
При обмене данными с буфером обмена нельзя передавать емузапертые блоки данных, равно как нельзя оставлять их запертыми после чтения.
2) Перед началом обмена данными с буфером обмена Вы должныего открыть. Делается это с помощью функции
BOOL  OpenClipboard(hWnd );
если Вы положите какие-либо данные в буфер, то окно,указанное Вами, будет считаться владельцем всех данных буфера обмена.
3) Затем Вы можете осуществить необходимые операции обменаданными. Если Вы собираетесь положить данные в буфер обмена, то Вы должныпредварительно удалить все уже находящиеся в нем данные:
BOOL  EmptyClipboard(void );
и только затем положить нужные данные, воспользовавшисьфункцией:
HGLOBAL     SetClipboardData(nFormat, hGlobal );
параметр nFormatзадает имя формата данных, а hGlobalявляется хендлом глобального блока данных. Функция возвращает Вам новый хендлэтого блока данных, с помощью которого Вы можете обращаться к этим данным дозакрытия буфера обмена.
Вы можете положить в буфер обмена несколько блоков данныхразного формата одновременно. Так как положенные в буфер данные сохраняются тамлибо до его очистки, либо до завершения работы Windows, то передавать большиеблоки может быть слишком накладно.
Для этого случая в Windows предусмотрен механизм передачиданных с задержкой. Вызывая функцию SetClipboardData Вы указываете вместохендла блока данных NULL.Это означает, что данные для буфер обмена у Вас есть, но передавать Вы ихбудете только по требованию. Для такой передачи Вам надо будет обрабатывать трисообщения:
WM_RENDERFORMAT                       nFormat                            0L
сообщение требует данные для буфераобмена. При этом буфер уже открыт другим приложением, поэтому Вам открывать илизакрывать его не надо. Вам надо просто вызвать функцию
SetClipboardData(nFormat, hGlobal );
передав ей хендл реального блокаданных.
WM_RENDERALLFORMATS, 0,0L
сообщение посылается Вашему окнукогда оно уничтожается, а буфер обмена содержит задержанные данные Вашего окна.Вы должны обычным образом (то есть открыть–очистить–положить–закрыть) передатьвсе данные в буфер обмена.
WM_DESTROYCLIPBOARD, 0,0L
сообщение информирует Вас о том, чтовызвана функция EmptyClipboard,когда буфер обмена содержит задержанные данные Вашего окна. Ваши данные большене понадобятся, поэтому Вы можете освободить используемые структуры данных.
Если Вы собираетесь только читать данные из буфера обмена, тоочищать его не надо, а получить данные нужного формата можно с помощью функции
HGLOBAL     GetClipboardData(nFormat );
Функция возвращает хендл глобального блока памяти, который Выдолжны скопировать к себе.
4) После завершения обмена с буфером обмена Вы должны закрытьего с помощью функции
BOOL  CloseClipboard(void );
На этом заканчиваются операции обмена данными с буферомобмена.
Кроме рассмотренных, Вы можете применять еще несколькофункций, облегчающих работу с буфером обмена:
BOOL  IsClipboardFormatAvailable(nFormat );
Эта функция сообщает, присутствуют–ли данные нужного форматав буфере обмена. Проверку на наличие тех или иных форматов данных можновыполнить и иным способом, с помощью функции
UINT   EnumClipboardFormats(nFormat );
Эта функция перебирает присутствующие форматы данных в буфереобмена и возвращает номер следующего в списке или 0. Примерприменения:
UINTnFormat= 0;
while( ( nFormat= EnumClipboardFormats(nFormat ) ) != 0 ) { // переборформатов     }
Можно узнать количество присутствующих в буфере обменаформатов данных с помощью функции
UINT   CountClipboardFormats(void );
5) Сейчас мы рассмотрим правила применения форматов данных CF_DSP…Эти данные предназначены для использования только Вашим приложениями.
Основная идея заключается в том, что приложения, читающие,скажем, формат CF_TEXT,его и будут запрашивать у буфера обмена. При этом, даже если буфер обменасодержит данные в формате CF_DSPTEXT,он их не передаст – номера форматов разные, поэтому считается, что Вы можетепередавать данные в формате CF_DSP…только для своего приложения, “спрятав” его от остальных.
Однако может случиться так, что одновременно два разныхприложения попробуют использовать приватные данные в таком формате. Поэтомувозникает необходимость научиться различать данные, положенные Вашимприложением, от данных, положенных чужим приложением. Для этого Вы можетевоспользоваться функцией
HWND GetClipboardOwner(void );
которая вернет хендл окна, положившего данные. Если вызатрудняетесь по хендлу определить принадлежность окна к нужному приложению, тоВы почти наверняка знаете, к какому классу оно должно принадлежать (так как иданные, и приложение, и класс окон разработаны Вами). Поэтому Вы можете узнатьеще и имя класса окна:
int         GetClassName(hWnd, lpsBuffer,nMaxCount );Буферобмена и стандартное окно–редактор
Часто приходится использовать буфер обмена совместно собычным окном редактора. Больших сложностей здесь нет, так как такое окноавтоматически поддерживает операции обмена данными с буфером в формате CF_TEXT.Единственное, что нам надо сделать – научиться передавать редактору команды дляосуществления этого обмена.
Это обычно приходится делать для того, что бы добавить меню,содержащее пункты Cut–Copy–Paste.Так как сам редактор, будучи дочерним окном, не имеет меню, то меню добавляетсяк нашему родительскому окну. То есть команды, полученные от меню, надо как-топередать в окно редактора.
При этом заодно приходится разрешать/запрещать отдельныепункты меню в зависимости от наличия выделенного текста в редакторе (Cut, Copy) и наличиянужных данных в буфере обмена (Paste).
Для передачи обычному редактору команд для обмена данными сбуфером служат сообщения
WM_PASTE          0                                0L
вставить текст из Clipboard в текущуюпозицию каретки
WM_COPY            0                                0L
скопировать выделенный текст изредактора в Clipboard
WM_CUT               0                                0L
скопировать выделенный текст иудалить его из редактора
Мы должны просто послать нужное сообщение редактору и онвыполнит все остальное. Обратите внимание на то, что эти сообщения не являютсяспецифичными для редактора – они имеют префикс WM_ – то есть этосообщения для обычного окна.
Если Ваше окно будет поддерживать работу с буфером обмена, тонастоятельно рекомендуется поддерживать эти сообщения.
Для того, что бы разрешать или запрещать нужные пункты менюможно воспользоваться функцией IsClipboardFormatAvailableдля разрешения/запрещения операции Pasteи передачей сообщения EM_GETSELдля выяснения возможности операций Cutи Copy.Просмотрданных, находящихся в буфере обмена
Если Вы вводите новые форматы данных в буфер обмена, то вамможет понадобиться новая программа просмотра данных буфера обмена, котораяпозволит просматривать Ваши данные. В Windows принято, что программа просмотрадолжна обновлять отображаемую информацию при смене данных в буфере обмена. Дляэтого программа просмотра получает сообщение
WM_DRAWCLIPBOARD                     0                                         0L
Однако одновременно может работать несколько программ (окон)просмотра. При этом возникает необходимость где-то удерживать список всех такихпрограмм (окон) и посылать им всем соответствующие сообщения. Для этогоорганизуется цепочка программпросмотра, которую они сами поддерживают в корректном состоянии.
В какой-то степени это похоже на обработку прерываний DOS — система удерживаетхендл только первого окна просмотра, тот — хендл следующего и так далее.
Когда Вы запускаете свою программу просмотра, онарегистрируется в качестве программы просмотра буфера обмена с помощью функции
HWND SetClipboardViewer(hWnd );
при этом он становится на первое место в цепочке, а функция SetClipboardViewerвозвращает хендл следующего за ним (или 0, если других программ (окон)просмотра нет). Этот хендл должен быть сохранен.
Далее, при обновлении данных в буфере обмена, Ваше окнополучает сообщение WM_DRAWCLIPBOARD.Обычная обработка этого сообщения:
caseWM_DRAWCLIPBOARD:          if ( hWndNextViewer ) PostMessage( hWndNextViewer,wMsg, wParam, lParam );         InvalidateRect( hWnd, NULL, TRUE );            return0;
В результате обработки этого сообщения окно программыпросмотра перерисовывается, и такое же сообщение получает другая программа.
Во время нормальной работы какая-либо из программ просмотразаканчивает функционирование прежде остальных. При этом цепочка программпросмотра изменяется. Для того, чтобы корректно изменить цепочку, применяетсяфункция
BOOL  ChangeClipboardChain(hWndViewer, hWndNextViewer );
Что значит: вместо окна hWndViewer будетиспользоваться окно hWndNextViewer.Эта функция обычно вызывается при обработке сообщения WM_DESTROY. Врезультате выполнения этой функции первая программа просмотра в цепочке получитсообщение (WM_CHANGECBCHAIN,hWndRemoved, hWndNext). Это сообщение обрабатывается так:
case WM_CHANGECBCHAIN:         if ( wParam == hWndNextViewer ) {             hWndNextViewer= LOWORD( lParam);          } else if ( hWndNextViewer ) {             PostMessage( hWndNextViewer,wMsg, wParam, lParam );         }          return 0;
В результате такой обработки, если наше окно стоит в цепочке до удаляемого, но непрямо перед ним, оно передаст сообщение дальше; если оно стоит прямо передудаляемым, то оно исправит свой хендл следующего окна просмотра и не пустит сообщениедальше, так как цепочка уже исправлена.
Отображение данных программой просмотра происходит также, каки их обычное чтение (открыл–прочитал–закрыл).Динамический обмен данными (DDE)
Пока что мы рассмотрели только один способ обмениватьсяданными между разными приложениями — буфер обмена. Однако этот способ не всегдаэффективен. В большинстве случаев он ограничен применением в редакторах.
Однако необходимость обмена данными между разнымиприложениями реально существует, поэтому Windows содержит еще одно средство длятакого обмена — DDE (DynamicData Exchange).
DDE основан на использовании сообщений для передачи данныхмежду двумя приложениями. При этом одно из приложений (обычно содержащееданные) называется сервером(server), а другое (обычно требующее данных) – клиентом (client). В более общем виде:клиент выступает в роли активного приложения, требующего, что бы его запросыбыли обслужены сервером. Сам процесс обмена данными средствами DDE междуклиентом и сервером называется DDE–разговором(DDE–conversation).
Обычно протокол обмена данными между клиентом и серверомвыглядит примерно следующим образом:
·   клиент передает всем доступнымприложениям сообщение о том, что ему надо.
·   если в системе находится подходящийсервер, он отвечает клиенту о возможности обслуживания.
·   клиент посылает запрос(ы) серверу длявыполнения требований.
·   в некоторых случаях сервер можетинформировать клиента об изменении или наличии тех или иных данных. Для этогоклиент должен подписаться(advise) на необходимые данные.
·   обмен между клиентом и сервером можетпродолжаться до тех пор, пока один из них не потребует прекращения.
В процессе DDE–разговора происходит обмен сообщениями междудвумя приложениями. Понятно, что адресатами таких сообщений являются не самиприложения, а их окна. В большинстве случаев для нормального веденияDDE–разговора создаются специальные скрытые (невидимые) окна, которые иосуществляют обмен сообщениями между собой. Конечно, в DDE могут участвоватьокна одного приложения, не только разных, но надо учесть, что DDE для обменаданными в пределах одного приложения является неэффективным. Так как процессыподготовки и приема данных могут занимать значительное время, то требуется,чтобы для обмена сообщениями использовалась функция PostMessage, а не SendMessage(кроме установления DDE–разговора). Это, правда, создает дополнительнуюсложность — необходимость синхронизации действий разных окон между собой.
В Windows содержится все необходимое для организации DDE,причем в двух экземплярах. Помимо старого способа, существующего с первыхверсий Windows, в Windows версии 3.1 была добавлена специальная библиотекаDDEML (DDE Management Library), являющаяся “надстройкой” над старым способом ипредназначенная для формализации протоколов обмена. Существенная разница междуэтими способами связана с тем, что для организации DDE старым способом выдолжны предусмотреть обработку специальных сообщений необходимыми Вам окнами, абиблиотека DDEML сама создает необходимые скрытые окна и организует обменсообщениями; при этом для взаимодействия с Вашим приложением DDEML будетвызывать специально разработанную Вами CALLBACK процедуру (не являющуюоконной процедурой).
Считается, что при использовании DDEML несколько упрощаетсянаписание приложений, так как она берет на себя вопросы синхронизации обмена.Однако на практике не было замечено реального упрощения работы в связи сприменением библиотеки. Более того, исходный текст может оказаться даже больше,чем при использовании старого метода.
Поэтому мы будем рассматривать только старый способорганизации DDE. Предварительно мы введем несколько терминов, используемых вDDE. Когда клиент начинает DDE или требует данные, он посылает серверуспецификацию того, что он требует.
Эта спецификация состоит из 3 пунктов:
·   имя приложения — application (в DDEML называется сервис (service)); так как в большинствеслучаев в документации применяется термин service, то его мы и будем использоватьдальше. Хотя, надо отметить, этот параметр обычно задает условное имяприложения–сервера.
·   тему DDE–разговора — topic
·   требуемые данные — item
Имя сервиса и тема DDE–разговора используютсяпри установлениисвязи. В результате этого в системе должно быть установлен обмен данными междуодной или несколькими парами окон, которые поддерживают данные сервис и тему.Одно из окон в каждой паре будет являться клиентом, а другое — сервером. Впроцессе дальнейшего DDE–разговора может происходить обмен данными, имя которыхзадается отдельно и является возможным именем данных для данного сервиса итемы.
Эти три имени представлены в виде атомов (ATOM), поэтому нам надоразобраться с атомами и правилами их применения перед продолжением разговора оDDE.Атомы
Атомами в Windows называются нумерованные строки текста,хранимые в специальной таблице. Максимальный размер строки 255 символов. Припомещении строки в таблицу ей присваивается уникальный номер, которыйсобственно и используется вместо самой строки. Часто атомом называют именно этиномера, а строки — именами атомов. Обе платформы (Windows 3.x и Win32) используютдля задания атомов 16-ти разрядные числа, называемые ATOM, что позволяетв одном двойном слове передавать до двух атомов.
Если добавлять две одинаковых строки в таблицу атомов, то ониполучат одинаковый номер. На этом основан один из способов сравнения строк —добавить обе строки в таблицу и сравнить атомы (не забудьте после этого удалитьдобавленный атом, даже если это было надо только для проверки).
Для каждого приложения доступны две таблицы атомов — локальная и глобальная. Так как DDE поддерживается между разными приложениямито, очевидно, должна применяться только глобальная таблица атомов.
Для работы с глобальными атомами предназначены следующиефункции:
ATOM GlobalAddAtom(lpszName ); UINT  GlobalGetAtomName( aAtom, lpsBuffer, nMaxCount );    ATOM     GlobalFindAtom(lpszName );      ATOM      GlobalDeleteAtom( aAtom );
С помощью этих функций можно добавить атомы, узнать имяконкретного атома, найти нужный атом в таблице и удалить атом. Причем, сколькораз добавлялся атом с одним именем, столько раз он должен быть удален, что быон действительно был удален из таблицы.
Для работы с локальными атомами используются аналогичныефункции, но не имеющие в начале слова “Global”. Помимо этого для работы слокальной таблицей атомов добавлена еще одна функция:
ATOM AddAtom(lpszName ); UINT  GetAtomName( aAtom, lpsBuffer, nMaxCount );    ATOM     FindAtom(lpszName );      ATOM     DeleteAtom( aAtom );
BOOL  InitAtomTable(UINT cTableEntries );
Которая позволяет указать максимальное число строк,помещаемых в локальную таблицу атомов. По умолчанию это число равно 37 (размерглобальной таблицы атомов изменить нельзя). При работе с локальной таблицейатомов удалять атомы в конце работы приложения необязательно — при завершенииработы всего приложения таблица будет автоматически удалена.
При обмене данными посредством DDE происходитпоследовательная посылка сообщений от одного приложения к другому и наоборот.Одна DDE транзакция обычно состоит из двух — трех последовательных сообщений. Вэтих сообщениях часто передаются необходимые имена, представленные в видеглобальных атомов. При этом возникает необходимость так организовать обменсообщениями, что бы обеспечить удаление всех вновь добавляемых атомов. Дляэтого принято такое решение: приложение, посылающее атомы вместе с сообщениедолжно эти атомы создать, а затем проверить код завершения функции PostMessage— если посылка прошла успешно, то атом должен быть освобожден принимающимприложением, а если при посылке возникла ошибка, то атом должно удалитьпосылющее приложение. В некоторых случаях атом, полученный приложением ссообщением, может быть послан вместе с ответным сообщением назад (кромесообщения WM_DDE_INITIATE,о чем см. ниже). В этом случае не обязательно атом удалять а потом создаватьснова, можно использовать полученный; однако необходимо убедиться, что он былпослан с ответным сообщением и, если посылка не состоялась, его надо удалить.Особенностизадания параметра lParam сообщений DDE
Следует учесть некоторые различия в применении параметра lParamсообщений DDE на 16–ти и 32–х разрядных платформах (Windows 3.x и Win32). Делов том, что первоначально этот параметр использовался для передачи двухзначений, часто атома имени данных и хендла глобального блока. В случае 32–хразрядной платформы хендл блока сам занимает двойное слово и разместиться в lParamсовместно с атомом имени данных не может.
Для решения этой проблемы были введены специальные функции,осуществляющие “упаковку” двух указанных значений (называемых младшим истаршим) в одно двойное слово. Фактически можно считать, что выделяетсяспециальная структура, содержащая пару полей, идентификатор которойиспользуется в качестве lParam.
Так как разные сообщения DDE используют lParam различнымобразом, то и упаковка данных осуществляется не для каждого сообщения, так чтодля каждого сообщения надо проверять необходимость использования этогомеханизма, а для сообщения WM_DDE_ACKнадо еще проверить, в ответ на какое сообщение он послан. Для работы с“упакованными” данными необходимо применять специальные функции:
LONG  PackDDElParam(UINT uMsg,UINT uLow,UINT uHigh); LONG      UnpackDDElParam(UINT uMsg,LONG lParam,PUINT puLow,PUINT puHigh);       LONG     FreeDDElParam(UINT uMsg,LONG lParam);    LONG      ReuseDDElParam(       LONGlParam, UINTuMsgIn, UINTuMsgOut, UINTuLow, UINTuHigh );
Функция PackDDElParamупаковывает указанные данные в промежуточную структуру, хендл которойвозвращается в виде LONG;возвращаемое значение может быть использовано только для передачи данных спомощью функции PostMessageи только для некоторых сообщений DDE. Функция UnpackDDElParamвыполняет обратную операцию, но промежуточная структура данных при этомсохраняется. Для того, что бы ее удалить необходимо использовать FreeDDElParam.Последняя функция ReuseDDElParamможет применяться для использования одной структуры при повторной передачеданных или ответе на принятое сообщение. В качестве параметра lParam функции PostMessageнеобходимо указывать возвращаемое, а не первоначальное значение.
В описании параметров сообщений мы сохраняем прежнийсинтаксис, причем lParam может по–прежнему задаваться в виде двух компонент старший & младший.Единственное отличие, что под старшим и младшим компонентом не надоподразумевать слова, а для получения этих компонентом может потребоватьсяфункция UnpackDDElParam,о чем в описании сообщения будет сделана специальная оговорка. В редких случаяхмогут даваться два описания параметров, для Windows 3.x и для Win32. Дляразличия этих вариантов в описании параметров сообщения могут быть добавленыспециальные пояснения
(Packed Win32)     — если параметр упаковывается в случаеплатформы Win32
(Windows 3.x) — если описание применяется только в случаеWindows 3.x
(Win32)      — если описание применяется только в случаеWin32DDE,начало обмена и его завершение
Когда мы говорили об общем виде протокола DDE, мы отметили,что он основан на обмене сообщениями. В большинстве случаев сообщения посылаются (post) а не передаются (send). Приэтом приложение, посылающее сообщение не имеет возможности дождаться обработкиэтого сообщения. Поэтому те данные, которые посылаются одним приложением,должны быть уничтожены другим, что бы они не оставались в памяти. Единственноеисключение из этого правила — сообщения WM_DDE_INITIATE и ответ на него (WM_DDE_ACK)которые всегда передаются, а не посылаются.
При начале DDE–разговора посылается сообщение
WM_DDE_INITIATE                             hWnd       aTopic& aService
это сообщение используется для началаDDE–разговора. Параметр wParamсодержит хендл пославшего окна, а lParamв младшем слове — атом aService,а в старшем — атом aTopic,задающие, соответственно, сервис и тему DDE–разговора. Нулевые значения атомовуказывают соответственно на любую поддерживаемую тему и любой сервис. Передача: Когда клиентвызывает функцию SendMessageдля установления DDE–разговора, он создает необходимые атомы, а после возвратаиз функции SendMessageон обязан эти атомы удалить. Получение:сервер, получивший это сообщение, и поддерживающий указанные сервис и темуотвечает сообщением WM_DDE_ACK.При этом он обязан создать заново требуемые атомы — использовать атомы, полученныес сообщением запрещено. Значения атомов aTopic и aService равные 0указывают, соответственно, на любую поддерживаемую сервером тему и сервис. Вэтом случае сервер должен ответить столько раз, сколько подходящих тем исервисов он поддерживает.
Это сообщение передается всем приложениям (в качестве хендлаокна–получателя сообщения используется HWND_BROADCAST или -1). Тот сервер,который поддерживает данную тему и сервис должен ответить на этот запроспередачей сообщения
WM_DDE_ACK    hWnd                        aTopic& aService
сообщение, подтверждающее принятоепрежде сообщение. Параметр wParamсодержит хендл пославшего окна, а lParamиспользуется разными способами, в зависимости от того, какое сообщение вызвалоэто подтверждение. В зависимости от значений параметров сообщения рассматриваютположительные и отрицательныеподтверждения. При ответе на WM_DDE_INITIATEмладшее слово lParamсодержит атом сервиса, а старшее — атом темы, при этом WM_DDE_ACK непосылается, а передаетсяс помощью функции SendMessage,причем оно рассматривается только как положительное, так как в качествеотрицательного ответа используется отсутствиеэтого сообщения. Если WM_DDE_ACKполучено в ответ на WM_DDE_INITIATE,то для обоих платформ — Windows 3.x и Win32 оно используется одинаково; а еслионо получено в ответ на какое–либо иное сообщение, то параметр lParam будетиспользован для передачи “упакованного” значения. Получение: приложение–клиент,получившее сообщение WM_DDE_ACKобязано удалить все сопровождающие его атомы. Передача: при ответе на WM_DDE_INITIATEзапрещено использовать полученные атомы, сервер обязан создать необходимые дляответа атомы и послать их (так как на одно WM_DDE_INITIATEможет ответить несколько серверов, а при обработке WM_DDE_ACK клиентобязан удалять все атомы).
Если WM_DDE_ACKпередается в ответ на сообщение WM_DDE_INITIATE,то в lParamсодержатся такие–же данные, что и у сообщения WM_DDE_INITIATE.Это считается положительным подтверждением. Если ответ отрицательный, топодтверждение при инициализации просто не выдается.
В большинстве случаев сервер, отвечающий на WM_DDE_INITIATE,создает специальное окно для каждого DDE–разговора. Это связано с тем, что придальнейшем обмене данными сервис и тема указываться не будут, а самDDE–разговор определяется фактически окном–клиентом и окном–сервером. Однатакая пара окон обменивается данными только в рамках указанных при установленииDDE–разговора темы и сервиса. Только самые простые DDE–серверы могут обходитьсяодним окном, но при этом они в данный момент времени могут работать только с однимклиентом.
Внимание! При установлении связи с каким–либосервером Вы можете получить несколько сообщений WM_DDE_ACK отразных серверов, поддерживающих указанные сервис и тему. Если в сообщении WM_DDE_INITIATEвы указали любую поддерживаемую тему и/или любой возможный сервис, топрактически всегда отвечают сразу несколько серверов. В этом случае Вы должнысоздавать список всех тем и сервисов, с которыми устанавливается связь даннойоперацией (не забудьте в конце закрыть все начатые DDE–разговоры), а если высобираетесь работать только с одним из ответивших серверов, то со всемиостальными вы должны завершить DDE–разговор посылкой WM_DDE_TERMINATE.
Замечание. Согласно документации сервер,получивший WM_DDE_INITIATEс указанием любой поддерживаемой темы или сервиса, обязан отправить в ответстолько сообщений WM_DDE_ACK,сколько тем и сервисов он поддерживает. На самом деле многие DDE серверы этогоне делают, как, например, Microsoft Internet Explorer. Такие серверы отвечаюттолько на точно указанные сервис и тему. Это может быть оправдано, если серверподдерживает значительное число тем или сервисов. При ответе на WM_DDE_INITIATE ужеустанавливаетя DDE–разговор, для которого сервер создает отдельное окно и,соответственно, расходует ресурсы. Если сервер отвечает на целый список теми/или сервисов, то создается много окон, что, скорее всего, излишне. Строгоговоря, указывать любую тему или сервис надо только в случае реальнойнеобходимости, так как одним таким запросом могут быть установлены несколькодесятков DDE–разговоров сразу (так, например, один только Netscape Navigatorотвечает примерно 3 десятками поддерживаемых им тем).
/>
Начало DDE–разговора — это единственный случай, когдасообщения DDE передаются с помощью SendMessage, а не PostMessage. Этонеобходимо для нормального начала обмена.
Сейчас мы рассмотрим небольшой пример взаимодействия двухприложений, клиента и сервера при начале DDE–разговора.
В данном примере рассмотрен самый простой случай, когда связьустанавливается только с одним сервером и для одной темы. При этом можнообойтись без создания списка сервисов и тем, с которыми устанавливаютсясоединения.
Считается, что при инициализации DDE–разговора, клиент долженполучить ответ (или убедится в его отсутствии) немедленно. Этого можно достичьтолько используя передачу, а не посылку сообщений. В этом случае подтверждениеприходит в то время, пока клиент ожидает завершения работы процедуры SendMessage.Для нормального продолжения DDE клиент должен запомнить хендл сервера (а сервер- хендл клиента).
Так как возможен случай, что на запрос клиента ответят два иболее серверов, то надо предусмотреть либо отказ от установления более чемодного соединения (как в приведенном примере), либо организовать DDE сразу снесколькими серверами.
/>
Если клиент получил положительный ответ от сервера, то онможет начать обмен данными. Этот обмен будет продолжаться до тех пор, пока обаприложения не обменяются сообщениями
WM_DDE_TERMINATE                       hWnd                                0L
сообщение оканчивает DDE–разговор.Параметр hWndявляется хендлом пославшего окна. При этом уничтожаются вспомогательныеструктуры и обнуляются переменные.
/>
Послать WM_DDE_TERMINATEможет как клиент, так и сервер. Оба они должны быть готовы к приему такогосообщения от напарника.
/>Обменданными между клиентом и сервером
Обмен данными между клиентом и сервером может происходить понескольким различным сценариям. Всего можно выделить три способа полученияданных от сервера и еще один способ, предназначенный для передачи данных отклиента к серверу.
В DDE различают три способа получения данных от сервера,называемых видами связи.Эти три вида связи называются холодная,теплая и горячая. Коротко пояснимразличия этих видов связи:
·   холоднаясвязь — cold link
обмен данными происходит только позапросу клиента. Сервер посылает в ответ данные или отрицательноеподтверждение.
·   горячаясвязь — hot link
клиент “подписывается” напериодическое получение данных от сервера, после чего сервер начинаетпередавать данные клиенту, как только в этом возникает необходимость. Длязавершения горячей связи клиент должен сообщить об этом серверу.
·   теплаясвязь — warm link
клиент, как и при горячей связи,подписывается на получение обновленных данных. Однако сервер передает неданные, а только сообщения о том, что у него есть данные для клиента. Клиентможет затребовать данные у сервера в любой удобный для него момент.
Последние два вида связи (теплая и горячая) называются иногдапостоянной (permanent)связью.
Передача данных от клиента к серверу осуществляется толькоодним способом, по инициативе клиента, который передает серверу соответствующеесообщение, содержащее посылаемые данные.
Когда два приложения обмениваются данными друг с другом, онипередают друг другу хендлы блоков данных и атомы. Для того, что бы эти данныебыли доступны обоим приложениям, они должны быть глобальными. Однако надоучесть, что обычно глобальные блоки данных связаны с тем приложением, котороеих создало, то есть при завершении приложения эти данные автоматическиуничтожаются. Так как процесс DDE–разговора является асинхронным, то необходимообеспечивать сохранность данных независимо от существования создавшего ихприложения. Для этого глобальные блоки данных должны быть разделяемыми — при ихвыделении надо указывать флаг GMEM_DDESHARE(или GMEM_SHARE,который является синонимом).
В случае платформы Win32 используются прежние функции длявыделения глобальных блоков данных, несмотря на то, что блоки выделяются тольков локальном для каждого процесса виртуальном адресном пространстве. Системаавтоматически осуществляет передачу данных из адресного пространства одногопроцесса в адресное пространство другого процесса при передаще соответствующихсообщений.
Особое внимание надо уделить вопросу освобождения ресурсов,так как атомы и передаваемые блоки данных сами не уничтожаются, даже если всеучаствующие в DDE приложения завершили работу. Все созданные объекты должныбыть обязательно уничтожены, независимо от исхода операции. Сложности связаны стем, что один и тот–же объект может быть уничтожен либо клиентом, либосервером, в зависимости от протекания процесса обмена, а, кроме того, впроцессе обмена могут присходить различные ошибки (например, функция PostMessageне может послать сообщение).“Холодная” связь — cold link
При холоднойсвязи обмен данными активируется клиентом. Для этого клиентпосылает специальное сообщение, в ответ на которое сервер отправляет данные(если может), либо сообщение об ошибке:
WM_DDE_REQUEST                            hWnd       aItem& cfFormat
это сообщение является требованиемпередачи данных. Параметр wParamявляется хендлом пославшего сообщение окна, а lParam содержит вмладшем слове номер формата данных (номера те–же, что используются буферомобмена), а в старшем — атом имени данных. Платформы Win32 и Windows 3.xиспользуют это сообщение одинаково.
В ответ на это сообщение сервер может ответить либосообщением WM_DDE_DATA,если он имеет необходимые данные и может их послать, либо “отрицательным”сообщением WM_DDE_ACK,указывающим, что сервер не может послать требуемые данные.
WM_DDE_DATA  hWnd                        aItem& hData (Packed Win32)
Это сообщение передает данныеклиенту. Младший компонент lParamсодержит хендл глобального разделяемого блока данных hData. Этотпараметр может быть равен 0,что реально встречается только в случае “теплой” связи (см. ниже). Передаваемыйблок данных должен начинаться со структуры DDEDATA, флаг fAckReqкоторой указывает на необходимость отправки подтверждения о получении данных. Передача: сервер не может сбрасыватьодновременно оба бита fReleaseи fAckReqв 0.То есть корректными являются только три возможных комбинации fRelease и fAckReq,при которых можно определить, кто должен освобождать блок данных — клиент илисервер. Освобождение ресурсов:атом aItemдолжен удаляться, если только он не посылается с ответным сообщением WM_DDE_ACK(если бит fAckReqзаголовка данных равен 1,то есть требуется ответ). Освобождение блока данных зависит от установки битов fReleaseи fAckReqзаголовка данных:fRelease fAckReq 1 блок данных всегда освобождается сервером при получении ответа от клиента (считается, что к моменту получения сервером подтверждения клиент уже прочитал требуемые данные и они ему больше не нужны). 1 блок данных всегда освобождается клиентом, сервер ответа не получает (так клиент может некоторое время сохранять полученные данные независимо от течения DDE–разговора). 1 1 при успешном чтении блок данных освобождается клиентом, а в случае ошибки (то есть при получении сервером отрицательного подтверждения) блок освобождается сервером. Этот вариант близок к предыдущему, за исключением того, что за ошибочно полученный блок клиент ответственности не несет.
Структура DDEDATA содержит следующие данные:
typedef structtagDDEDATA {       WORD unused:12,                   fResponse:1,  // 1 в ответна WM_DDE_REQUEST, 0 — WM_DDE_ADVISE         fRelease:1,          // данныедолжны быть удалены после получения     reserved:1,     fAckReq:1;         // 1— необходимо послать подтверждение о приеме           short cfFormat;        //формат данных         BYTE Value[1];       // сами данные   } DDEDATA;
Внимание! в документации по Windows 3.x SDK иWin32 SDK содержалась ошибка — там были перепутаны пояснения к полям fResponseи fAckReq,так что описание структуры DDEDATAпротиворечило описанию сообщения WM_DDE_DATA.В документации, сопровождающей компилятор Microsoft Visual C++ v4.0 эта ошибкабыла исправлена.
Заметьте, что сервер может потребовать подтверждения о приемепосланных данных. Для этого он устанавливает бит fAckReq в заголовкепереданных данных равным 1.В таком случае необходимо послать ответное сообщение WM_DDE_ACK(положительное или отрицательное, если возникла какая-либо ошибка).
/>
Если сервер не имеет нужных данных,то вместо WM_DDE_DATAклиент получит WM_DDE_ACK (отрицательное)
/>
WM_DDE_ACK    hWnd                        aItem& wStatus (Packed Win32)
Младший компонент lParam содержитинформацию о посылаемом подтверждении. Младший байт этого слова содержит код,возвращаемый приложением, старший байт содержит два значащих бита 0x80 и 0x40.Часто этот параметр представляется в виде структуры DDEACK. Старшееслово lParamпредставляет собой атом, идентифицирующий данные. Согласно документации этотатом обязательно должен быть удален при обработке этого сообщения. В случаеплатформы Win32 параметр lParamиспользуется для передачи “упакованного” значения; при этом надо помнить, чтоесли WM_DDE_ACKприходит в ответ на WM_DDE_INITIATE,то параметр lParamсодержит обычные, не упакованные данные.
Структура DDEACKсодержит следующие данные:
typedef struct tagDDEACK{          WORD    bAppReturnCode:8,     // код, определяемый приложением            reserved:6,    fBusy:1,   // 1 — сервер занят и не может обработать запрос            fAck:1;    //1 — сообщение было получено приложением    } DDEACK;
В зависимости от значений битов fBusy и fAckразличают следующие возможные виды ответов:
·   fAck= 1 fBusy = 0 — положительноеподтверждение
·   fAck= 0 fBusy = 0 — отрицательноеподтверждение
·   fAck= 0 fBusy = 1 — сервер занят, либо неможет обработать запрос в разумные сроки (что есть “разумные” сроки — непоясняется)
·   Согласно документации установка обеихфлагов fAckи fBusyодновременно должна быть исключена.
Вместо структуры DDEACK часто используют просто битовыешаблоны — 0x8000соответствует положительному подтверждению, 0 — отрицательномуи 0x4000— сервер занят (вообще говоря, это тоже отрицательное подтверждение, хотязапрос можно попробовать повторить).
Дополнительно ситуация осложняется тем, что в процессеDDE–разговора применяется посылка, а не передача сообщений. То есть междупосылкой сообщения серверу и получения от него ответа может пройти значительноевремя, в течении которого необходимо обрабатывать остальные поступающиесообщения, система при этом может переключаться на другие приложения и, чтосамое неприятное, в это время либо клиент, либо сервер могут вообще завершитьработу. В этой ситуации приходится вставлять специальные фрагменты кода,осуществляющие ожидание того или иного сообщения в течении указанногопромежутка времени и принимать специальные меры, если ответа слишком долго нет.
//     // waitfor time out   //    static BOOL TimeOut( HWND hWnd, UINT wMsg, UINT uTimeOut ){            DWORD dwTime;        MSG msg;
  // FALSEdesign normal termination       // TRUE design time-out or negative WM_DDE_ACK           dwTime=GetCurrentTime();        while ( dwTime + uTimeOut > GetCurrentTime() ) {                if( PeekMessage( &msg, hWnd, NULL, NULL, PM_REMOVE ) ) {                        //обработатьполученноесообщение                    TranslateMessage( &msg );                       DispatchMessage(&msg );                  if ( msg.message == WM_DDE_ACK ) {                        //check for .fAck                            return LOWORD( msg.lParam ) &0x8000? FALSE: TRUE;                     } else if ( msg.message == wMsg )return FALSE;             }          }
  return TRUE;     }
Обычно процедура, ожидающая нужное сообщение, выглядитнесколько иначе. Это связано с желательным отображением курсора ожидания (ввиде песочных часов), а также с обработкой сообщений, извлеченных из очереди.Во многих случаях нормальная обработка сообщений не сводится только к функциям TranslateMessageи DispatchMessage,но также включает поддержку акселераторов, немодальных диалогов и пр. Помимоэтого следует учесть, что в случае платформы Windows 3.x размер очередисообщений приложения сравнительно невелик — так что при извлечении сообщений,направленных только конкретному окну, очередь может легко переполниться.
Еще одно замечание касается времени ожидания ответа отсервера. Этот промежуток не надо назначать очень большим, но в то же время ондолжен быть достаточным, что бы даже на медленных машинах уверенно получатьответ от сервера. При избыточно большом времени ожидания ответа может возникатьвпечатление “зависания” приложения, пока оно ждет ответа. Обычно человекожидает от компьютера почти мгновенной реакции или, в худшем случае, явновидимой работы. Если программа останавливается на срок более 10–15 секунд, тоуже возникает беспокойство о ее работе.
Практически приемлемым является интервал порядка 5–30 секунд,так как на средней машине ответ может задерживаться до 5–10 секунд принормальной работе и, соответственно, на медленных машинах интервал следуетувеличить примерно до тридцати секунд. (Различие между быстрыми и медленнымимашинами весьма относительно — с ростом мощности компьютеров растет нагрузка наних, поэтому время выполнения более–менее значительных операций снижаетсясравнительно медленно). Возможным решением является двухэтапное ожидание —первый этап до 3–6 секунд проходит как обычно, а по истечении этого интервалаотображается окно диалога с кнопкой “Cancel”, после чего ожидание продолжаетсялибо до получения ответа, либо до отмены ожидания пользователем. Это позволяетпользователю оценить занятость системы и, при необходимости, продолжитьожидание в разумных пределах.
Задача определения времени ожидания ответа от сервераявляется достаточно важной из–за необходимости освобождать занятые ресурсы.Фактически, при обмене сообщениями возникает следующая ситуация: при посылкекаких либо разделяемых объектов от одного приложения к другому (это могут бытьатомы или глобальные блоки данных) необходимо эти объекты удалять после ихиспользования. Однако тут надо учесть следующие нюансы:
·    если функция PostMessageне может послать сообщение, объекты можно удалять сразу, так как второеприложение их не получит.
·    если сообщениепослано, то момент уничтожения объектов выбирается в соответствии с описаниемсообщения в документации. Так, например, атомы обычно удаляются по следующейсхеме
-    при получениипосланного запроса проверяется необходимость ответа, если ответ нужен, то атомпередается вместе с ответом и уничтожается приложением, пославшим запрос
-    если ответ нужен,но его послать не удается, то атом уничтожается получившим запрос приложением
-    если ответ ненужен, то атом опять–же уничтожается получившим запрос приложением
·    посланноесообщение может “не дойти” до адресата (например, если окно–получатель будетзакрыто уже после того, как к нему в очередь попадет это сообщение, но до егообработки). При этом ответа не будет, а приложение–получатель освободитьресурсы не сможет.
·    посланноесообщение может быть успешно принято, а при его обработке, но уже послеосвобождения полученных ресурсов, получатель не сможет отправить обратнонеобходимого подтверждения.
В двух последних случаях вопрос освобождения ресурсовостается открытым, так как приложение, пославшее запрос, не может получитьинформацию о результатах его выполнения, и, соответственно, о необходимостиосвобождения данных. При этом можно слегка изменить рассмотренное ранее“двухэтапное” ожидание, добавив в диалог кнопку или флажок освобожденияресурсов. Обычно пользователь может оценить причину задержки ответа — сбой вработе, либо досрочное завершение работы приложения и момент возникновения этойситуации — до обработки или после. По крайней мере, если ситуация будетповторяющейся, то пользователь сможет подобрать наиболее “дешевый” выход из нее.“Горячая” связь — hot link
Протокол при горячейсвязи. После инициации обмена клиент посылает запрос WM_DDE_ADVISE,указывая желаемые данные. Сервер отвечает о возможности горячей связи, и,обычно, сразу же посылает первые данные, для инициации клиента. Таким образомосуществялется как–бы “подписка” на получение необходимых данных при ихизменении. Этот вид связи удобен, если Вам необходимо взаимодействовать скакой–либо базой данных, содержимое которой может динамически обновляться, какнапример база данных “товары на складе” для больших магазинов — доступ к этойбазе одновременно могут иметь разные отделы магазина и список товаров,имеющихся на сладе, постоянно изменяется. Использование горячей связи в этомслучае позволит создать локальные списки на компьютерах разных отделов иавтоматически обновлять их, как только осуществляется изменение общей базыданных.
/>
WM_DDE_ADVISE                                hWnd aItem& hOptions (Packed Win32)
Младший компонент параметра lParamсодержит хендл глобального разделяемого блока, содержащего небольшую структуру DDEADVISE,а старший компонент — атом, описывающий данные на которые осуществляетсяподписка. Структура DDEADVISEопределяет тип связи — горячая или холодная, а также необходимость требоватьподтверждения о доставке данных от клиента. В случае платформы Win32 параметр lParamиспользуется для передачи “упакованного” значения. Сервер, получивший этосообщение обязан ответить на него посылкой подтверждения WM_DDE_ACK,положительного или отрицательного. Освобождениересурсов: Клиент, пославший запрос, должен дождаться ответа отсервера. Если ответ положительный, то блок данных должен быть удален сервером,а если отрицательный, то клиент должен сам его удалить.
Структура DDEADVISEсодержит следующие данные:
typedef struct {          WORD    reserved:14,                  fDeferUpd:1,      // 1-”теплая” связь, 0-”горячая”                       fAckReq:1;               //1-необходимо требовать подтверждение, 0-не надо          short   cfFormat;                 //желаемый формат данных      } DDEADVISE;
При задании бита fDeferUpd равным 1 осуществляетсятак называемая теплая связь, при которой сервер только информирует о наличии унего измененных данных, но не посылает их клиенту непосредственно. В случаегорячей связи этот бит должет быть равен 0. Бит fAckReq, равный 1,указывает на необходимость требовать от клиента подтверждения о полученииданных. Позже, когда сервер начнет посылать клиенту сообщения WM_DDE_DATA онустановит в структуре DDEDATAбит fAckReqсоответствующим тому, который Вы указали в структуре DDEACK и,соответственно, клиент должен будет подтверждать получение им данных от сервера(надо учесть, что бит fAckReqвлияет на стратегию освобождения посылаемых данных — подробнее см. описаниесообщения WM_DDE_DATA).
Поле cfFormatуказывает на формат данных, которые клиент ожидает от сервера. Вы можете дляодного вида данных в пределах одного DDE–разговора подписаться на получениеданных разного типа одновременно, посылая WM_DDE_ADVISEстолько раз, сколько форматов данных Вы хотите получать. Однако такаявозможность разрешена только для горячей связи. Согласно документации подписка на одни и те–же данныев разных форматах для теплой связи запрещена.
Сервер, при получении сообщения WM_DDE_ADVISE,проверяет возможность осуществить подписку на эти данные, создает необходимыеему структуры данных и отвечает клиенту сообщением WM_DDE_ACK —положительным или отрицательным. Это сообщение в данном случае используетсятак–же, как и при ответе на WM_DDE_DATA.
Если подписка осуществлена успешно, то далее сервер начинаетизвещать клиента об обновлении данных, осуществляя каждый раз пересылку этихданных клиенту с помощью уже рассмотренного сообщения WM_DDE_DATA.Единственное отличие от получения данных в ответ на WM_DDE_REQUEST заключаетсяв том, что бит fResponseструктуры DDEDATAравен 0,тогда как при отправке данных по запросу он устанавливается в 1. Это позволяетопределить причину получения данных при обработке сообщения.
/>
Заканчивается такой обмен посылкой WM_DDE_UNADVISEсерверу. В этом сообщении указывается, от подписки на какие данные клиентотказывается (так как теоретически один клиент может подписаться сразу на нескольковидов данных).
/>
WM_DDE_UNADVISE                          hWnd       aItem& cfFormat
Младшее слово параметра lParamописывает формат данных, а старшее слово — атом, описывающий данные на которыеосуществляется подписка. В случае платформы Win32 параметр lParam используетсятакже, как и в случае Windows 3.x. Если параметр cfFormat равен 0,то отменяется подписка на указанные данные для всех тех форматов, для которыхбыла осуществлена подписка. Если параметр aItem равен NULL,то отменяется подписка на все виды данных в пределах данного DDE–разговора.Сервер, обработав это сообщение, должен послать в ответ подтверждение WM_DDE_ACK,положительное или отрицательное; при этом если одна операция отменяет подпискуна данные в разных форматах, то посылается только одно подтверждение.
Сообщение WM_DDE_UNADVISEне эквивалентено WM_DDE_TERMINATE,после его обработки отменяется только подписка, а сам DDE–разговорпродолжается, так что клиент может установить затем новый вид связи с сервером.Сервер обязан ответить на сообщение WM_DDE_ADVISE положительным илиотрицательным подтверждением.“Теплая” связь — warm link
Протокол при теплойсвязи. После инициации обмена клиент посылает запрос WM_DDE_ADVISE,указывая желаемые данные. Сервер отвечает о возможности теплой связи, и,обычно, сразу же посылает WM_DDE_DATA,извещая клиента о возможности немедленного получения данных. Позже серверпосылает WM_DDE_DATAклиенту, когда данные обновляются. Сообщения WM_DDE_DATA вданном случае не передают самих данных, а только извещают клиента о их наличииу сервера. Протоколы связи и используемые сообщения при теплой связипрактически полностью совпадают с протоколами и сообщениями горячи связи,поэтому мы просто рассмотрим те немногие отличия, которые есть между двумяэтими видами связи.
Как и горячая связь, теплая устанавливается с помощьюсообщения WM_DDE_ADVISEс единственным исключением — бит fDeferUpdструктуры DDEADVISEравен 1,а не 0.
/>
Далее сервер начинает информировать клиента об обновлении(или наличии) у него нужных данных. Для этого используется обычное сообщение WM_DDE_DATA,которое в данном случае не передает никаких данных. Для этого параметр hData(младшее слово lParamили, в случае Win32, младшая компонента, извлекаемая с помощью UnpackDDElParam)устанавливается равным 0.Клиент, получая такое сообщение без данных, может немедленно запросить нужныеданные или просто запомнить, что данные изменились и позже, при необходимости,получить их.
/>
Заметьте, что в случае горячей связи вы можете подписатьсясразу на получение информации об изменении одних и тех–же данных в разныхформатах, а в случае теплой связи это запрещено. Такой запрет объясняется тем,что при поддержке таких видов связи для разных данных или одних и тех–жеданных, но в разных форматах, клиент должен вести список данных и форматов накоторые осуществлена подписка. При получении WM_DDE_DATA,информирующего об изменении данных имя данных задается параметром aItem(упакован в lParam),а формат данных — полем cfFormatструктуры DDEDATA.Однако в случае теплой связи сообщение WM_DDE_DATA не содержит данных и,следовательно, информация о формате данных недоступна, при этом однозначнонайти запись в спике не представляется возможным, если только не наложитьограничение — при теплой связи подписываться можно на данные только в одномформате, тогда запись списка будет однознано определена именем данных.
Для того, что бы получить нужные данные, клиент простопосылает отдельный запрос WM_DDE_REQUEST,как в случае холодной связи. Таким образом теплая связь является “гибридом”двух других видов связи — холодной и горячей.
/>
Отмена подписки в случае теплой связи ничем не отличается оттакого–же действия для горячей связи — клиент посылает сообщение WM_DDE_UNADVISEсерверу, который отвечает на это подтверждением.
/>
Работа с DDE в случае теплой или горячей связи обычно невызывает значительных сложностей; однако надо обратить внимание на возможностьиспользования нескольких видов связи одновременно. Обычно, при написанииприложений используются предположения типа “В ответ на посланное WM_DDE_REQUESTдолжно прийти либо WM_DDE_DATA,либо WM_DDE_ACK”.Однако в жизни ситуация может оказаться существенно сложнее — если одно окноиспользует несколько соединений, то в ответ на посланный WM_DDE_REQUESTсначала может прийти сообщение WM_DDE_DATA,информирующее о получении данных (в случае теплой или горячей связи) и толькопотом настоящий ответ на WM_DDE_REQUEST.Для того, что бы уменьшить вероятность таких ситуаций, для каждогоDDE–разговора создают специальное скрытое окно, взаимодействующее с сервером(или клиентом). Однако при разработке собственных приложений такую возможностьвсе–равно надо учитывать, так как в процедурах обработки сообщений придетсяпроверять, в связи с каким событием получено то или иное сообщение; а приразработке DDE–клиентов надо постараться разделить разные виды обмена даннымилибо по разным DDE–разговорам, либо по времени.Передача данных от клиента к серверу
При обмене данными между клиентом и сервером возможно нетолько получение клиентом данных, содержащихся на сервере, но и возможностьпередачи некоторых данных от клиента к серверу. Для этого предусмотреноспециальное сообщение WM_DDE_POKE,которое посылается клиентом серверу для записи данных.
/>
WM_DDE_POKE  hWnd                        aItem& hData (Packed Win32)
Сообщение WM_DDE_POKEпосылается клиентом для передачи данных серверу. В случае платформы Windows 3.xмладший компонент параметра lParamсодержит хендл передаваемого блока данных, а старший — атом имени передаваемыхданных. Передаваемый блок данных должен быть глобальным и разделяемым и долженначинаться со структуры DDEPOKE.При посылке сообщения WM_DDE_POKEиспользуется следующая стратегия освобождения блока данных: он уничтожаетсяпославшим приложением (клиентом), если:
·   в заголовке блока бит fRelease равен 0
·   клиент вернул отрицательный ответ
·   посылка данных не состоялась
Получившее данные приложение (сервер)будет уничтожать этот блок только если бит fRelease равен 1 исервер возвращает положительное подтверждение.
typedef struct {          WORD    unused:13,                    fRelease:1,          // 1 — блок освобождается клиентом                  fReserved:2;      short   cfFormat;           // формат данных (см. форматы буфера обмена)            BYTE Value[1];       //передаваемые данные    } DDEPOKE;
Сервер, получивший это сообщение должен сохранить полученныеданные и сообщить клиенту о результате выполнения этой операции посылкойсообщения WM_DDE_ACK,положительного или отрицательного. В этом сообщении может быть переданполученный атом имени данных, либо полученный может быть удален, а для посылкиподтверждения создан заново.Выполнениекоманд DDE
Помимо механизма обмена данными между клиентом и сервером DDEпредусматривает еще один способ взаимодействия приложений — выполнение серверомкоманд клиента. Для того, что бы указать серверу на необходимость выполнениякоманды, клиент должен послать специальное сообщение WM_DDE_EXECUTE, вответ на которое сервер пытается выполнить команду и сообщает клиенту орезультате ее выполнения, посылая в ответ сообщение WM_DDE_ACK(положительное или отрицательное).
/>
WM_DDE_EXECUTE                            hWnd hCmd& 0 (Windows 3.x)
WM_DDE_EXECUTE                            hWnd            hCmd(Win32)
Параметр lParam описываеткоманду, передаваемую серверу. Для этого строка, содержащая команду, помещаетсяв блок данных, выделяемый с помощью функции GlobalAlloc сфлагом GMEM_DDESHARE.В случае платформы Windows 3.x старшее слово параметра lParam содержитхендл этого блока, а младшее — просто 0; А в случае платформы Win32 параметрlParamнепосредственно содержит этот хендл. Освобождение ресурсов: блок данных, содержащий команду,должен быть освобожден клиентом при получении подтверждения от сервера. Вместес этим подтверждением возвращается хендл этого блока, так что клиент свободноможет его освободить.
В случае платформы Win32 строка, содержащая команду можетбыть представлена UNICODE–строкой, но только в том случае, если оба участвующиев DDE–разговоре окна поддерживают UNICODE, то есть оба возвращают TRUEпри вызове функции IsWindowUnicode(hWnd ).
В ответ на сообщение WM_DDE_EXECUTE сервер посылаетсообщение WM_DDE_ACK,информирующее о выполнении команды (положительное) или о неудаче(отрицательное). В этом сообщении возвращается хендл блока, содержащегокоманду, так что сервер может этот блок удалить, а также в параметре wStatus— в младшем байте — может быть возвращен дополнительный код, уточняющийрезультат или причину ошибки (см. описание структуры DDEACK,представляющей параметр wStatusв разделе, посвященном холодной связи).
WM_DDE_ACK    hWnd                        hCmd& wStatus (Packed Win32)
Младший компонент lParam содержитинформацию о посылаемом подтверждении. Младший байт этого слова содержит код,возвращаемый приложением, старший байт содержит два значащих бита 0x80 и 0x40.Часто этот параметр представляется в виде структуры DDEACK. Старшийкомпонент lParamпредставляет собой хендл блока, содержащего команду. Согласно документации этотблок обязательно должен быть освобожден при обработке этого сообщения. В случаеплатформы Win32 параметр lParamиспользуется для передачи хендла “упакованных” данных.
При разработке собственных DDE–серверов, исполняющихкакие–либо команды, надо обратить внимание на формат записи этих команд.Разработчики Microsoft предполагают совершенно определенный формат записикоманд, который, к сожалению, часто не поддерживается стороннимиразработчиками. Конечно, с точки зрения совместимости, желательнопридерживаться этого формата.
В соответствии с документацией Microsoft команда должнапредставлять собой строку, оканчивающуюся нулевым символом и содержащую одну илинесколько команд. Каждая команда должна быть заключена в квадратные скобки […].Каждая команда состоит из двух частей — имени команды и, возможно, спискапараметров. При этом список параметров заключается в круглые скобки, апараметры в списке разделяются запятыми. Имя команды не может содержатьпробелов, скобок, запятых и кавычек; параметры могут содержать эти символы, нов таком случае такой параметр должен быть заключен в кавычки, а символ кавычекв тексте параметра представляется как повторяющаяся кавычка. В прежних версияхDDE — для Windows 3.x — требовалось, что бы символы ( ) [ ],встречаемые в тексте параметра (в кавычках) дублировались; в то время как вWin32 это уже не требуется. Проблема, однако, существует — в среде Win32 могутбыть запущены как 16–тиразрядные приложения, так и 32–хразрядные, при этом возможно взаимодействие таких приложений посредством DDE. Втакой ситуации серверы должны распознавать оба варианта представления скобок —как повторяющиеся, так и не повторящиеся.
Примеры для Win32:
[команда]     [команда1][команда2][команда_с_параметрами(параметр1, параметр2)][командаN]      [команда(параметр1,”параметр2 с пробелами, скобками []() и “”кавычками”)]
В случае Windows 3.x последний пример будет выглядеть так:
[команда(параметр1,”параметр2с пробелами, скобками [[]](()) и “” кавычками”)]
Если строка содержит несколько заключенных в квадратныескобки команд, то сервер должен разобрать эту строку по командам и обработатьих последовательно. Вариант с передачей нескольких команд в одной строке имеетодно неудобство — возвращается результат выполнения всей последовательностикоманд, причем понять где возникла ошибка (скажем, синтаксис команды илинедостаток свободных ресурсов) будет практически невозможно.
ТемаDDE–разговора “System”
Согласно документации Microsoft всем вновь разрабатываемымDDE–серверам рекомендуется поддерживать в каждом сервисе специальную служебнуютему DDE–разговора “System”.Эта тема предназначена для получения основной информации о сервере. В пределахэтой темы определено несколько стандартных имен данных, к которым можнообратиться для получения требуемой информации.
К сожалению сами разработчики Microsoft не очень строгособлюдают это правило. Так, например, Microsoft Word и Microsoft Excelподдерживают эту тему, а Program Manager, Explorer, Internet Explorer — неподдерживают. Однако документация остается документацией, поэтому приразработке собственных DDE–серверов стоит придерживаться предлагаемых правил.
При обмене данными с серверами, поддерживающими тему “System”надо придерживаться следующих правил:
·   для получения данных используетсяхолодная связь
·   данные предоставляются только вформате CF_TEXT
·   по многим запрашиваемым даннымвозвращается список значений. Этот список представлен в виде строки,оканчивающейся нулевым символом, отдельные пункты которой отделяются символомтабуляции (код — 9).
·   При использовании DDEML имя темы “System”определено в файле DDEML.Hкак SZDDESYS_TOPIC.
Помимо темы “System”предусмотрен специальное название данных, которые должны поддерживаться во всехостальных темах, с помощью которого можно получить информацию о всехподдерживаемых именах данных в данной теме.
При разработке собственного сервера не обязательноподдерживать все приведенные данные, равно как не обязательно вообщеподдерживать тему “System”(хотя это желательно). Если Вы поддерживаете только некоторые данные этой темы,обратите внимание на данные “SysItems”,которые позволяют клиенту узнать, какие именно данные можно запрашивать.Название темы для DDE Название темы для DDEML Описание Formats SZDDESYS_ITEM_FORMATS Возвращает список поддерживаемых сервером форматов данных. Обычно названия совпадают с названиями форматов буфера обмена, но первые три символа “CF_” опущены. То есть формат CF_BITMAP будет представлен как BITMAP. Рекомендуется так упорядочивать названия форматов в списке, что бы форматы передающие больше информации были первыми. То есть, например, формат DIB должен быть расположен до формата BITMAP. Help SZDDESYS_ITEM_HELP Пояснения к использованию данного DDE сервера. Достаточно произвольный текст, желательно поясняющий, какие данные могут быть получены, какие команды могут быть выполнены какие данные могут быть переданы серверу и т.д. ReturnMessage SZDDESYS_ITEM_RTNMSG Информация о последнем отправленом подтверждении WM_DDE_ACK. С помощью этого типа данных можно передавать дополнительные данные, уточняющие результат выполнения последней транзакции. Status SZDDESYS_ITEM_STATUS В ответ на этот запрос сервер должен отправить строку “Busy” или “Ready”, информирующую о состоянии сервера. Заметьте, что эту строку сервер должен посылать, даже если он занят и не может обрабатывать иные запросы. SysItems SZDDESYS_ITEM_SYSITEMS Возвращает список имен данных, которые представлены в теме “System”. Этот список может использоваться клиентом, что бы узнать, какие данные он может запрашивать в рамках темы “System”. Topics SZDDESYS_ITEM_TOPICS Возвращает список тем, поддерживаемых сервером в данный момент времени. В процессе работы сервера этот список может изменяться, так, например, Microsoft Word открывает отдельную тему для работы с каждым загруженным документом. TopicItemList SZDDE_ITEM_ITEMLIST Аналогично SysItems, возвращает список данных, поддерживаемых для данной темы. Этот запрос может применяться к темам, не являющимися темой “System”. Этот список может изменяться в процессе работы сервера.
ProgramManager в качестве DDE–сервера
Program Manager является специализированным DDE–сервером. Онпозволяет запущенным приложениям управлять конфигурацией групп приложений,изменять их атрибуты и т.д. Обычно программы установки приложений используютDDE с Program Manager для создания необходимой группы и наполнения ееэлементами. В случае Windows–94 или Windows NT v4.0 и выше Program Managerобычно не используется, но работающий при этом Explorer поддерживает те–жесамые сервис, тему и команды, что и Program Manager. При этом вместо группсоздаются подменю в меню Пуск|Программы (Start|Programs).
В принципе Windows может быть сконфигурирован так, что вместоProgram Manager используется совершенно иная оболочка, которая может неподдерживать описываемый DDE. Возможным выходом из этой ситуации являетсяпопытка запуска Program Manager (PROGMAN.EXE),если с первого раза не удается установить DDE–разговор.
Для взаимодействия с Program Manager необходимо установитьDDE–разговор с сервером, поддерживающим сервис и тему с одинаковым именем “PROGMAN”,а затем можно передавать необходимые команды (сообщение WM_DDE_EXECUTE).Команды, которые поддерживает Program Manager, позволяют приложению создавать,отображать, удалять и перезагружать группы, добавлять, изменять или удалятьэлементы групп и завершать работу Program Manager. Для этого предназначеныследующие команды:
CreateGroup  — создать группу программ
ShowGroup    — показать группу в указанномсостоянии
Reload    — перезагрузить группу из файла
DeleteGroup   — удалить группу
AddItem — добавить элемент
ReplaceItem    — изменить элемент
DeleteItem — удалить элемент
ExitProgman  — выйти изProgram Manager
Program Manager обрабатывает команды в формате,рекомендованном Microsoft. То есть каждая команда заключается в квадратныескобки, а если она имеет дополнительные параметры, то список параметровзаключается в круглые скобки, причем параметры в списке разделены запятыми. Водном запросе к серверу можно указать несколько команд сразу, например:
[ShowGroup(“Accessories”,1)][AddItem(myapp.exe,”Myapp”,myapp.exe,5)]
Этот пример добавляет элемент “My app” в группу“Accessories”.
Помимо выполнения этих команд Program Manager может выполнятьобмен данными с клиентом, благодаря чему возможно получение информации обимеющихся группах и элементах этих групп. Для этого используются те же именасервиса и темы “PROGMAN”,что и для выполнения команд, но при этом серверу передаются запросы наполучение данных по холодной связи в формате CF_TEXT.
Для того, что бы получить список групп, клиент долженпрочитать данные с именем “Group”.В ответ Program Manager вернет список групп, разделенный символами переводастроки.
Приложение может запросить у Program Manager информацию огруппе. Для этого оно должно прочитать данные с именем, соответствующем именигруппы. Возвращается текст из нескольких строк, каждая строка состоит изнескольких компонент, разделяемых запятыми. Первая строка определяет информациюо самой группе, а все последующие — об элементах группы.
Информация о группе состоит из:
·   имени группы, заключенного в кавычки
·   пути к файлу группы (.grp)
·   число элементов в группе
Информация об элементе группы:
·   команда, заключенная в кавычки
·   название каталога
·   пути к файлу, содержащему пиктограмму
·   индексу пиктограммы в файле
·   “горячей клавише” (в числовой форме)
·   флагу минимизированного состояния призапуске приложения
Примечание: в документации утверждается, что на количествоэлементов в группе наложено ограничение — не более 50 элементов наодну группу. Однако существует иное, гораздо более жесткое ограничение — размерgrp–файлапод Windows 3.x не может быть больше 64K. Этот размер может быть легкопревышен, если используются видео–режимы с большим количеством цветов (Hi–Colorили TrueColor), например 16,24или 32бита на пиксел. Дело в том, что grp–файлсодержит в себе изображения всех пиктограмм, размер которых увеличивается сувеличением числа цветов. При этом предельный размер grp–файла может бытьдостигнут после десятка элементов (для TrueColor — примерно 13 элементов).
Далее более подробно описываются команды, обрабатываемыеProgram Manager. При этом мы будем придерживаться следующего синтаксиса —описание самой команды в квадратные скобки не заключается, а необязательныепараметры выделяются с их помощью. Для лучшей читаемости параметры разделяютсядополнительным пробелом (при передачи команды этих пробелов быть не должно).
Рекомендуется дополнительно просмотреть раздел “Выполнениекоманд DDE” для получения справок о синтаксисе записи команд и параметров.
CreateGroup(GroupName [,CommonGroupFlag] ) CreateGroup( GroupName [,GroupFile] )
Команда CreateGroupсоздает указанную группу, либо делает активной уже существующую.
Параметры:
GroupName    строка, задающая имя группы.
CommonGroupFlag  параметр указывает, какая группасоздается. Если он равен 1,то создается общая группа, а если 0,то персональная. Если работающий пользователь не имеет административныхпривилегий, то попытка создать общую группу не удастся. По умолчаниюиспользуется 1для пользователей, располагающих привилегиями администратора, и 0 для остальныхпользователей.
GroupFile        в Windows 3.1 этот параметрзадавал имя grp–файла.В старших версиях Windows в качестве второго параметра рассматриваются только 0 и 1,иначе этот параметр игнорируется.
ShowGroup( GroupName,ShowCommand [,CommonGroupFlag] )
Команда ShowGroupпозволяет показать группу в нормальном, максимизированном или минимизированномвиде.
Параметры:
GroupName    строка, задающая имя группы.
ShowCommand   целое число, указывающее, в какомвиде надо отобразить окно группы.
1  — отобразить активным в нормальном состоянии
2  — отобразить активным в минимизированном состоянии
3  — отобразить активным в максимизированном состоянии
4  — отобразить окно группы в нормальном состоянии; активноеокно останется активным
5  — делает окно группы активным, не изменяя ее размера иположения
6  — минимизирует окно группы
7  — минимизирует окно группы; активное окно останетсяактивным
8  — отображает в текущем состоянии
CommonGroupFlag  параметр указывает, какая группасоздается. Если он равен 1,то создается общая группа, а если 0,то персональная. Если работающий пользователь не имеет административныхпривилегий, то попытка создать общую группу не удастся. По умолчаниюиспользуется 1для пользователей, располагающих привилегиями администратора, и 0 для остальныхпользователей.
Reload( GroupName[,CommonGroupFlag] )
Эта команда заставляет Program Manager удалить описаниегруппы из памяти и загрузить grp–файлснова. Такая возможность может применяться, если программа модифицируетнепосредственно grp–файл,а затем хочет отобразить сделанные изменения.
Параметры:
GroupName    строка, задающая имя группы.
CommonGroupFlag  параметр указывает, какая группасоздается. Если он равен 1,то создается общая группа, а если 0,то персональная. Если работающий пользователь не имеет административныхпривилегий, то попытка создать общую группу не удастся. По умолчаниюиспользуется 1для пользователей, располагающих привилегиями администратора, и 0 для остальныхпользователей.
DeleteGroup(GroupName [,CommonGroupFlag] ) DeleteGroup( GroupName [,GroupFile] )
Команда DeleteGroupудаляет указанную группу.
Параметры:
GroupName    строка, задающая имя группы.
CommonGroupFlag  параметр указывает, какая группасоздается. Если он равен 1,то создается общая группа, а если 0,то персональная. Если работающий пользователь не имеет административныхпривилегий, то попытка создать общую группу не удастся. По умолчаниюиспользуется 1для пользователей, располагающих привилегиями администратора, и 0 для остальныхпользователей.
GroupFile        в Windows 3.1 этот параметрзадавал имя grp–файла.В старших версиях Windows в качестве второго параметра рассматриваются только 0 и 1,иначе этот параметр игнорируется.
AddItem( CmdLine[,Name [,IconPath [,IconIndex [,xPos, yPos [,DefDir       [,HotKey [,fMinimize[fSeparateMemSpace] ] ] ] ] ] ] )
Команда AddItemпозволяет добавить в активную группу программный элемент.
Параметры:
CmdLine         строка, задающая команду,осуществляющую запуск приложения. Эта строка должна содержать имя приложения, атакже может содержать полный путь к приложению и другие необходимые параметры.
Name               задает имя элемента вгруппе. Если этот параметр опущен, то используется имя приложения.
IconPath          задает полное имя файла,содержащего ресурс пиктограммы, которая будет отображаться в окне группы. Этоможет быть выполняемый файл Windows (.exe, .dll) или отдельнаяпиктограмма (.ico).Если этот параметр опущен, то пиктограмма ищется в файле приложения, причем,если указанный файл не является приложеним, то используется пиктограммаассоциированного с данным типом файлов приложения (ассоциации задаются спомощью реестра Windows); а если и это не удается, то используется пиктограммапо умолчанию.
IconIndex        задает индекс пиктограммы вфайле, определенном параметром IconPath.
xPos, yPos       задают положение пиктограммы вокне группы. Эти параметры представлены целыми числами. Если позиция не задана,то Program Manager находит первое неиспользуемое место.
DefDir             задает имя рабочегокаталога приложения.
HotKey            задает “горячую” клавишу,используемую для вызова приложения.
fMinimize        указывает, надо–ли отображатьокно приложения при запуске в виде пиктограммы.
fSeparateMemSpace  указывает, надо–ли запускать 16–тиразрядное приложение в самостоятельном адресном пространстве (используется нанекоторых платформах Win32).
ReplaceItem( ItemName )
Команда ReplaceItemиспользуется для замены одного элемента другим. При выполнении этой командыуказанный элемент удаляется, а его позиция запоминается. Последующий вызов AddItemсоздаст новый элемент в этом месте.
Параметры:
ItemName        строка, задающая имяудаляемого элемента. На его месте будет размещен следующий создаваемый элемент.
DeleteItem( ItemName )
Команда DeleteItemосуществляет удаление указанного программного элемента.
Параметры:
ItemName        строка, задающая имяудаляемого элемента.
ExitProgman(bSaveGroups )
Эта команда завершает работу Program Manager.
Параметры:
bSaveGroups  величина, указывающая надо или нетсохранять текущее состояние групп перед завершением работы Program Manager. Призначении 0сохранения групп не происходит.


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

Поделись с друзьями, за репост + 100 мильонов к студенческой карме :

Пишем реферат самостоятельно:
! Как писать рефераты
Практические рекомендации по написанию студенческих рефератов.
! План реферата Краткий список разделов, отражающий структура и порядок работы над будующим рефератом.
! Введение реферата Вводная часть работы, в которой отражается цель и обозначается список задач.
! Заключение реферата В заключении подводятся итоги, описывается была ли достигнута поставленная цель, каковы результаты.
! Оформление рефератов Методические рекомендации по грамотному оформлению работы по ГОСТ.

Читайте также:
Виды рефератов Какими бывают рефераты по своему назначению и структуре.

Сейчас смотрят :

Реферат Проходка конвейерной выработки на шахте Новодонецкая
Реферат Введение «Временного положения» 1867-1868 гг. и политико-правовые последствия для казахов
Реферат Взяточничество 3
Реферат A Comparison Of Contemporary And Romantic Literatu
Реферат Образ Софьи в комедии Грибоедова Горе от ума
Реферат организация и планирование машиностроительного производства
Реферат Карамзин Н. М.
Реферат Культура эпохи Реформации
Реферат Особливості ціноутворення на продукцію промислового підприємства
Реферат Организация работы холодного цеха кафе на 75 посадочных мест
Реферат "Великий перелом" и его изображение в романе А.А. Шолохова "Поднятая целина"
Реферат Проблемы регулирования внешнеэкономической деятельностью
Реферат Колядки та щедрівки
Реферат Euclid of Alexandria
Реферат Организация производства на многопредметной прерывно-поточной линии МППЛ