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

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

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