Электронная библиотека книг Александра Фролова и Григория Фролова.
 
Библиотека
Братьев
Фроловых
Электронная библиотека книг Александра Фролова и Григория Фролова.
Библиотека системного программиста
Программирование на JAVA
ПК. Шаг за шагом
Другие книги
Восстановление данных
Антивирусная защита
Статьи для
программистов
Пользователю компьютера

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

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


Создание интернет-магазинов: http://www.shop2you.ru/ © Александр Фролов, Григорий Фролов, 1991-2016