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

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр.

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

Передача сообщений между приложениями

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

Если скорость передачи данных не является критичной, можно воспользоваться удобным способом передачи данных, не требующим синхронизации. Этот метод основан на передаче сообщения WM_COPYDATA из одного приложения в другое при помощи функции SendMessage (функцию PostMessage для передачи этого сообщения использовать нельзя).

Сообщение WM_COPYDATA использует оба параметра - wParam и lParam. Через параметр wParam необходимо передать идентификатор окна, посылающего сообщение. Параметр lParam используется для передачи указателя на предварительно заполненную структуру COPYDATASTRUCT, в которой находится ссылка на передаваемые данные.

Если приложение обрабатывает сообщение WM_COPYDATA, то соответствующий обработчик должен вернуть значение TRUE, а если нет - FALSE.

Ниже мы привели формат структуры COPYDATASTRUCT:


typedef struct tagCOPYDATASTRUCT 
{
  DWORD dwData; // 32-разрядные данные
  DWORD cbData; // размер передаваемого буфера с данными
  PVOID lpData; // указатель на буфер с данными
} COPYDATASTRUCT;

Перед посылкой сообщения WM_COPYDATA приложение должно заполнить структуру COPYDATASTRUCT.

В поле dwData можно записать произвольное 32-разрядное значение, которое будет передано вместе с сообщением.

В поле lpData вы дополнительно можете записать указатель на область данных, полученную, например, при помощи функции HeapAlloc. Размер этой области следует записать в поле cbData.

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

Если вам достаточно передать из одного приложения в другое 32-разрядное значение, в поле lpData можно записать константу NULL.

Приложение RCLOCK

Для иллюстрации методов работы с сообщением WM_COPYDATA мы подготовили два приложения, которые называются RCLOCK и STIME.

Приложение RCLOCK раз в секунду получает от приложения STIME сообщение WM_COPYDATA, вместе с которым передается строка текущего времени. Полученая строка отображается в небольшом окне, расположенном в левом нижнем углу рабочего стола (рис. 2.2).

Рис. 2.2. Окно приложения RCLOCK

Заметим, что сразу после запуска (если приложение STIME еще не активно) в окне приложения RCLOCK отображается строка <Unknown>, как это показано на рис. 2.3.

Рис. 2.3. Исхндное состояние окна приложения RCLOCK

Если при активном приложении RCLOCK завершить приложение STIME, последнее передаст строку <Terminated>, которая будет показана в окне приложения RCLOCK.

Для того чтобы завершить работу приложения RCLOCK вы должны сделать его окно текущим, щелкнув по нему левой клавишей мыши, а затем нажать комбинацию клавиш <Ctrl+F4>. При необходимости вы сможете реализовать более удобный способ самостоятельно. В 11 томе “Библиотеки системного программиста” мы привели исходные тексты приложения TMCLOCK, из которого вы можете взять некоторые идеи для улучшения пользовательского интерфейса. Например, вы можете организовать перемещение окна приложения мышью, обрабатывая сообщение WM_NCHITTEST.

Исходные тексты приложения RCLOCK

Главный файл исходных текстов приложения RCLOCK представлен в листинге 2.3.

Листинг 2.3. Файл rclock/rclock.c


// ==================================================
// Приложение RCLOCK (серверное)
// Демонстрация использования сообщения WM_COPYDATA
// для передачи данных между процессами
//
// (С) Фролов А.В., 1996
// Email: frolov@glas.apc.org
// ==================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"
#include "afxres.h"
#include "rclock.h"

HINSTANCE hInst;
char szAppName[]  = "RclockApp";
char szAppTitle[] = "Remote Clock";

// Метрики шрифта с фиксированной шириной символов
LONG cxChar, cyChar;

RECT rc;
char szBuf[80];

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  // Сохраняем идентификатор приложения
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    // Если было, выдвигаем окно приложения на
    // передний план
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), 
    IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), 
    IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
     WS_POPUPWINDOW | WS_THICKFRAME, 
     100, 100, 100, 100, 
     NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Размещаем окно в нижней левой части рабочего стола
  GetWindowRect(GetDesktopWindow(), &rc);

  MoveWindow(hWnd,
    rc.right  - cxChar * 25,
    rc.bottom - cyChar * 3,
    cxChar * 10, cyChar * 2, TRUE);
    
  // Отображаем окно и запускаем цикл 
  // обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    // Это сообщение посылается приложением STIME
    case WM_COPYDATA:
    {
      // Копируем данные, полученные от приложения STIME,
      // во внутренний буфер
      strcpy(szBuf, ((PCOPYDATASTRUCT)lParam)->lpData);

      // Перерисовываем содержимое окна, отображая в нем
      // полученную строку символов
      InvalidateRect(hWnd, NULL, TRUE);
      break;
    }

    HANDLE_MSG(hWnd, WM_CREATE,  WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_PAINT,   WndProc_OnPaint);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct)
{
  HDC hdc;
  TEXTMETRIC tm;
  
  hdc = GetDC(hWnd);
  
  // Выбираем в контекст отображения шрифт с фиксированной
  // шириной букв
  SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

  // Определяем и сохраняем метрики шрифта
  GetTextMetrics(hdc, &tm);

  cxChar = tm.tmMaxCharWidth;
  cyChar = tm.tmHeight + tm.tmExternalLeading;
  
  ReleaseDC(hWnd, hdc);

  // Выполняем инициализацию буфера szBuf, содержимое
  // которого отображается в окне приложения
  strcpy(szBuf, (LPSTR)"<Unknown>");
 
  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnPaint
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnPaint(HWND hWnd)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;

  // Перерисовываем внутреннюю область окна
  hdc = BeginPaint(hWnd, &ps);
  
  GetClientRect(hWnd, &rc);

  // Рисуем в окне строку символов, полученную от
  // приложения STIME
  DrawText(hdc, szBuf, -1, &rc,
    DT_SINGLELINE | DT_CENTER | DT_VCENTER);

  EndPaint(hWnd, &ps);
  return 0;
}
Определения и глобальные переменные

В глобальном массиве szAppName хранится текстовая строка с названием приложения. Это название будет использовано приложением STIME для поиска главного окна приложения RCLOCK.

В глобальных переменных cxChar и cyChar хранятся метрики шрифта с фиксированной шириной символов, которые будут определены на этапе создания главного окна приложения при обработке сообщения WM_CREATE.

Структура rc типа RECT предназначена для хранения размеров окна рабочего стола.

Буфер szBuf используется для хранения данных, передаваемых из приложения STIME при помощи сообщения WM_COPYDATA.

Функция WinMain

Функция WinMain приложения RCLOCK сразу после запуска приложения выполняет поиск своей копии, используя для этого функцию FindWindow. Если такая копия найдена, главное окно этой копии выдвигается на передний план функцией SetForegroundWindow, после чего работа функции WinMain завершается. Такая техника уже использовалась нами ранее.

В том случае, когда запускается первая копия приложения RCLOCK, функция WinMain выполняет обычные действия. Она регистрирует класс окна и создает главное окно приложения. Для того чтобы это окно имело вид, показанный на рис. 2.2, для него указываются стили WS_POPUPWINDOW и WS_THICKFRAME:


hWnd = CreateWindow(szAppName, szAppTitle, 
   WS_POPUPWINDOW | WS_THICKFRAME, 
   100, 100, 100, 100, NULL, NULL, hInst, NULL);

Для определения размеров и расположения главного окна приложения RCLOCK функция WinMain определяет размеры окна рабочего стола, сохраняя их в глобальной переменной rc:


GetWindowRect(GetDesktopWindow(), &rc);

Размещение главного окна приложения RCLOCK выполняется функцией MoveWindow, как это показано ниже:


MoveWindow(hWnd,
  rc.right  - cxChar * 25, rc.bottom - cyChar * 3,
  cxChar * 10, cyChar * 2, TRUE);

Заметим, что метрики шрифта cxChar и cyChar определяются при обработке сообщения WM_CREATE, который получает управление при вызове функции CreateWindow. Поэтому после возвращения из функции CreateWindow содержимое глобальных переменных cxChar и cyChar будет отражать размеры рабочего стола.

После изменения размеров и расположения главного окна приложения RCLOCK выполняется отображение этого окна и запуск обычного цикла обработки сообщений.

Функция WndProc

В задачу функции WndProc входит обработка сообщений WM_CREATE, WM_DESTROY, WM_PAINT и WM_COPYDATA. Для обработки первых трех сообщений при помощи макрокоманды HANDLE_MSG вызываются функции WndProc_OnCreate, WndProc_OnDestroy и WndProc_OnPaint, соответственно.

Для сообщения WM_COPYDATA в файле windowsx.h, к сожалению, не предусмотрены специальные макрокоманды. Мы могли бы подготовить такую макрокоманду самостоятельно, однако, так как обработка сообщения WM_COPYDATA очень проста, мы использовали классический способ:


case WM_COPYDATA:
{
  strcpy(szBuf, ((PCOPYDATASTRUCT)lParam)->lpData);
  InvalidateRect(hWnd, NULL, TRUE);
  break;
}

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

В нашем случае в качестве данных передается строка символов, закрытая двоичным нулем, поэтому для копирования мы используем функцию strcpy.

После выполнения копирования обработчик сообщения WM_COPYDATA вызывает функцию InvalidateRect, что в результате приводит к перерисовке главного окна приложения. В этом окне обработчик сообщения WM_PAINT нарисует текстовую строку, полученную от другого приложения и скопированную только что в буфер szBuf.

Функция WndProc_OnCreate

Функция WndProc_OnCreate вызывается при создании главного окна приложения. Эта функция получает контекст отображения, выбирает в него шрифт с фиксированной шириной букв и определяет его метрики. Ширина и высота символов сохраняются, соответственно, в глобальных переменных cxChar и cyChar. Эти значения используются для вычисления размеров главного окна приложения.

В заверешении функция WndProc_OnCreate записывает в глобальный буфер szBuf строку <Unknown>:


strcpy(szBuf, (LPSTR)"<Unknown>");

Эта строка будет отображаться в главном окне приложения до тех пор, пока вы не запустите приложение STIME.

Функция WndProc_OnDestroy

Эта функция завершает цикл обработки сообщений, вызывая для этого функцию PostQuitMessage.

Функция WndProc_OnPaint

При обработке сообщения WM_PAINT функция WndProc_OnPaint отображает в главном окне приложения RCLOCK текстовую строку, записанную в буфере szBuf. Для рисования строки используется функция DrawText, так как с ее помощью легко выполнить центровку строки в окне по вертикали и горизонтали.

Файл rclock.h

В файле rclock.h (листинг 2.4) находятся прототипы функций, определенных в приложении RCLOCK.

Листинг 2.4. Файл rclock/rclock.h


// -----------------------------------------------------
// Описание функций
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

void WndProc_OnDestroy(HWND hWnd);
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct);
void WndProc_OnPaint(HWND hWnd);
Файл resource.h

Файл resource.h (листинг 2.5) создается автоматически и содержит определения для файла описания ресурсов приложения RCLOCK.

Листинг 2.5. Файл rclock/resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by RCLOCK.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        106
#define _APS_NEXT_COMMAND_VALUE         40006
#define _APS_NEXT_CONTROL_VALUE         1010
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
Файл rclock.rc

В файле rclock.rc (листинг 2.6) определены ресурсы приложения RCLOCK.

Листинг 2.6. Файл rclock/rclock.rc


//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure 
// application icon
// remains consistent on all systems.
IDI_APPICON             ICON    DISCARDABLE     "RCLOCK.ICO"
IDI_APPICONSM           ICON    DISCARDABLE     "RCLOCKSM.ICO"

//////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE 
BEGIN
    IDD_DIALOG1, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 162
        TOPMARGIN, 7
        BOTTOMMARGIN, 52
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // English (U.S.) resources
//////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Приложение STIME

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

Исходные тексты приложения STIME

Главный файл исходных текстов приложения STIME представлен в листинге 2.7.

Листинг 2.7. Файл rclock/stime/stime.c


// ==================================================
// Приложение STIME (работает вместе с приложением RTIME)
// Демонстрация использования сообщения WM_COPYDATA
// для передачи данных между процессами
//
// (С) Фролов А.В., 1996
// Email: frolov@glas.apc.org
// ==================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <time.h>
#include "resource.h"
#include "afxres.h"
#include "stime.h"

HINSTANCE hInst;
char szAppName[]  = "StimeApp";
char szAppTitle[] = "Time Sender";

// Имя приложения RTIME
char szServerAppName[]  = "RclockApp";

// Идентификатор главного окна приложения RTIME
HWND hWndServer;

// Структура для передачи данных между процессами
// при помощи сообщения WM_COPYDATA
COPYDATASTRUCT cd;

// Буферы для передаваемых данных
char szBuf[80];
char szTerminated[] = "<Terminated<";

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  // Сохраняем идентификатор приложения
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    // Если было, выдвигаем окно приложения на
    // передний план
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Ищем окно серверного приложения RCLOCK и сохраняем
  // его идентификатор
  hWndServer = FindWindow(szServerAppName, NULL);
  if(hWndServer == NULL)
  {
    // Если окно серверного приложения не найдено,
    // выводим сообщение об ошибке и завершаем работу
    // приложения
    MessageBox(NULL, "Server RCLOCK not found", szAppName,
      MB_ICONEXCLAMATION | MB_OK); 
    return FALSE;
  }
  
  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), 
    IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), 
    IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
     NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);
    
  // Отображаем окно и запускаем цикл 
  // обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_TIMER,   WndProc_OnTimer);
    HANDLE_MSG(hWnd, WM_CREATE,  WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct)
{
  // Создаем таймер с периодом работы 1 сек
  SetTimer(hWnd, CLOCK_TIMER, 1000, NULL);
  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnTimer
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnTimer(HWND hWnd, UINT id)
{
  time_t t;
  struct tm *ltime;

  // Определяем текущее время
  time(&t);
  ltime = localtime(&t);

  // Формируем текстовую строку времени
  wsprintf(szBuf, "%02d:%02d:%02d",
    ltime->tm_hour, ltime->tm_min,ltime->tm_sec);

  // Записываем адрес и размер строки в структуру
  // типа COPYDATASTRUCT
  cd.lpData = szBuf;
  cd.cbData = strlen(szBuf) + 1;

  // Посылаем сообщение серверному приложению RCLOCK
  SendMessage(hWndServer, WM_COPYDATA, 
    (WPARAM)hWnd, (LPARAM)&cd);
  return 0;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  // Перед завершением работы приложения передаем
  // серверу строку <Terminated>
  cd.lpData = szTerminated;
  cd.cbData = strlen(szTerminated) + 1;

  SendMessage(hWndServer, WM_COPYDATA, 
    (WPARAM)hWnd, (LPARAM)&cd);

  // Удаляем таймер
  KillTimer(hWnd, CLOCK_TIMER);

  PostQuitMessage(0);
  return 0L;
}
Определения и глобальные переменные

Помимо всего прочего в области глобальных переменных определен массив szServerAppName, в котором хранится имя приложения RCLOCK. Это имя будет использовано в функции WinMain для поиска главного окна серверного приложения.

Идентификатор главного окна найденного приложения RCLOCK хранится в глобальной переменной hWndServer. Этот идентификатор используется для посылки сообщения WM_COPYDATA функцией SendMessage.

В области глобальных переменных определена структура cd типа COPYDATASTRUCT, которая совместно с глобальным буфером szBuf используется для передачи текстовой строки в приложение RCLOCK.

Кроме того, определен буфер szTerminated, в котором находится строка символов Terminated. Эта строка передается в приложение RCLOCK перед завершением работы приложения STIME.

Функция WinMain

После поиска своей собственной копии приложение STIME ищет окно серверного приложения RCLOCK:


hWndServer = FindWindow(szServerAppName, NULL);

Если это приложение не найдено, выдается сообщение об ошибке, вслед за чем работа приложения STIME завершается.

В случае успешного поиска идентификатор найденного окна приложения RCLOCK записывается в глобальную переменную hWndServer. Вслед за этим выполняется процедура регистрации класса главного окна приложения STIME, создается главное окно приложения и запускается обычный цикл обработки сообщений.

Функция WndProc

Функция WndProc обрабатывает сообщения WM_CREATE, WM_DESTROY и WM_TIMER, вызывая для этого функции WndProc_OnCreate, WndProc_OnDestroy и WndProc_OnTimer, соответственно.

Функция WndProc_OnCreate

При создании главного окна приложения STIME обработчик сообщения WM_CREATE создает таймер с периодом работы 1 сек, вызывая для этого функцию SetTimer:


SetTimer(hWnd, CLOCK_TIMER, 1000, NULL);

Созданный таймер будет иметь идентификатор CLOCK_TIMER.

Функция WndProc_OnTimer

Функция WndProc_OnTimer получает управление примерно один раз в секунду, обрабатывая сообщения WM_TIMER, поступающие от таймера с идентификатором CLOCK_TIMER.

Прежде всего эта функция формирует строку символов с текущим временем в формате “ЧЧ:ММ:СС”, вызывая библиотечные фукнции системы разработки time и localtime:


time(&t);
ltime = localtime(&t);
wsprintf(szBuf, "%02d:%02d:%02d",
 ltime->tm_hour, ltime->tm_min,ltime->tm_sec);
 

Затем выполняется инициализация полей структуры cd типа COPYDATASTRUCT. В процессе инициализации мы записываем адрес буфера, содержащего строку символов, в поле lpData, а размер этого буфера (с учетом двоичного нуля, закрывающего строку) - в поле cbData:


cd.lpData = szBuf;
cd.cbData = strlen(szBuf) + 1;

Поле dwData не используется.

Заметим, что хотя серверное приложение RCLOCK при копировании полученных данных не использует поле cbData (так как мы передаем строку символов, закрытую двоичным нулем), при подготовке сообщения WM_COPYDATA к отправке необходимо заполнить оба поля: и lpData, и cbData.

Посылка сообщения WM_COPYDATA выполняется очень просто:


SendMessage(hWndServer, WM_COPYDATA, 
  (WPARAM)hWnd, (LPARAM)&cd);

Сообщение посылается в окно с идентификатором hWndServer, который был определен в функции WinMain. В качестве параметра wParam вместе с этим сообщением необходимо передать идентификатор окна посылающего приложения, то есть идентификатор окна приложения STIME. Через параметр lParam передается адрес заполненной структуры cd типа COPYDATASTRUCT.

Функция WndProc_OnDestroy

Перед завершением своей работы приложение STIME посылает приложению RCLOCK строку <Terminated>, которая будет отображена в окне приложения RCLOCK. Для посылки используется только что описанная нами методика:


cd.lpData = szTerminated;
cd.cbData = strlen(szTerminated) + 1;
SendMessage(hWndServer, WM_COPYDATA, 
    (WPARAM)hWnd, (LPARAM)&cd);

Далее функция WndProc_OnDestroy удаляет таймер и завершает цикл обработки сообщений, вызывая для этого функцию PostQuitMessage.

Файл stime.h

В файле stime.h (листинг 2.8) определен идентификатор таймера CLOCK_TIMER, а также прототипы функций.

Листинг 2.8. Файл rclock/stime/stime.h


#define CLOCK_TIMER 100

LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void WndProc_OnDestroy(HWND hWnd);
BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct);
void WndProc_OnPaint(HWND hWnd);
void WndProc_OnTimer(HWND hWnd, UINT id);
Файл resource.h

Файл resource.h (листинг 2.9) создается автоматически и содержит определения для файла описания ресурсов приложения STIME.

Листинг 2.9. Файл rclock/stime/resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by STIME.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        106
#define _APS_NEXT_COMMAND_VALUE         40006
#define _APS_NEXT_CONTROL_VALUE         1010
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
Файл stime.rc

В файле stime.rc (листинг 2.10) определены ресурсы приложения STIME.

Листинг 2.10. Файл rclock/stime/stime.rc


//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


//////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure 
// application icon
// remains consistent on all systems.
IDI_APPICON             ICON    DISCARDABLE     "STIME.ICO"
IDI_APPICONSM           ICON    DISCARDABLE     "STIMESM.ICO"

//////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE 
BEGIN
    IDD_DIALOG1, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 162
        TOPMARGIN, 7
        BOTTOMMARGIN, 52
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // English (U.S.) resources
//////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//

//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
[Назад] [Содеожание] [Дальше]