Windows для профессионалов

       

Передача данных через сообщения


Здесь мы обсудим, как система обеспечивает передачу данных между процессами с помощью сообщений В некоторых оконных сообщениях параметр 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

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

  • На создание копии данных в адресном пространстве другого процесса неиз бежно уходит какое-то время Значит, пока SendMessage не вернст управление, нельзя допускать изменения содержимого общей проекции файла каким-либо другим потоком

  • Сообщение WM_COPYDATA позволяет 16-разрядным приложениям взаимо действовать с 32-разрядными (и наоборот), как впрочем и 32-разрядным — с 64-разрядными (и наоборот). Это удивительно просюй способ общения меж ду новыми и старыми приложениями. К тому же, WM_COPYDATA полностью поддерживается как в Windows 2000, так и в Windows 98 Но, если Вы все еще пишете 16-разрядные Windows-приложсния, учтите, что сообщение WM_COPY


  • 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


    Содержание раздела