Передача данных через сообщения
Здесь мы обсудим, как система обеспечивает передачу данных между процессами с помощью сообщений В некоторых оконных сообщениях параметр lParam задает адрес блока памяти. Например, сообщение WM_SETTEXT использует lParam как ука затель на строку (с нулевым символом в конце), содержащую новый текст для окна. Рассмотрим такой вызов:
SendMessage(FindWindow{NULL, "Calculator"), WM_SETTEXT, 0, (LPARAM) "A Test Caption" );
Вроде бы все достаточно безобидно определяется описатель окна Calculator и делается попытка изменить его заголовок на «A Test Caption». Но приглядимся к тому, что тут происходит
В lParam передается адрес строки (с новым заголовком), расположенной в адрес ном пространстве Вашего процесса. Получив это сообщение, оконная процедура программы Calculator берет lParam и пытается манипулировать чем-то, что, «по ее мнению", является указателем на строку с новым заголовком.
По адрес в lParam указывает на строку в адресном пространстве Вашего процес са, а не программы Calculator Вот Вам и долгожданная неприятность — нарушение доступа к памяти. Но если Вы все же выполните показанную ранее строку, все будет работать нормально. Что за наваждение5
А дело в том, что система отслеживает сообщения WM_SETTEXT и обрабатывает их не так, как большинство других сообщений. При вызове SendMessage внутренний код функции проверяет, не пытаетесь ли Вы послать сообщение WM_SETTEXT. Если это так, функция копирует строку из Вашего адресного пространства в проекцию файла и делает его доступным другому процессу. Затем сообщение посылается пото ку другого процесса. Когда поток-приемник готов к обработке WM_SETTEXT, он оп ределяет адрес общей проекции файла (содержащей копию строки) в адресном про странстве своего процесса Параметру lParam присваивается значение именно этого адреса, и WM_SETTEXT направляется нужной оконной процедуре. После обработки этого сообщения, проекция файла уничтожается Не слишком ли тут накручено, а?
К счастью, большинство сообщений не требует такой обработки — она осуществ ляется, только если сообщение посылается другому процессу. (Заметьте: описанная обработка выполняется и для любого сообщения, параметры wParam или lParam ко торого содержат указатель на какую-либо структуру данных )
А вот другой случай, когда от системы требуется особая обработка, — сообщение WM_GETTEXT. Допустим, Ваша программа содержит код:
char szBuf[200];
SendMessage(FindWindow(NULL, "Calculator"), WM_GETTEXT, Sizeof(szBuf), (LPARAM) szBuf);
WM_GETTEXT требует, чтобы оконная процедура программы Calculator помести ла в буфер, на который указывает szBuf, заголовок своего окна. Когда Вы посылаете это сообщение окну другого процесса, система должна на самом деле послать два сообщения. Сначала — WM_GETTEXTLENGTH Оконная процедура возвращает число символов в строке заголовка окна. Это значение система использует при создании проекции файла, разделяемой двумя процессами,
Создав проекцию файла, система посылает для cro заполнения сообщение WM_GET TEXT Затем переключается обратно на процесс, первым вызвавший функцию SendMes-
sage, копирует данные из общей проекции файла в буфер, на который указывает szBuf, и заставляет SendMessage вернуть управление
Что ж, все хороши, пока Вы посылаете сообщения, известные системе А если мы определим собственное сообщение (WM_USER + x), собираясь отправить его окну другого процесса? Система не «поймет», что нам нужна общая проекция файла для корректировки указателей при их пересылке. Но выход есть — что сообщение WM_COPYDATA:
COPYDATASTRUCT cds;
SendMessage(hwndReceiver, WM_COPYDATA, (WPARAM) hwndSender, (LPARAM) &cds);
COPYDATASTRUCT — структура, определенная в WinUser.h:
typedef struct tagCOPYDATASTRUCT
{
ULONG_PTR dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;
Чтобы переслать данные окну другого процесса, нужно сначала инициализиро вать эту структуру. Элемент dwData резервируется для использования в Вашей про грамме В него разрешается записывать любое значение. Например, передавая в дру гой процесс данные, в этом элементе можно указывать тип данных
Элемент cbData задает число байтов, пересылаемых в другой процесс, a lpData указывает на первый байт данных Адрес, идентифицируемый элементом lpData, на ходится, конечно же, в адресном пространстве отправителя
Увидев, что Вы посылаете сообщение WM_COPYDATA, SendMessage создает проек цию файла размером cbData байтов и копирует данные из адресного пространства Вашей программы в эту проекцию. Затем отправляет сообщение окну-приемнику При обработке этого сообщения принимающей оконной процедурой параметр lParam указывает на структуру COPYDATASTRUCT, которая находится в адресном простран стве процесса-приемника Элемент lpData этой структуры указывает на проекцию файла в адресном пространстве процесса-приемника.
Вам следует помнить о трех важных вещах, связанных с сообщением WM_COPY DATA
DATA и структура COPYDATASTRUCT в Microsoft Visual С++ версии 1 52 не оп ределены Вам придется добавить их определения самостояельно.
// включите этот код в свою 16-разрядную Windows-программу
#define WM_COPYDATA 0x004A
typedef VOID FAR* PVOID;
typedef struct taqCOPYDATASTRUCT
{
DWORD dwData;
DWORD cbDdta;
PVOID lpData;
} COPYDATASTRUCT, FAR* PCOPYDATASTRUCT;
Сообщение WM_COPYDATA — мощный инструмент, позволяющий разработчикам экономить массу времени при решении проблем связи между процессами И очень жаль, что применяется оно нечасто Насколько полезно это сообщение, иллюстриру ет программа-пример LastMsgBoxInfo из главы 22