Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы© Александр Фролов, Григорий ФроловТом 17, М.: Диалог-МИФИ, 1994, 287 стр. 3.4. Приложение DDEMLSRТеперь, когда вы познакомились с основными возможностями библиотеки DDEML, самое время приступить к практике. Мы подготовили для вас два приложения - DDEMLSR и DDEMLCL, которые, как нетрудно догадаться, являются сервером и клиентом DDEML. Приложение DDEMLSR (рис. 3.5) регистрирует сервис "BMPServer". Клиент может установить канал связи с разделом "BMPFile" и работать с элементом данных "DDEData".
Рис. 3.5. Приложение DDEMLSR Функции, выполняемые сервером, предельно просты. После запуска и регистрации сервиса сервер находится в состоянии ожидания запросов от клиента. Предусмотрены два вида запросов - запрос данных от сервера (транзакция XTYP_REQUEST) и передача данных серверу (транзакция XTYP_POKE). Когда сервер получает запрос на передачу данных клиенту, он в ответ передает текстовую строку, в которой находится описание версии приложения DDEMLSR. Если клиент посылает серверу данные (в виде текстовой строки), сервер отображает данные на экране при помощи функции MessageBox (рис. 3.6).
Рис. 3.6. Сервер отображает текстовую строку, полученную от клиента по каналу DDE Функция WinMain и функция главного окна приложения определены в файле ddemlsr.cpp (листинг 3.1). Листинг 3.1. Файл ddeml/ddemlsr.cpp // ---------------------------------------- // Приложение DDEMLSR // Сервер DDEML // ---------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <mem.h> #pragma hdrstop #include "ddemlsr.hpp" // Прототипы функций BOOL DDEServerOpen(HINSTANCE hInst, LPSTR szService, LPSTR szTopic, LPSTR szItem); void DDEServerClose(void); BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Имя класса окна char const szClassName[] = "DDEMLSERVER"; // Заголовок окна char const szWindowTitle[] = "DDEML Server"; HWND hwnd; HINSTANCE hInst; // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // Можно запускать только одну копию приложения if(hPrevInstance) return FALSE; // Инициализируем приложение if(!InitApp(hInstance)) return FALSE; // Сохраняем идентификатор приложения hInst = hInstance; hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_OVERLAPPED | WS_SYSMENU, 0, 0, 200, 100, 0, 0, hInstance, NULL); // Если создать окно не удалось, завершаем приложение if(!hwnd) return FALSE; // Если главное окно сервера должно быть // невидимым, поставьте перед следующей // строкой символ комментария ShowWindow(hwnd, SW_SHOWNORMAL); while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APP_ICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); wc.lpszClassName = (LPSTR)szClassName; aWndClass = RegisterClass(&wc); return (aWndClass != 0); } // ===================================== // Функция WndProc // ===================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_CREATE: { // Инициализируем DDEML DDEServerOpen(hInst, (LPSTR)"BMPServer", (LPSTR)"BMPFile", (LPSTR)"DDEData"); return 0; } // Обработка сообщений от меню case WM_COMMAND: { switch (wParam) { case CM_HELPABOUT: { MessageBox(hwnd, "DDEML Server\nVersion 1.0\n" "(C) Frolov A.V., 1995", "About DDEML Server", MB_OK | MB_ICONINFORMATION); return 0; } // Завершаем работу приложения case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } } case WM_DESTROY: { PostQuitMessage(0); // Завершаем работу с DDEML DDEServerClose(); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); } Приложение DDEMLSR создает обычное главное окно, имеющее меню. Однако в таком окне не всегда есть необходимость - сервер может вообще не рисовать на экране свое окно, так как его функции могут быть не связаны с отображением информации. Для того чтобы скрыть главное окно приложения (а точнее, не показывать его вовсе), достаточно убрать вызов функции ShowWindow перед запуском цикла обработки сообщений. Разумеется, для стиля окна в этом случае не следует использовать значение WS_VISIBLE. При создании главного окна приложения обработчик сообщения WM_CREATE инициализирует сервер, вызывая функцию DDEServerOpen, определенную в файле ddemlfn.cpp (листинг 3.2). Функции передается имена сервиса, раздела данных и элемента данных. Обработчики сообщений от меню предназначены для просмотра версии сервера и завершения работы приложения. Эти действия выполняются обычным образом. При завершении работы приложения обработчик сообщения WM_DESTROY закрывает канал DDEML и освобождает все занятые ресурсы, вызывая функцию DDEServerClose, определенную в файле ddemlfn.cpp. Для удобства мы собрали все функции, имеющие отношение к DDEML, в файле ddemlfn.cpp. Там же определена и функция обратного вызова для сервера DDEML. Листинг 3.2. Файл ddeml/ddemlfn.cpp // ----------------------------------------------------- // Функции для работы с библиотекой DDEML // Сервер DDEML // ----------------------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <ddeml.h> #include <dde.h> #include <mem.h> #include <string.h> #pragma hdrstop #include "ddemlsr.hpp" HDDEDATA EXPENTRY _export DDEServerCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2 ); // Идентификатор приложения, полученный после регистрации в // библиотеке DDEML DWORD idInst = 0L; FARPROC lpDdeProc; HSZ hszService = NULL; HSZ hszTopic = NULL; HSZ hszItem = NULL; // Идентификатор канала HCONV hConvApp = NULL; // Буфер для приема данных char szDDEData[200]; // Версия сервера. Эта текстовая строка // передается клиенту по его запросу char szDDEServerVersion[] = "DDEML Server v.1.0, (C) Frolov A.V."; //----------------------------------------------------- // Функция DDEServerOpen // Инициализация библиотеки DDEML //----------------------------------------------------- BOOL DDEServerOpen(HINSTANCE hInst, LPSTR szService, LPSTR szTopic, LPSTR szItem) { int rc; // Создаем переходник для функции обратного вызова lpDdeProc = MakeProcInstance((FARPROC)DDEServerCallback, hInst); // Выполняем инициализацию if(DdeInitialize((LPDWORD)&idInst, (PFNCALLBACK)lpDdeProc, APPCLASS_STANDARD, 0L)) { return FALSE; } // После успешной инициализации получаем идентификаторы // строк для сервиса, раздела и элемента данных else { hszService = DdeCreateStringHandle(idInst, szService, CP_WINANSI); hszTopic = DdeCreateStringHandle(idInst, szTopic, CP_WINANSI); hszItem = DdeCreateStringHandle(idInst, szItem, CP_WINANSI); // Регистрируем сервис DdeNameService(idInst, hszService, (HSZ)NULL, DNS_REGISTER); return TRUE; } } //----------------------------------------------------- // Функция DDEServerClose // Завершение работы с DDEML //----------------------------------------------------- void DDEServerClose(void) { // Завершение работы канала связи if(hConvApp != NULL) { DdeDisconnect(hConvApp); hConvApp = NULL; } // Сервис больше не доступен DdeNameService(idInst, hszService, (HSZ)NULL, DNS_UNREGISTER); // Освобождение идентификаторов строк DdeFreeStringHandle(idInst, hszService); DdeFreeStringHandle(idInst, hszTopic); DdeFreeStringHandle(idInst, hszItem); // Удаление переходника функции обратного вызова FreeProcInstance(lpDdeProc); } //----------------------------------------------------- // Функция DDEServerCallback // Функция обратного вызова для сервера DDEML //----------------------------------------------------- #pragma argsused HDDEDATA EXPENTRY _export DDEServerCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2) { switch(wType) { // Создание канала передачи данных case XTYP_CONNECT: { // Если сервис поддерживается, возвращаем // признак успешного создания канала if((HSZ)hsz2==(HSZ)hszService) return((HDDEDATA)TRUE); else return((HDDEDATA)FALSE); } // Запрос данных от сервера case XTYP_REQUEST: { // Создаем идентификатор данных hData = DdeCreateDataHandle(idInst, szDDEServerVersion, lstrlen(szDDEServerVersion) + 1, 0L, hszItem, CF_TEXT, 0); // В случае успеха возвращаем созданный идентификатор if(hData != NULL) return(hData); else return(NULL); } // Запрос на выполнение команды, отрабатывается вхолостую case XTYP_EXECUTE: break; // Передача данных серверу case XTYP_POKE: { // Проверяем элемент данных if(hsz1 == hszTopic) { // Получаем данные DdeGetData(hData, (LPBYTE) szDDEData, 200L, 0L); // Отображаем принятые данные на экране if(szDDEData != NULL) { MessageBox(NULL, szDDEData, "DDEML Server", MB_OK | MB_SYSTEMMODAL | MB_ICONINFORMATION); // Признак успешного завершения транзакции return((HDDEDATA)DDE_FACK); } } else return((HDDEDATA)NULL); break; } // Подтверждение создания канала case XTYP_CONNECT_CONFIRM: { // Сохраняем идентификатор канала hConvApp = hConv; break; } // Завершение работы канала case XTYP_DISCONNECT: { hConvApp = NULL; break; } // Ошибка case XTYP_ERROR: { break; } } return((HDDEDATA)NULL); } Транзакция XTYP_ERROR передается в функцию обратного вызова в случае возникновения ошибки. Младшее слово параметра dwData1 содержит код ошибки. Библиотека DDEML в Windows версии 3.1 поддерживает только один код ошибки - DML_ERR_LOW_MEMORY. Эта ошибка возникает при нехватке оперативной памяти. В случае возникновения такой ошибки выполнение обработки транзакции может быть не завершено. Файл ddemlsr.hpp (листинг 3.3) содержит определения констант для приложения DDEMLSR. Листинг 3.3. Файл ddeml/ddemlsr.hpp #define CM_HELPABOUT 301 #define CM_FILEINFO 302 #define CM_FILECLOSE 303 #define CM_FILEEXIT 304 #define CM_EDITCOPY 305 В файле определения ресурсов (листинг 3.4) описано главное меню приложения и пиктограмма. Листинг 3.4. Файл ddeml/ddemlsr.rc #include "ddemlsr.hpp" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END APP_ICON ICON "ddemlsr.ico" Файл определения модуля приложения DDEMLSR приведен в листинге 3.5. Листинг 3.5. Файл ddeml/ddemlsr.def NAME DDEMLSR DESCRIPTION 'Приложение DDEMLSR, (C) 1995, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple |