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

Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы

© Александр Фролов, Григорий Фролов
Том 17, М.: Диалог-МИФИ, 1994, 287 стр.

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

2.4. Отложенная запись

Мы уже говорили о том, что приложения, как правило, записывают данные в Clipboard одновременно в нескольких форматах. В некоторых случаях это может привести к неэкономному расходованию оперативной памяти. Например, если приложение копирует битовое изображение в форматах CF_DIB, CF_BITMAP вместе с CF_PALETTE, CF_METAFILEPICT и еще в нескольких собственных форматах (о собственных форматах читайте ниже), общий объем израсходованной для записи памяти может оказаться значительным. В то же время, если пользователь скопировал изображение в Clipboard только для того чтобы, например, вставить его в Paintbrush, может оказаться достаточным использование формата метафайла.

В этом разделе мы расскажем о том, как организовать отложенную запись (delayed rendering) в Clipboard и приведем исходные тексты приложения, выполняющего такую операцию с текстом.

Выполнение отложенной записи

Сам по себе процесс выполнения отложенной записи предельно прост: достаточно открыть и очистить Clipboard, вызвать функцию SetClipboardData, указав в первом параметре формат данных, а во втором - значение NULL, и закрыть Clipboard. Так, отложенная запись текстовых данных выглядит следующим образом:

OpenClipboard(hwnd);
EmptyClipboard();
SetClipboardData(CF_TEXT, NULL);
CloseClipboard();

Выполняя отложенную запись данных в Clipboard, приложение как бы объявляет, что оно способно "по первому требованию" других приложений предоставить данные в указанном формате. Сами данные на момент объявления могут не существовать.

Как только какое-либо приложение попытается прочитать записанные подобным образом данные, Windows обнаружит, что данных нет в памяти, так как для идентификатора соответствующего блока памяти указано значение NULL. В этом случае приложение, выполнившее отложенную запись, получит сообщение WM_RENDERFORMAT, в ответ на которое оно должно выполнить реальную запись данных.

Параметр wParam сообщения WM_RENDERFORMAT содержит код формата данных, который потребовался приложению, выполняющему чтение данных из Clipboard.

Обработчик сообщения WM_RENDERFORMAT должен, не открывая и не очищая Clipboard, предоставить данные в нужном формате, вызвав функцию SetClipboardData. На этот раз необходимо указать реальный идентификатор незафиксированного блока памяти, содержащего копируемые данные:

SetClipboardData(wParam, hglbTextCopyBuf);

После выполнения операции не следует закрывать Clipboard.

Теперь рассмотрим следующую ситуацию. Пусть пользователь скопировал данные из приложения в Clipboard с использованием отложенной записи, а затем попытался закрыть это приложение, и пусть в момент копирования не было активно ни одно окно динамического просмотра Clipboard.

В данной ситуации данные объявлены, но реально их нет в памяти. Следовательно, перед завершением приложения требуется выполнить запись данных в Clipboard во всех возможных форматах.

Если приложение объявило форматы данных в Clipboard, "взяв на себя обязательство" обеспечить их в случае необходимости, оно не может уйти со сцены, не выполнив реальной записи данных, так как в противном случае записывать объявленные данные будет некому.

Эта проблема имеет простое решение. Перед завершением работы приложение, объявившее в Clipboard данные, но не предоставившее их, получит от Windows сообщение WM_RENDERALLFORMATS. В ответ на это сообщение оно должно выполнить реальную запись данных.

Вот возможная реализация обработчика сообщения WM_RENDERALLFORMATS:

case WM_RENDERALLFORMATS:
{
   OpenClipboard(hwnd);
   EmptyClipboard();
   SendMessage(hwnd, WM_RENDERFORMAT, CF_TEXT, 0L);
   SendMessage(hwnd, WM_RENDERFORMAT, CF_BITMAP, 0L);
   SendMessage(hwnd, WM_RENDERFORMAT, CF_PALETTE, 0L);
   CloseClipboard();
   return 0;
}

Приложение несколько раз посылает само себе сообщение WM_RENDERFORMAT, указывая все возможные для него форматы данных. При этом обработчик сообщения WM_RENDERFORMAT каждый раз будет выполнять реальную запись данных в Clipboard.

Возможна такая ситуация, когда приложение выполнило отложенную запись и затем предоставила реальные данные, заказав для их хранения блок глобальной памяти, а другое приложение вскоре после этого очистило Clipboard и записало туда свои данные. Теперь первое приложение хранит в глобальной памяти никому не нужные данные, которые могут занимать много места.

В указанной ситуации ваше приложение получит сообщение WM_DESTROYCLIPBOARD. Обработчик этого сообщения может уничтожить ненужный блок памяти.

Приложение CLIPRNDR

Для иллюстрации метода отложенной записи мы изменили приложение CLIPTXT, создав на его основе приложение CLIPRNDR. Внешний вид главного окна приложения и выполняемые им функции не изменились, однако теперь это приложение способно выполнять более экономную отложенную запись данных в Clipboard.

Основной файл исходных текстов приведен в листинге 2.11.


Листинг 2.11. Файл cliprndr/cliprndr.cpp


// ----------------------------------------
// Отложенная запись данных в Clipboard
// ----------------------------------------
#define STRICT
#include <windows.h>
#include <mem.h>
#include "cliprndr.hpp"

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char const szClassName[]   = "ClipRndrClass";
char const szWindowTitle[] = "Delayed Rendering Demo";
char const szClipboardText[] =
  "Этот текст будет записан\r\n"
  "в универсальный буфер обмена Clipboard\r\n"
  "приложением CLIPRNDR";
// =====================================
// Функция WinMain
// =====================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpszCmdLine, int nCmdShow)
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  if(!hPrevInstance)
    if(!InitApp(hInstance))
      return FALSE;

  hwnd = CreateWindow(
    szClassName, szWindowTitle,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT, CW_USEDEFAULT,
    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)(COLOR_WINDOW + 1);
  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;
  RECT rc;

  static HGLOBAL hglbTextCopyBuf;
  LPSTR lpTextCopy;
  static HGLOBAL hglbTextPasteBuf;
  LPSTR lpTextPaste;
  static HGLOBAL hglbClipBuf;
  LPSTR lpClipBuf;

  switch (msg)
  {
    case WM_CREATE:
    {
      hglbTextPasteBuf = GlobalAlloc(GHND, 1);
      if(hglbTextPasteBuf == NULL)
        return -1;
      lpTextPaste = (LPSTR)GlobalLock(hglbTextPasteBuf);
      if(hglbTextPasteBuf == NULL)
        return -1;
      lpTextPaste[0] = '\0';
      GlobalUnlock(hglbTextPasteBuf);
      return 0;
    }
    case WM_PAINT:
    {
      hdc = BeginPaint(hwnd, &ps);
      lpTextPaste = (LPSTR)GlobalLock(hglbTextPasteBuf);
      if(lpTextPaste != NULL)
      {
        GetClientRect(hwnd, &rc);
        DrawText(hdc, lpTextPaste, -1, &rc,
          DT_LEFT | DT_EXPANDTABS);
        GlobalUnlock(hglbTextPasteBuf);
      }
      EndPaint(hwnd, &ps);
    }
    case WM_COMMAND:
    {
      switch (wParam)
      {
        // Выполняем отложенное копирование данных
        case CM_EDITCOPY:
        {
          OpenClipboard(hwnd);
          EmptyClipboard();
          SetClipboardData(CF_TEXT, NULL);
          CloseClipboard();
          return 0;
        }

        // Чтение текстовых данных из Clipboard
        case CM_EDITPASTE:
        {
          OpenClipboard(hwnd);
          hglbClipBuf = GetClipboardData(CF_TEXT);
          if(hglbClipBuf != NULL)
          {
            lpClipBuf = (LPSTR)GlobalLock(hglbClipBuf);
            if(lpClipBuf != NULL)
            {
              hglbTextPasteBuf =
                 GlobalReAlloc(hglbTextPasteBuf,
              GlobalSize(hglbClipBuf), GMEM_NODISCARD);

              lpTextPaste = 
                 (LPSTR)GlobalLock(hglbTextPasteBuf);
              if(lpTextPaste != NULL)
              {
                lstrcpy(lpTextPaste, lpClipBuf);
                InvalidateRect(hwnd, NULL, TRUE);
                GlobalUnlock(hglbTextPasteBuf);
              }
              else
                MessageBox(hwnd, "Мало памяти",
                  (LPSTR)szWindowTitle, MB_OK | MB_ICONHAND);
              GlobalUnlock(hglbClipBuf);
            }
            else
              MessageBox(hwnd, "Мало памяти",
                (LPSTR)szWindowTitle, MB_OK | MB_ICONHAND);
          }
          else
            MessageBox(hwnd, "Формат CF_TEXT недоступен",
            (LPSTR)szWindowTitle, MB_OK | MB_ICONHAND);
          CloseClipboard();
          return 0;
        }
        case CM_HELPABOUT:
        {
          MessageBox(hwnd,
            "Приложение CLIPTXT\n(C) Фролов А.В., 1995",
            (LPSTR)szWindowTitle,
            MB_OK | MB_ICONINFORMATION);
          return 0;
        }
        case CM_FILEEXIT:
        {
          DestroyWindow(hwnd);
          return 0;
        }
        default:
          return 0;
      }
    }
    // Копируем данные во всех форматах
    case WM_RENDERALLFORMATS:
    {
       OpenClipboard(hwnd);
       EmptyClipboard();

       // Инициируем копирование в текстовом формате
       SendMessage(hwnd, WM_RENDERFORMAT, CF_TEXT, 0L);
       CloseClipboard();
       return 0;
    }
    // Копируем данные в нужном формате
    case WM_RENDERFORMAT:
    {
      // Работаем только с текстовым форматом
      if(wParam != CF_TEXT) return 0;

      hglbTextCopyBuf = GlobalAlloc(GHND,
        sizeof(szClipboardText) + 1);
      if(hglbTextCopyBuf != NULL)
      {
        lpTextCopy = (LPSTR)GlobalLock(hglbTextCopyBuf);
        if(lpTextCopy != NULL)
        {
          lstrcpy(lpTextCopy, szClipboardText);
          GlobalUnlock(hglbTextCopyBuf);

          // Фактическая запись данных
          SetClipboardData(wParam, hglbTextCopyBuf);
        }
        else
          MessageBox(hwnd, "Мало памяти",
            (LPSTR)szWindowTitle, MB_OK | MB_ICONHAND);
      }
      else
        MessageBox(hwnd, "Мало памяти",
          (LPSTR)szWindowTitle, MB_OK | MB_ICONHAND);
      return 0;
    }
    case WM_DESTROY:
    {
      if(hglbTextPasteBuf != NULL)
        GlobalFree(hglbTextPasteBuf);
      PostQuitMessage(0);
      return 0;
    }
    default:
      break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

В приложении CLIPRNDR использованы рассмотренные нами приемы. Так как данные, предназначенные для записи в Clipboard, определены статически, приложение не освобождает занимаемую ими память и, соответственно, не обрабатывает сообщение WM_DESTROYCLIPBOARD.

Файл cliprndr.hpp содержит определения констант (листинг 2.12).


Листинг 2.12. Файл cliprndr/cliprndr.hpp


#define CM_HELPABOUT 24000
#define CM_EDITPASTE 24001
#define CM_EDITCOPY  24002
#define CM_FILEEXIT  24003

Файл описания ресурсов приведен в листинге 2.13.


Листинг 2.13. Файл cliprndr/cliprndr.rc


#include "cliprndr.hpp"
APP_MENU MENU 
BEGIN
        POPUP "&File"
        BEGIN
                MENUITEM "E&xit",     CM_FILEEXIT
        END
        POPUP "&Edit"
        BEGIN
                MENUITEM "&Copy",     CM_EDITCOPY
                MENUITEM "&Paste",    CM_EDITPASTE
        END
        POPUP "&Help"
        BEGIN
                MENUITEM "&About...", CM_HELPABOUT
        END
END
APP_ICON ICON "cliprndr.ico"

Файл определения модуля вы найдете в листинге 2.14.


Листинг 2.14. Файл cliprndr/cliprndr.def


NAME        CLIPRNDR
DESCRIPTION 'Приложение CLIPRNDR, (C) 1995, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   8120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

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