Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система 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

[Назад] [Содеожание] [Дальше]