代码之家  ›  专栏  ›  技术社区  ›  andrewrk

在Qt应用程序中接收wm_CopyData消息

  •  2
  • andrewrk  · 技术社区  · 15 年前

    我正在使用仅限Windows的Qt应用程序,需要从Microsoft OneNote插件接收数据。这个插件是用C语言编写的,可以发送wm-copydata消息。如何在C++ QT应用程序中接收这些消息?

    我需要:

    • 可以指定一个窗口在调用registerClassEx时注册的“类名”,这样我就可以确保插件将wm-copydata消息发送到正确的窗口。
    • 有权访问消息ID以检查它是否是wm_CopyData和lparam,其中包含带有实际数据的copyDatastruct。这个信息是在wndproc中传递的,但是我找不到一个钩子,在那里我可以拦截这些消息。
    4 回复  |  直到 15 年前
        1
  •  6
  •   andrewrk    15 年前

    这一切都可以在qt内处理:

    1. 用一个类来扩展qwidget,该类将捕获wm_CopyData消息:

          class EventReceiverWindow : public QWidget
      {
          Q_OBJECT
      public:
          EventReceiverWindow();
      
      signals:
          void eventData(const QString & data);
      
      private:
          bool winEvent ( MSG * message, long * result );
      };
      
    2. 生成要设置为qwidget窗口标题的guid:

      EventReceiverWindow::EventReceiverWindow()
      {
          setWindowTitle("ProjectName-3F2504E0-4F89-11D3-9A0C-0305E82C3301::EventReceiver");
      }
      
    3. 重写winEvent以处理wm_CopyData结构,并在收到信号时发出信号:

      bool EventReceiverWindow::winEvent ( MSG * message, long * result )
      {
              if( message->message == WM_COPYDATA ) {
                  // extract the string from lParam
                  COPYDATASTRUCT * data = (COPYDATASTRUCT *) message->lParam;
      
                  emit eventData(QString::fromAscii((const char *)data->lpData, data->cbData));
      
                  // keep the event from qt
                  *result = 0;
                  return true;
              }
      
              // give the event to qt
              return false;
      }
      
    4. 在另一个类中,可以使用该类接收消息字符串:

      EventReceiverWindow * eventWindow = new EventReceiverWindow;
      QObject::connect(eventWindow, SIGNAL(eventData(const QString &)), this, SLOT(handleEventData(const QString &)));
      

      ……

      void OneNoteInterface::handleEventData(const QString &data)
      {
          qDebug() << "message from our secret agent: " << data;
      }
      
    5. 在发送消息的程序中, find the window 通过唯一的窗口标题。下面是C中的一个例子:

      private struct COPYDATASTRUCT
      {
          public IntPtr dwData;
          public int cbData;
          [MarshalAs(UnmanagedType.LPStr)]
          public string lpData;
      }
      
      private const int WM_COPYDATA = 0x4A;
      
      [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
      static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
      
      [DllImport("User32.dll", EntryPoint = "SendMessage")]
      private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
      
      private void sendMessageTo(IntPtr hWnd, String msg)
      {
          int wParam = 0;
          int result = 0;
      
          if (hWnd != IntPtr.Zero )
          {
              byte[] sarr = System.Text.Encoding.Default.GetBytes(msg);
              int len = sarr.Length;
              COPYDATASTRUCT cds;
              cds.dwData = IntPtr.Zero;
              cds.lpData = msg;
              cds.cbData = len + 1;
              result = SendMessage(hWnd, WM_COPYDATA, wParam, ref cds);
          }
      }
      

      然后你可以:

      IntPtr hwnd = FindWindowByCaption(IntPtr.zero, "ProjectName-3F2504E0-4F89-11D3-9A0C-0305E82C3301::EventReceiver");
      sendMessageTo(hwnd, "omg hai");
      
        2
  •  1
  •   OregonGhost    15 年前

    您还可以创建一个虚拟窗口,仅用于使用win32 api接收该消息。我猜你不能访问qt窗口的窗口过程,所以这应该是最简单的方法。

    能够 (我不会)还通过设置新的wndproc(使用 SetWindowLong(Ptr) ,可以使用 QWidget::winId() )在这个wndproc中,您只需处理特定的wm_CopyData,并将所有其他窗口消息传递给旧的wndproc。

        3
  •  0
  •   Idan K    15 年前

    要处理窗口接收的消息,请重写 QCoreApplication::winEventFilter .如果那不起作用,你可以看看 QAbstractEventDispatcher .

    对于您可以尝试使用的类名 QWidget::winId 以及Win32 API。我试着帮你找,但现在不行,也许试一下 GetClassName .

        4
  •  0
  •   andrewrk    15 年前

    你可以用 QWinHost 从qt解决方案创建一个虚拟窗口。跟随 the guide 将向您展示如何指定类名并检查消息的事件循环。