Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

лекции / Shchupak_Yu._Win32_API_Razrabotka_prilozheniy_dlya_Windows

.pdf
Скачиваний:
0
Добавлен:
11.02.2026
Размер:
13.15 Mб
Скачать

Обмен данными между процессами

471

 

 

Физическая память и страничный файл

Так как на современных компьютерах оперативная память RAM имеет размеры, по крайней мере, на порядок меньше 4 Гбайт, то система имитирует задеклариро" ванную память за счет дискового пространства. При этом на диске создается стра ничный файл (page file), который вместе с физической памятью RAM образует виртуальную память, доступную всем процессам. Другое название страничного файла — файл подкачки (swap file).

Процессор сам управляет отображением виртуальных адресов из машинных команд в эквивалентные физические адреса в ходе выполнения команды. Осуще" ствляя это отображение, процессор оперирует страницами памяти размером 4 Кбайта. Этот же размер страниц используется Windows для управления вирту" альной памятью.

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

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

Данные отсутствуют в оперативной памяти, но размещены где"то в странич" ном файле. Попытка доступа к данным генерирует прерывание, называемое Page Fault (Ошибка страницы). Тогда система начинает искать свободную стра" ницу в оперативной памяти. Если такой страницы нет, то система вынуждена освободить одну из занятых страниц. Если занятая страница не модифициро" валась, она просто освобождается. В ином случае она сначала копируется из оперативной памяти в страничный файл. После этого система отыскивает в страничном файле запрошенный блок данных, загружает этот блок на сво" бодную страницу оперативной памяти и, наконец, проецирует виртуальный адрес данных на соответствующий адрес в физической памяти.

Иногда из"за программной ошибки или сбоя в аппаратной части требуемая страница отсутствует и в оперативной памяти, и в страничном файле. Тогда система генерирует ошибку Invalid Page Fault, и работающее приложение за" крывается.

Архитектура интерфейсов (API) управления памятью

Диспетчер виртуальной памяти (Virtual Memory Manager — VMM) является со" ставной частью ядра операционной системы. Он отображает виртуальные адреса на физические, используя механизм подкачки страниц памяти (page swapping). Кроме того, он предоставляет прикладным программам различные интерфейсы (API) для работы с виртуальной памятью:

Virtual Memory API — набор функций, позволяющих приложению работать с виртуальным адресным пространством. Например, функции VirtualAlloc и VirtualFree позволяют процессу получать страницы из памяти или возвращать их системе.

Memory Mapped File API — набор функций, позволяющих работать с файлами, проецируемыми в память. Это новый механизм, предоставляемый Windows для работы с файлами и взаимодействия процессов.

472

Глава 9. Многозадачность

 

 

Heap Memory API — набор функций, позволяющих работать с динамически рас" пределяемыми областями памяти (кучами).

Файлы данных, проецируемые в память

Проецирование файла данных в адресное пространство процесса предоставляет разработчику мощный механизм работы с файлами. Спроецировав файл на адрес" ное пространство процесса, программа получает возможность работать с ним, как с массивом. Это очень удобно при манипуляциях с большими потоками данных.

Для проецирования файла в память необходимо выполнить три операции:

1.Создать объект ядра «файл», идентифицирующий дисковый файл, который вы хотите использовать как проецируемый в память (функция CreateFile).

2.Создать объект ядра «проекция файла» при помощи функции CreateFileMapping. При этом используется дескриптор файла, возвращенный функцией CreateFile.

3.Указать отображение объекта «проекция файла» или его части на адресное про" странство процесса (функция MapViewOfFile).

Закончив работу с проецируемым в память файлом, тоже следует выполнить три операции:

1.Отменить отображение на адресное пространство процесса объекта «проек" ция файла» (функция UnmapViewOfFile).

2.Закрыть объект ядра «проекция файла».

3.Закрыть объект ядра «файл».

Описанную технологию можно проиллюстрировать следующим фрагментом кода:

HANDLE hFile, hFileMapping; PVOID pArray;

hFile = CreateFile("File Name", ... ); hFileMapping = CreateFileMapping(hFile, ... ); CloseHandle(hFile) ;

pArray = MapViewOfFile(hFileMapping, ... ); CloseHandle(hFileMapping) ;

//

/* Работаем с файлом, как с массивом pArray */

//

UnmapViewOfFile(pArray);

В этом примере «закрывающие» операции выполняются сразу после исполь" зования соответствующего дескриптора объекта. Это уменьшает вероятность утеч" ки ресурсов.

Использование проекции файла для реализации разделяемой памяти

Самый низкоуровневый механизм совместного использования данных в одной системе — проецирование файла в память. На нем так или иначе базируются все другие механизмы разделения данных. Поэтому, если вы хотите получить макси" мальное быстродействие с минимумом издержек, лучше всего применять именно проецирование.

Обмен данными между процессами

473

 

 

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

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

Этот способ даже проще стандартного, рассмотренного в предыдущем разделе. Во"первых, не нужно вызывать функцию CreateFile. Вы просто вызываете функ" цию CreateFileMapping и передаете значение INVALID_HANDLE_VALUE (или константу –1) в параметре hFile. Но при вызове функции CreateFileMapping следует передать в последнем ее параметре C"строку, содержащую имя этого объекта. Тогда другие процессы, если им понадобится доступ к разделяемой памяти, смогут вызвать функ" цию OpenFileMapping и передать ей то же самое имя.

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

Когда работа с объектом «проекция файла» завершена, процесс должен выз" вать функцию CloseHandle. Как только все дескрипторы объекта будут закрыты, система освободит память, переданную из страничного файла.

Модель «клиент-сервер»

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

Клиентом называется объект, запрашивающий доступ к службе или ресурсу. Сервер — это объект, выполняющий некую службу или обладающий ресурсом.

Клиент и сервер могут работать на одной и той же машине, используя локаль" ные механизмы коммуникации, или на разных машинах, применяя для связи се" тевые средства.

Поведение клиента и сервера асимметрично. Процесс"сервер инициализиру" ется и переходит в состояние ожидания запросов от возможных клиентов. Как правило, процесс"клиент запускается в интерактивном режиме и посылает зап" росы серверу. Сервер исполняет полученный запрос, причем это может подразу" мевать диалог с клиентом, а может — и нет. Затем сервер вновь переходит в состо" яние ожидания запросов от других клиентов.

В рассматриваемом ниже примере клиентом и сервером являются приложе" ния ClientApp и ServerApp. Рисунок 9.3 показывает, как осуществляется взаимосвязь между этими приложениями через локальные механизмы коммуникации.

474

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Глава 9. Многозадачность

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 9.3. Взаимодействия клиента и сервера

Хотя на рисунке изображен только один клиент, на самом деле их может быть несколько. Максимально возможное число клиентов определяется «мощностью» или пропускной способностью сервера.

Сценарий взаимодействия клиента и сервера предусматривает начальную фазу,

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

вразделяемую память. Синхронизация взаимодействия клиента и сервера осу" ществляется через объекты ядра «события». Таким образом, в рабочей фазе по" вторяются действия, помеченные на рис. 9.3 номерами 3—6.

Обмен данными

с помощью сообщения WM_COPYDATA

Системное сообщение WM_COPYDATA является, вероятно, самым простым методом обмена данными между процессами.

Чтобы послать любое сообщение при помощи функции SendMessage, необхо" димо знать дескриптор окна, которому оно посылается. Обычно этот дескриптор получают вызовом функции FindWindow, например:

hwndServer = FindWindow(NULL, "ServerApp");

С помощью этой инструкции клиентское приложение узнает дескриптор ос" новного окна серверного приложения, имеющего заголовок ServerApp.

Прежде чем посылать сообщение WM_COPYDATA, вы должны определить струк" турную переменную типа COPYDATASTRUCT. Эта структура объявлена следующим образом:

typedef struct tagCOPYDATASTRUCT { ULONG_PTR dwData;

DWORD cbData; PVOID lpData;

} COPYDATASTRUCT, *PCOPYDATASTRUCT;

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

Обмен данными между процессами

475

 

 

Поле lpData может содержать указатель на данные, передаваемые приложению" получателю. Если lpData не равно NULL, то поле cbData должно содержать размер

вбайтах данных, на которые указывает lpData.

Взависимости от потребностей приложения вы может использовать оба ука" занных поля (dwData и lpData) или только одно из них.

Когда вы посылаете сообщение WM_COPYDATA с помощью функции SendMessage, адрес переменной типа COPYDATASTRUCT должен передаваться в параметре lParam.

Например, следующие инструкции в клиентском приложении подготавлива" ют запрос в виде С"строки request и посылают его серверному приложению, кото" рое имеет заголовок окна ServerApp:

COPYDATASTRUCT cds; char request[80];

sprintf(request, "ClientApp"); cds.lpData = &request; cds.cbData = strlen(request);

hwndServer = FindWindow(NULL, "ServerApp"); SendMessage(hwndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);

Программа, которой адресуется сообщение WM_COPYDATA, должна рассматривать данные, на которые указывает lpData, как данные только для чтения. Указатель lpData является корректным только в процессе обработки сообщения WM_COPYDATA. Если принимающее приложение намерено использовать полученные данные в дальней" шей своей работе, то оно должно скопировать их в локальный буфер.

Данные, размещенные по адресу lpData, не должны содержать никаких указа" телей, поскольку в адресном пространстве принимающего приложения эти ука" затели будут интерпретироваться неверно.

Но пора от теории перейти к практике и показать, наконец, нечто работающее.

Приложение ServerApp

Исходный код приложения ServerApp приведен в листинге 9.4.

Листинг 9.4. Проект ServerApp

//////////////////////////////////////////////////////////////////////

// ServerApp.cpp #include <windows.h> #include <stdio.h> #include "KWnd.h"

#define MAX_STR_SIZE 80

#define MAX_N 10 // максимальное количество клиентов int N = 0; // текущее количество клиентов

HANDLE hEvtRecToServ[MAX_N]; // массив событий "рабочий запрос от клиента" char eventName[MAX_N][MAX_STR_SIZE + 8]; // массив имен событий

HANDLE hEvtServIsExist; // событие "Сервер уже запущен"

HANDLE hEvtServIsFree; // событие "Сервер свободен"

HANDLE hEvtServIsDone; // событие "Сервер выполнил рабочий запрос"

HANDLE hFileMap = NULL; // объект "проекция файла"

struct ThreadManager { HWND hwndParent;

};

476

Глава 9. Многозадачность

 

ThreadManager tm; // структура для связи с потоком

DWORD WINAPI ThreadFunc(LPVOID);

 

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM,

LPARAM);

//==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{

MSG msg;

KWnd mainWnd("ServerApp", hInstance, nCmdShow, WndProc, NULL, 100, 100, 450, 100);

while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg);

}

return (msg.wParam);

}

//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

HDC hDC; PAINTSTRUCT ps;

COPYDATASTRUCT* pCds; COPYDATASTRUCT cds;

static char request[MAX_STR_SIZE]; char suffix[8];

HWND hwndClient; static HANDLE hThread; HWND* pHwnd = &hWnd;

int i;

switch (uMsg)

{

case WM_CREATE:

hEvtServIsExist = OpenEvent(EVENT_ALL_ACCESS, FALSE, "Server"); if (hEvtServIsExist) {

MessageBox(hWnd, "Сервер уже выполняется. 2 экземпляр запрещен.", "ServerApp", MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL);

PostQuitMessage(0);

}

else

hEvtServIsExist = CreateEvent(NULL, FALSE, FALSE, "Server"); break;

case WM_COPYDATA: // прием запроса от клиента на связь с сервером

if (N == MAX_N) {

MessageBox(hWnd, "Сервер перегружен. В доступе отказано", "ServerApp", MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL);

break;

}

// Извлечение сообщения

pCds = (COPYDATASTRUCT*)lParam;

продолжение

Обмен данными между процессами

477

 

 

Листинг 9.4 (продолжение)

strncpy(request, (char*)pCds->lpData, pCds->cbData); strcpy(eventName[N], request);

sprintf(suffix, "_%d", N); strcat(eventName[N], suffix);

// Создание события hEvtRecToServ[N] - "запрос N-го клиента" hEvtRecToServ[N] = CreateEvent(NULL, FALSE, FALSE, eventName[N]);

if (!N) {

//Инициализация массива hEvtRecToServ for (i = 0; i < MAX_N; ++i)

hEvtRecToServ[i] = hEvtRecToServ[0];

//Создание файла, проецируемого в память

hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,

PAGE_READWRITE, 0, 4 * 1024, "SharedData");

// Создание событий "Сервер свободен" и "Сервер выполнил рабочий запрос" hEvtServIsFree = CreateEvent(NULL, FALSE, TRUE, "ServerIsFree"); hEvtServIsDone = CreateEvent(NULL, FALSE,FALSE, "ServerIsDone");

// Запуск потока для обработки запросов клиентов tm.hwndParent = hWnd;

hThread = CreateThread(NULL, 0, ThreadFunc, &tm, 0, NULL);

}

// Отправка обратного сообщения клиенту hwndClient = FindWindow(NULL, request); cds.dwData = N;

cds.lpData = &eventName[N]; cds.cbData = strlen(eventName[N]);

SendMessage(hwndClient, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);

if (N < MAX_N) N++; InvalidateRect(hWnd, NULL, TRUE); break;

case WM_PAINT:

hDC = BeginPaint(hWnd, &ps); if (N) {

char text[40];

sprintf(text, "Количество обслуживаемых клиентов: %d", N); TextOut(hDC, 10, 20, text, strlen(text));

}

EndPaint(hWnd, &ps); break;

case WM_DESTROY: CloseHandle(hFileMap); PostQuitMessage(0); break;

default:

return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

return 0;

}

//==================================================================== DWORD WINAPI ThreadFunc(LPVOID lpv)

{

478

Глава 9. Многозадачность

 

 

ThreadManager* pTm =

(ThreadManager*)lpv;

HWND hParent = pTm->hwndParent; static PVOID pView;

char suffix[40]; int k;

char text[100]; DWORD dw;

while (1) {

// Ожидание "рабочего запроса" от клиента

dw = WaitForMultipleObjects(MAX_N, hEvtRecToServ, FALSE, INFINITE); switch (dw) {

case WAIT_FAILED:

MessageBox(hParent, "Ошибка вызова WaitForMultipleObjects", "ServerApp", MB_OK);

return 0;

default:

k= dw - WAIT_OBJECT_0; // индекс клиента

//Отображаем проекцию файла на адресное пространство процесса pView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);

//Извлекаем содержание запроса в буфер text

strcpy(text, (PTSTR)pView);

// Добавляем к нему "суффикс", содержащий имя клиента

sprintf(suffix, " - %s\0",

eventName[k]);

strcat(text, suffix);

 

//Помещаем сформированную запись обратно в проекцию файла strcpy((PTSTR)pView, text);

UnmapViewOfFile(pView);

//Освобождаем событие hEvtServIsDone SetEvent(hEvtServIsDone);

break;

}

}

return 0;

}

//////////////////////////////////////////////////////////////////////

После старта сервер ждет первого запроса на связь от возможного клиента. Этот запрос поступает в виде сообщения WM_COPYDATA. В коде программы ClientApp, который приводится ниже, можно увидеть, что клиент посылает в этом сообще" нии C"строку, содержащую его имя (которое совпадает с заголовком главного окна приложения). Сервер извлекает эту строку request и добавляет к ней суффикс, содержащий символ подчеркивания и номер запроса N. Модифицированная строка запоминается в элементе массива eventName[N]. Затем сервер создает объект"со" бытие с дескриптором hEvtRecToServ[N] и именем eventName[N].

Если поступивший запрос является первым и N имеет нулевое значение, то сервер выполняет ряд инициализирующих действий:

Все элементы массива hEvtRecToServ заполняются значениями дескриптора hEvtRecToServ[0]. Если этого не сделать, то функция WaitForMultipleObjects, вы" зываемая позже для массива событий hEvtRecToServ, работать не будет.

Создается объект «проекция файла» с дескриптором hFileMapи именем SharedData. При этом размер файла, задаваемый четвертым и пятым параметрами функции CreateFileMapping, равен 4 Кбайт. Эта величина должна быть кратна размеру

Обмен данными между процессами

479

 

 

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

Создаются объекты"события hEvtServIsFree и hEvtServIsDone.

Запускается вторичный поток с входной функцией ThreadFunc. Этот поток пред" назначен для обработки «рабочих запросов» клиентов.

Завершая обработку принятого сообщения WM_COPYDATA, сервер отправляет имя eventName[N] обратно клиенту, используя для этого все то же сообщение WM_COPYDATA. Это сообщение является ответом"уведомлением для клиента (см. рис. 9.3).

Алгоритм функции потока ThreadFunc очень прост. Как только сервер обнару" живает освобождение какого"либо события из массива hEvtRecToServ, он опреде" ляет индекс клиента и приступает к обработке «рабочего запроса». Обратите вни" мание на то, что после записи «ответа» в разделяемую память поток вызывает функцию SetEvent, чтобы перевести событие hEvtServIsDone в свободное состоя" ние. Как мы увидим ниже, клиентское приложение будет ждать этого события, чтобы извлечь ответ сервера из разделяемой памяти.

Приложение ClientApp

Исходный код приложения ClientApp приведен в листинге 9.5.

Создав приложение, добавьте к нему ресурс меню с идентификатором IDR_MENU1. Меню должно содержать один пункт с именем Link to server и иденти" фикатором IDM_LINK.

Листинг 9.5. Проект ClientApp

//////////////////////////////////////////////////////////////////////

// ClientApp.cpp #include <windows.h> #include <stdio.h> #include "resource.h" #include "KWnd.h"

#define MAX_LEN_REQUEST 80 #define MAX_LEN_EVTNAME 88 #define TIMER_ID 1

#define TIMER_PERIOD 1000

HANDLE hEvtRecToServ; // событие "Запрос к серверу от клиента"

HANDLE hEvtServIsFree; // событие "Сервер свободен"

HANDLE hEvtServIsDone; // событие "Сервер выполнил запрос"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //==================================================================== int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

{

MSG msg;

KWnd mainWnd("ClientApp", hInstance, nCmdShow, WndProc, MAKEINTRESOURCE(IDR_MENU1), 100, 100, 450, 150);

while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg);

}

480

Глава 9. Многозадачность

 

 

return (msg.wParam);

}

//==================================================================== LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

HDC hDC; PAINTSTRUCT ps;

COPYDATASTRUCT* pCds; COPYDATASTRUCT cds;

static char request[MAX_LEN_REQUEST]; static char eventName[MAX_LEN_EVTNAME]; static char text[100];

static char msgSended[256]; static char msgReceived[256];

HWND hwndServer;

static BOOL isLinkToServer;

static BOOL bServerIsDone = FALSE;

static HANDLE hFileMap; // объект "проекция файла" static PVOID pView;

DWORD dw0, dw1; static int count = 0;

switch (uMsg)

{

case WM_CREATE: isLinkToServer = FALSE; msgSended[0] = 0; msgReceived[0] = 0; break;

case WM_COMMAND:

switch (LOWORD(wParam))

{

case IDM_LINK: // Отправка запроса на связь с сервером sprintf(request, "ClientApp");

cds.lpData = &request; cds.cbData = strlen(request);

hwndServer = FindWindow(NULL, "ServerApp"); if (!hwndServer) {

MessageBox(hWnd, "Ошибка связи!", "ClientApp", MB_OK); break;

}

SendMessage(hwndServer, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);

break;

}

break;

case WM_COPYDATA: // Получение ответа от сервера

 

 

pCds =

(COPYDATASTRUCT*)lParam;

 

 

// Èìÿ

разделяемого объекта-события

 

 

strncpy(eventName, (char*)pCds->lpData, pCds->cbData);

продолжение