Посылка сообщения в другое приложение

336
28 октября 2017, 17:20

Имеются 2 независимых приложения с почти идентичным кодом. В одном из них генерируем сообщение (нажав на мышку) и вместе с сообщением высылается указатель на структуру.

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

Что уже пробовал:

//Высылаемая структура, есть глобальный объект структуры - ball
struct ballXY
{
    bool state;
    int azaz;
};
ballXY ball = {false, 666};
// а так же message для нашей структуры
const UINT ball_msg = RegisterWindowMessage((LPCWSTR)("ball"));

Функция CALLBACK выглядит как-то так:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == ball_msg)
    {
        ball = *(ballXY*)lParam;
        return 0;
    }
    switch (message)
    {
    case WM_PAINT:
        //рисуем тут
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_LBUTTONDOWN:
        if (!ball.state)
            ball.state = true;
        break;
    case WM_TIMER:
        if (ball.state) {
            SendMessage(HWND_BROADCAST, ball_msg, NULL, (LPARAM)&ball);
            ball.state = false;
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
    return 0;
}

Итог: запустив 2 приложения, я нажал на правую кнопку мыши в одном - закрашился эксплорер, часть программ закрылась.

Answer 1

Для межпроцессорной пересылки данных Windows API предоставляет два простых способа:

  1. Именованные каналы. Хоть я и советовал этот способ в комментариях к вопросу, но он сопряжён с одной неустранимой трудностью. Кто закроет канал, если передающее приложение рухнет? Так как канал общий, то никто — будет утечка глобальных ресурсов.

  2. Посылка оконного сообщения WM_COPYDATA. Это сообщение — одно из немногих, которые обрабатываются оконной подсистемой на особых правах, а потому указанный в нём блок байт будет заботливо скопирован в адресное пространство получателя.

    При таком подходе код будет выглядеть так:

    • отправление сообщения:

      struct BallXY
      {
          bool state;
          int azaz;
      };
      // =================
      void sendBallState(BallXY& ball, HWND hParent)
      {
          // Перебираем все окна-получатели и рассылаем им обновлённые данные
          HWND hDestWnd = NULL;
          bool continueIteration = true;
          while(continueIteration)
          {
              hDestWnd = FindWindowEx(
                  /* hwndParent      */ NULL,
                  /* hwndChildAfter  */ hDestWnd,
                  /* lpszClass       */ TEXT("оконный_класс_получателя"),
                  /* lpszWindow      */ NULL);
              if(hDestinationWnd)
              {
                  COPYDATASTRUCT cds;
                  cds.dwData = 0; // Любой идентификатор; передаётся получателю как есть
                  cds.cbData = sizeof(ball);
                  cds.lpData = &ball;
                  SendMessage(hDestWnd, WM_COPYDATA, (WPARAM)hParent, (LPARAM)&cds);
              }
              else
                  continueIteration = false;
          }
      }
    • оконная процедура:

      static void onCopyData(HWND hWnd, HWND hSourceWnd, PCOPYDATASTRUCT cds)
      {
          UNREFERENCED_PARAMETER(hWnd);
          // Проверяем по размеру данных, что они хотя бы примерно похожи на наши
          if(cds->cbData == sizeof(BallXY))
          {
              // Проверяем, что отправитель — подконтрольное нам окно
              TCHAR sourceClassName[256]; // Длина гарантирована MSDN
              GetClassName(hSourceWnd, sourceClassName, sizeof(sourceClassName));
              if(lstrcmp(sourceClassName, TEXT("оконный_класс_отправителя")) == 0)
              {
                  // Безопасно преобразуем типы, избегая неопределённого поведения
                  memcpy(&ball, cds->lpData, sizeof(ball));
              }
      }
      static void onTimerTick(HWND hWnd, UINT uTimerId)
      {
          UNREFERENCED_PARAMETER(uTimerId);
          if(ball.state)
          {
              sendBallState(ball, hWnd);
              ball.state = false;
          }
      }
      LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
      {
          // Забываем про развесистые switch-case и пользуемся вместо этого удобными
          // message crackers из windowsx.h
          switch(message)
          {
              HANDLE_MSG(hWnd, WM_COPYDATA, onCopyData);
              HANDLE_MSG(hWnd, WM_TIMER, onTimerTick);
              // ...
          default:
              return DefWindowProc(hWnd, message, wParam, lParam);
          }
      }
READ ALSO
Умножение длинных чисел

Умножение длинных чисел

Интересует не столько решение, сколько причина по которой программа выводит неверный результат

289
Безопасное сравнение указателей

Безопасное сравнение указателей

Безопасен ли данный код

302
Как заставить Visual Studio работать со свойствами в C++?

Как заставить Visual Studio работать со свойствами в C++?

Visual Studio 2010 на попытку создать свойство у класса пишет:

313
no matching function for call to 'MainWindow::connect();

no matching function for call to 'MainWindow::connect();

Есть кнопка в ob_panelui - on_pB_Switch_clicked и элемент в mainwindow

239