Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы© Александр Фролов, Григорий ФроловТом 17, М.: Диалог-МИФИ, 1994, 287 стр. 3.5. Приложение DDEMLCLПриложение DDEMLCL создано нами специально для работы с сервером DDEMLSR, описанном в предыдущем разделе. Вы можете запустить сервер перед запуском клиента DDEMLCL, либо не делать этого. В последнем случае на экране появится предупреждающее сообщение о том, что сервер не запущен (рис. 3.7).
Рис. 3.7. Запрос на запуск сервера DDEML Вам будет предложено запустить сервер, для чего следует нажать на клавишу "Yes". Приложение DDEMLCL предпримет попытку запустить приложение DDEMLSR из текущего каталога или из каталогов, указанных в переменной среды PATH операционной системы MS-DOS. Главное окно приложения представлено на рис. 3.8.
Рис. 3.8. Приложение DDEMLCL С помощью меню "Action" вы можете послать серверу текстовую строку "c:\\nicebmp\\sky.bmp" (строка "Send Filename") или запросить версию сервера (строка "Get Server Version"). В последнем случае принятая строка будет отображена на экране (рис. 3.9).
Рис. 3.9. Клиент отображает текстовую строку, полученную от сервера Функция WinMain и функция главного окна приложения DDEMLCL определены в файле ddemlcl.cpp (листинг 3.6). Листинг 3.6. Файл ddeml/ddemlcl.cpp // ---------------------------------------- // Приложение DDEMLCL // Клиент DDEML // ---------------------------------------- #define STRICT #include <windows.h> #include <windowsx.h> #include <ddeml.h> #include <dde.h> #include <mem.h> #pragma hdrstop #include "ddemlcl.hpp" // Прототипы функций HCONV DDEClientOpen(HINSTANCE hInst, LPSTR szService, LPSTR szTopic, LPSTR szItem); void DDEClientClose(HCONV); BOOL DDESend(HCONV, LPSTR); BOOL DDEReceive(HCONV, LPSTR szBuf, int nBufSize); BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Имя класса окна char const szClassName[] = "DDEMLCLIENT"; // Заголовок окна char const szWindowTitle[] = "DDEML Client"; HWND hwnd; HINSTANCE hInst; HCONV hConv = NULL; // ===================================== // Функция 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_OVERLAPPEDWINDOW, 20, 20, 250, 100, 0, 0, hInstance, NULL); if(!hwnd) return FALSE; ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); 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 и создаем канал связи if(hConv == NULL) { hConv = DDEClientOpen(hInst, (LPSTR)"BMPServer", (LPSTR)"BMPFile", (LPSTR)"DDEData"); // Если сервер не запущен, предоставляем // пользователю возможность запустить его if(hConv == NULL) { if(IDYES == MessageBox(hwnd, "Сервер не запущен.\nЗапустить?", "DDEML Client", MB_YESNO | MB_ICONHAND)) { WORD rc; // Выполняем попытку запуска сервера rc = WinExec("DDEMLSR", SW_SHOW); if(rc < 32) { MessageBox(hwnd, "Невозможно запустить сервер", "DDEML Client", MB_ICONHAND); return -1; } else { // После удачного запуска повторяем // попытку инициализации DDEML // и создания канала связи hConv = DDEClientOpen(hInst, (LPSTR)"BMPServer", (LPSTR)"BMPFile", (LPSTR)"DDEData"); if(hConv == NULL) return -1; else return 0; } } return -1; } return 0; } return 0; } // Обработка сообщений от меню case WM_COMMAND: { switch (wParam) { case CM_HELPABOUT: { MessageBox(hwnd, "DDEML Client\nVersion 1.0\n" "(C) Frolov A.V., 1995", "About DDEML Client", MB_OK | MB_ICONINFORMATION); return 0; } // Завершаем работу приложения case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } // Посылаем текстовую строку серверу case CM_MSG_TO_SERVER: { if(!DDESend(hConv, (LPSTR)"c:\\nicebmp\\sky.bmp")) MessageBox(hwnd, "Сервер не отвечает", "DDEML Client", MB_OK); return 0; } // Принимаем текстовую строку от сервера case CM_MSG_FROM_SERVER: { BYTE szBuf[256]; if(DDEReceive(hConv, szBuf, 80L)) MessageBox(hwnd, szBuf, "DDEML Client", MB_OK); else MessageBox(hwnd, "Сервер не отвечает", "DDEML Client", MB_OK); return 0; } default: return 0; } } case WM_DESTROY: { PostQuitMessage(0); // Завершаем работу с DDEML DDEClientClose(hConv); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); } Обработчик сообщения WM_CREATE при инициализации главного окна приложения вызывает функцию DDEClientOpen, определенную в файле ddemlcf.cpp (листинг 3.7). Эта функция регистрирует приложение в библиотеке DDEML и пытается создать канал связи с сервером. Если канал связи создать не удалось, приложение DDEMLCL делает вывод о том, что сервер не запущен, и предлагает пользователю запустить его. Запуск выполняется при помощи функции WinExec. После удачного запуска сервера делается еще одна попытка создать канал связи. Когда пользователь выбирает из меню "Action" строку "Send Filename", приложение вызывает функцию DDESend, определенную в файле ddemlcf.cpp. Этой функции передается идентификатор созданного канала связи и текстовая строка "c:\\nicebmp\\sky.bmp". Функция DDESend передаст строку серверу, который отобразит ее на экране. Если пользователь выбирает из меню "Action" строку "Get Server Version", вызывается функция DDEReceive, также определенная в файле ddemlcf.cpp. Этой функции помимо идентификатора канала связи передается адрес и размер буфера, в который нужно записать принятую информацию. После приема данные отображаются на экране в виде текстовой строки с помощью функции MessageBox. Когда приложение DDEMLCL завершает свою работу, вызывается функция DDEClientClose, закрывающая канал и освобождающая связанные с ним ресурсы. В файле ddemlcf.cpp (листинг 3.7) собраны все функции, имеющие отношение к DDEML. Листинг 3.7. Файл ddeml/ddemlcf.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 "ddemlcl.hpp" HDDEDATA EXPENTRY _export DDEClientCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2); // Идентификатор приложения для DDEML DWORD idInst; // Функция обратного вызова для DDE FARPROC lpDdeProc; HSZ hszService; HSZ hszTopic; HSZ hszItem; HDDEDATA hData; DWORD dwResult; WORD wFmt = CF_TEXT; //----------------------------------------------------- // Функция DDEClientOpen // Инициализация DDEML, создание идентификаторов строк //----------------------------------------------------- HCONV DDEClientOpen(HINSTANCE hInst, LPSTR szService, LPSTR szTopic, LPSTR szItem) { HCONV hConv = NULL; // Создаем переходник для функции обратного вызова lpDdeProc = MakeProcInstance((FARPROC)DDEClientCallback, hInst); // Инициализируем DDEML if(DdeInitialize((LPDWORD)&idInst, (PFNCALLBACK)lpDdeProc, APPCMD_CLIENTONLY, 0L)) { return NULL; } else { // При успешной инициализации создаем // идентификаторы строк для сервиса, раздела // и элемента данных hszService = DdeCreateStringHandle(idInst, szService, CP_WINANSI); hszTopic = DdeCreateStringHandle(idInst, szTopic, CP_WINANSI); hszItem = DdeCreateStringHandle(idInst, szItem, CP_WINANSI); // Устанавливаем канал связи hConv = DdeConnect(idInst, hszService, hszTopic, (PCONVCONTEXT)NULL); // Возвращаем идентификатор созданного канала связи return hConv; } } //----------------------------------------------------- // Функция DDEClientClose // Завершение работы с библиотекой DDEML //----------------------------------------------------- void DDEClientClose(HCONV hConv) { // Закрываем канал связи if(hConv != NULL) { DdeDisconnect(hConv); hConv = NULL; } // Освобождаем идентификаторы строк DdeFreeStringHandle(idInst, hszService); DdeFreeStringHandle(idInst, hszTopic); DdeFreeStringHandle(idInst, hszItem ); // Удаляем переходник для функции обратного вызова FreeProcInstance(lpDdeProc); } //----------------------------------------------------- // Функция DDESend // Передача серверу текстовой строки //----------------------------------------------------- BOOL DDESend(HCONV hConv, LPSTR szString) { if(hConv != NULL) { // Создаем идентификатор данных hData = DdeCreateDataHandle (idInst, szString, lstrlen(szString) + 1, 0L, hszItem, CF_TEXT, 0); // Запускаем транзакцию записи данных if(hData != NULL) hData = DdeClientTransaction((LPBYTE)hData, -1, hConv, hszItem, CF_TEXT, XTYP_POKE, 1000, &dwResult); if(hData != NULL) return TRUE; else return FALSE; } else return FALSE; } //----------------------------------------------------- // Функция DDEReceive // Получение от сервера текстовой строки //----------------------------------------------------- BOOL DDEReceive(HCONV hConv, LPSTR szBuf, int nBufSize) { // Запускаем транзакцию чтения данных if(hConv != NULL) { hData = DdeClientTransaction(NULL, 0, hConv, hszItem, CF_TEXT, XTYP_REQUEST, 1000, &dwResult); // Получаем строку от сервера if(hData != NULL) { DdeGetData(hData, szBuf, nBufSize, 0L); return TRUE; } else return FALSE; } else return FALSE; } //----------------------------------------------------- // Функция DDEClientCallback // Функция обратного вызова для клиента DDEML //----------------------------------------------------- #pragma argsused HDDEDATA EXPENTRY DDEClientCallback(WORD wType, WORD wFmt, HCONV hConvX, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1, DWORD dwData2) { switch(wType) { case XTYP_DISCONNECT: return((HDDEDATA) NULL); case XTYP_ERROR: break; case XTYP_XACT_COMPLETE: break; } return((HDDEDATA)NULL); } Все символические константы определены в файле ddemlcf.hpp (листинг 3.8). Листинг 3.8. Файл ddeml/ddemlcf.hpp #define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_EDITCOPY 303 #define CM_MSG_TO_SERVER 304 #define CM_MSG_FROM_SERVER 305 Файл описания ресурсов представлен в листинге 3.9. Листинг 3.9. Файл ddeml/ddemlcf.rc #include "ddemlcl.hpp" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Action" BEGIN MENUITEM "&Send Filename", CM_MSG_TO_SERVER MENUITEM "&Get Server Version", CM_MSG_FROM_SERVER END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END APP_ICON ICON "ddemlcl.ico" Файл определения модуля для приложения DDEMLCL вы найдете в листинге 3.10. Листинг 3.10. Файл ddeml/ddemlcf.def NAME DDEMLCL DESCRIPTION 'Приложение DDEMLCL, (C) 1995, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple |