前情提要
什么是内存拷贝
内存拷贝的用处之一,简单来说,就是通过Windows的宏SendMessage来进行本机台上不同软件的通讯;不同于串口,网口等通讯,需要同硬件设备进行交互(虚拟串口,虚拟网口除外,不过这样更麻烦);
而内存拷贝可以让进入windows进程消息循环的两个应用进行信息交互;如何交互呢,举个例子,当你在键盘上按下一个按键,此时windows接收到一个消息,又或者你用鼠标在屏幕上点击了一下,其中,区分这些的标志是一个叫做消息号的东西 部分消息号如下所示。
我们一般在进行内存拷贝通讯的时候,使用的消息号是0400或004A,不固定,需要同要进行通讯的双方软件定义一致,双方使用的消息号要相同,才能进行数据收发。
有了消息号,还需要一个发送目标来指定接收方,内存拷贝使用的是窗口的标题,即软件窗口的标题,在windows进程中对该软件的命名也是根据窗口标题来的,有了这个,就可以准确无误的数据交互了。
代码
说到不同语言的内存拷贝通讯,是有点差别的,下面我们就来说说具体细节 相同语言(通讯双方软件的开发语言)的内存拷贝代码上无任何不同,这里不做赘述
传输数据的格式,一般可以只穿字符串,也可以传一个结构体,但是无论是那种数据,都要以指针的形式传递 下面我们使用结构体,关于结构体的定义,不同平台之间的定义相去无几 如c++ 只要保证对方是以这个格式接收的即可,一般习惯以lpData来表示发送数据的其实指针,cbData来表示数据的大小size,dwData来表示数据发送窗口的句柄号
对于不同编码的字符,即Ansi和unicode,这两个对于到时候数据的解析是不太一样的,即发送端是以ansi格式发送的,你不能以unicode格式解析(否则乱码),同时,这两个编码格式的数据长度度量不太一样,ansi : unicode = 1:2;具体见上篇博客
C++与C#通讯
C++这边的接收和发送
LRESULT CTestCommunicationDlg::OnCopyData(WPARAM wParam,LPARAM lParam)
{
PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam;
m_strDataRcv = (LPCWSTR)(pcds->lpData);
GetDlgItem(IDC_EDIT2)->SetWindowTextW(m_strDataRcv);
return 1L;
}
BOOL CTestCommunicationDlg::SendDataByWmCopyData(CString strText)
{
CWnd *pDisplayWnd = CWnd::FindWindow(NULL, _T("内存拷贝通讯"));
if(pDisplayWnd == NULL){
AfxMessageBox(_T("没有找到内存拷贝通讯窗口"));
return FALSE;
}
COPYDATASTRUCT cds;
memset(&cds, 0, sizeof(cds));
cds.dwData = 0;
cds.cbData = (strText.GetLength() ) * 2 + 1;
USES_CONVERSION;
cds.lpData = (LPVOID)(LPCWSTR)(strText);
::SendMessage(pDisplayWnd->GetSafeHwnd(), WM_COPYDATA,
(WPARAM)0, (LPARAM)&cds);
return TRUE;
}
c#这边的接收和发送 定义windows接口api和数据结构声明
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern IntPtr SendMessage(int hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
接收和发送
private void buttonSend_Click(object sender, EventArgs e)
{
WINDOW_HANDLER = FindWindow(null, textBoxTargetWindName.Text);
IntPtr p = Marshal.StringToHGlobalUni(textBoxSend.Text);
int size=textBoxSend.Text.Length*2+1;
COPYDATASTRUCT cOPYDATASTRUCT = new COPYDATASTRUCT();
cOPYDATASTRUCT.cbData = size;
cOPYDATASTRUCT.dwData = (IntPtr)0;
cOPYDATASTRUCT.lpData = p;
IntPtr iPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cOPYDATASTRUCT));
Marshal.StructureToPtr(cOPYDATASTRUCT, iPtr, true);
COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(iPtr, typeof(COPYDATASTRUCT));
SendMessage(WINDOW_HANDLER, 0x004A, IntPtr.Zero, iPtr);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_USER://消息号0400
str = Marshal.PtrToStringAnsi(m.WParam);
textBoxRecv.Text += "接收:" + str + "\r\n";
break;
case 0x004A://消息号
COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure((IntPtr)m.LParam, typeof(COPYDATASTRUCT));
str = Marshal.PtrToStringUni(cds.lpData); ;
textBoxRecv.Text += "接收:" + str.Replace("\0", "") + "\r\n";
break;
default: break;
}
base.WndProc(ref m);
}
C#和QT之间的通讯
C#同上 QT代码如下
void MainWindow::on_btMemSend_clicked()
{
m_MemTargetWin = ::FindWindow(NULL, ui->tbMemTargerWinName->text().toStdWString().c_str());
WId wid = this->winId();
if (NULL != m_MemTargetWin)
{
std::thread th([=](){
QString command = ui->tbMemSend->toPlainText();
COPYDATASTRUCT data;
data.dwData = 0;
data.cbData = command.length()*2+1;
data.lpData = (LPVOID)command.toStdWString().data();
::SendMessage(m_MemTargetWin, WM_COPYDATA, (WPARAM)wid, (LPARAM)&data);
});
th.detach();
}
}
bool MainWindow::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
if (eventType == "windows_generic_MSG")
{
MSG* msg = reinterpret_cast<MSG*>(message);
if (msg->message == WM_APPCOMMAND || msg->message == WM_COPYDATA)
{
COPYDATASTRUCT *data ;
data = reinterpret_cast<COPYDATASTRUCT*>(msg->lParam);
QString str=QString::fromStdWString((LPCWSTR)(data->lpData));
ui->tbMemRecv->append(str);
}
}
return QWidget::nativeEvent(eventType, message, result);
}
QT和C++之间的通讯
见上一篇博客
本文的所有上述功能皆已在windows下进行测试过,可放心食用
如果上文中叙述的观点及内容有与您理解出入的地方,欢迎交流
|