侧边栏壁纸
博主头像
陌上花 博主等级

回首万事皆休

  • 累计撰写 70 篇文章
  • 累计创建 11 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

CSharp之两个进程间通讯

种向日葵的人
2024-08-30 / 0 评论 / 0 点赞 / 32 阅读 / 0 字

Why

  • 因为最近有个任务是打算做一个有关于两个程序之间的通讯,使用的是EXE,之前稍有一些了解但是没有写过,因为之前要么用的是MQTT,要么用的是别的消息队列,Windows之间直接通讯的还是比较少,所以这一次有兴趣做一下,顺便记录。此处是基于WinFormMFC的通讯

How

  • 其实两者都是使用的WindowProc函数。

MFC

  • 以下会使用到json11的类库,不做过多解释。
  • 主页面
    mfc主页面.png
  • 主要代码
void CMyMFCDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData();
	//std::cout << IDC_EDIT1.getwin.GetWindowText() << std::endl; // IDC_EDIT1
	CString str;
	GetDlgItemText(IDC_EDIT1, str);
	std::cout << str << std::endl;


	//LRESULT copyData;  //copyDataResult has value returned by other app 
	//CWnd* pOtherWnd = CWnd::FindWindow(NULL, _T("mfctest2"));
	CString strData;
	GetDlgItemText(IDC_EDIT1, strData);

	std::string datas = strData.GetString();
	json11::Json callBack = json11::Json::object{
		{"Interface", "g"},
		{"Status","0"},
		{"Datas", strData.GetString()}
	};

	std::string jsonStr = callBack.dump();


	ULONG_PTR dwData = 3;
	LRESULT result = SendToCAM(jsonStr, dwData);
	if (result == 0)
	{
		MessageBox(_T("发送消息成功!"), _T("Message"), MB_OK);
	}
	else if (result == 1)
	{
		MessageBox(_T("发送消息失败!"), _T("Message"), MB_OK);
	}
}

LRESULT CMyMFCDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: 在此添加专用代码和/或调用基类

	switch (message)
	{
	case WM_COPYDATA:
		// TODO: 在此添加消息处理程序代码
	{
		std::cout << "WM_COPYDATA" << std::endl;


		PCOPYDATASTRUCT pCDS = (PCOPYDATASTRUCT)lParam;

		int dwData = (int)pCDS->dwData;

		LPCTSTR data = LPCTSTR(pCDS->lpData);
		CString strData(data);
		std::cout << data << std::endl;

		if (strData.IsEmpty())
		{
			return 1;
		}
		std::string str = strData.GetString();
		std::string err;
		json11::Json obj = json11::Json::parse(str, err);
		if (!err.empty())
		{
			MessageBox(strData, _T("格式转换出错"), MB_OK);
		}
		std::string interfacesType = obj["Interface"].string_value();
		std::string status = obj["Status"].string_value();
		std::string datas = obj["Datas"].string_value();


		switch (interfacesType[0])
		{
		case 'a':
		{
			//此处表示联通
			CString s_handle = (datas.c_str());
			this->s_Hwnd = (HWND)atoi(s_handle.GetString());
			datas = _T("已经接收到联通数据");
		}
		break;
		case 'b':
		{
			//G代码文件路径
			std::string gcodePath = datas;
			CString gcodePathStr = _T("当前G代码文件路径");
			gcodePathStr.Append(gcodePath.c_str());
			MessageBox(gcodePathStr, _T("G代码文件路径"), MB_OK);
			datas = _T("已经接收到G代码文件路径");
			return 0;
		}
		break;
		case 'g':
		{
			//点信息

		}
		break;
		default:
			break;
		}


		json11::Json callBack = json11::Json::object{
			{"Interface", interfacesType},
			{"Status", status},
			{"Datas", datas}
		};

		//MessageBox(strData, _T("已经接收到信息"), MB_OK);

		SendToCAM(callBack.dump(), dwData);
		return 0;

	}
	break;
	default:
		return CDialogEx::WindowProc(message, wParam, lParam);
		break;
	}


}

LRESULT CMyMFCDlg::SendToCAM(std::string msg, ULONG_PTR dwData)
{
	if (this->s_Hwnd == nullptr)
	{
		return 1;
	}
	COPYDATASTRUCT cpd;   //上面提到的结构体
	cpd.dwData = dwData;
	cpd.cbData = msg.size() + sizeof(TCHAR);        //sizeof(wchar_t)指 \0 的长度。     
	cpd.lpData = (void*)(msg.c_str());
	LRESULT result = ::SendMessage(this->s_Hwnd, WM_COPYDATA, (WPARAM)AfxGetApp()->m_pMainWnd->GetSafeHwnd(), (LPARAM)&cpd);


	return result;
}
  • 可以看到的是一开始是CAM传递数据给MFC,同时把CAM的句柄传递给MFC这样可以省去查找句柄的时间。同时相互之间都是通过json的形式传递的。

WinForm

  • 在主页面重定义函数
/// <summary>
/// 重写虚函数   
/// </summary>
/// <param name="m"></param>
protected override void DefWndProc(ref Message m)
{
    if (m.Msg == WinMessageHelper.WM_COPYDATA)
    {
        WinMessageHelper.COPYDATASTRUCT copyDataStruct = (WinMessageHelper.COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(WinMessageHelper.COPYDATASTRUCT));
        string str = Marshal.PtrToStringAnsi(copyDataStruct.lpData);
        //表示联通MFC返回
        var winMsgData = JsonConvert.DeserializeObject<WinMsgData<string>>(str);
        switch (winMsgData.Interface)
        {
            case 'a':
                {
                    if (int.Parse(winMsgData.Status) == 0)
                    {
                        StatusControl?.AddMessage("联通", TurExType.message, winMsgData.Datas);
                    }
                    else
                    {
                        StatusControl?.AddMessage("联通", TurExType.error, winMsgData.Datas);
                    }
                }
                break;
            case 'b':
                {
                    //获取G代码路径
                    if (int.Parse(winMsgData.Status) == 0)
                    {
                        StatusControl?.AddMessage("G代码文件路径", TurExType.message, winMsgData.Datas);
                    }
                    else
                    {
                        StatusControl?.AddMessage("G代码文件路径", TurExType.error, winMsgData.Datas);
                    }
                }
                break;
            case 'g':
                {
                    //传递点信息
                    var pointDatas = winMsgData.Datas.Split(',');
                    StatusControl?.AddMessage("点信息", TurExType.message, $"已经接收到了点信息{pointDatas}");

                    if (pointDatas.Count() % 3 != 0 || pointDatas.Count() / 3 == 2)
                    {
                        StatusControl?.AddMessage("点信息", TurExType.error, "点信息格式错误");
                        winMsgData.Interface = 'g';
                        winMsgData.Status = "1";
                        winMsgData.Datas = "点信息格式错误";

                        WinMessageHelper.SendMessageToWindow<string>("MyMFC", winMsgData);
                        return;
                    }

                    //暂定输出的G代码的路径为当前工程下的position.cnc
                    var gCodePath = $@"position.cnc";

                    //发送G代码路径到MFC
                    winMsgData.Interface = 'b';
                    winMsgData.Status = "0";
                    winMsgData.Datas = $@"{gCodePath}";
                    


                    var isSend = WinMessageHelper.SendMessageToWindow<string>("MyMFC", winMsgData);
                    
                }
                break;
            default:
                break;
        }


    }
    else
        base.DefWndProc(ref m);
}
  • 里面存在一些自定义的类,例如消息显示的StatusControl可以忽略。以下是WinMessageHelper类的全部代码,照道理其实是固定的。
 public class WinMessageHelper
 {
     private static IntPtr s_hwnd { get; set; }
     private static IntPtr c_hwnd { get; set; }

     public struct COPYDATASTRUCT
     {
         public IntPtr dwData;
         public int cbData;
         public IntPtr lpData;
     }
     //使用COPYDATA进行跨进程通信
     public const int WM_COPYDATA = 0x004A;

     #region 阻塞线程 需要等待回复 数据安全

     [DllImport("User32.dll", EntryPoint = "SendMessage")]
     public static extern int SendMessage(
         int hWnd, // handle to destination window
         int Msg, // message
         int wParam, // first message parameter
         ref COPYDATASTRUCT lParam // second message parameter
     );
     #endregion
     #region 不阻塞线程 直接执行返回 数据不安全 可能在调用数据的时候就已经被释放了
     //异步消息发送API
     [DllImport("User32.dll", EntryPoint = "PostMessage")]
     public static extern int PostMessage(
         int hWnd,        // 信息发往的窗口的句柄
         int Msg,            // 消息ID
         int wParam,         // 参数1
         ref COPYDATASTRUCT lParam  // 参数2
     );
     #endregion

     [DllImport("User32.dll", EntryPoint = "FindWindow")]
     public static extern int FindWindow(string lpClassName, string lpWindowName);

     /// <summary>
     /// 发送消息到窗口
     /// </summary>
     /// <returns></returns>
     public static bool SendMessageToWindow<T>(string windowName, WinMsgData<T> winMsgData)
     {
         //if (c_hwnd == IntPtr.Zero)
         //{

         //}
         int hwnd = FindWindow(null, windowName);
         if (hwnd == 0)
         {

             return false;
         }
         c_hwnd = (IntPtr)hwnd;
         COPYDATASTRUCT cds;

         switch (winMsgData.Interface)
         {

             case 'a':
                 cds.dwData = (IntPtr)1;
                 break;
             case 'b':
                 cds.dwData = (IntPtr)2;
                 break;
             case 'g':
                 cds.dwData = (IntPtr)3;
                 break;
             default:
                 cds.dwData = (IntPtr)0;
                 break;
         }

         var message = JsonConvert.SerializeObject(winMsgData);

         cds.lpData = Marshal.StringToHGlobalAnsi(message);
         cds.cbData = Encoding.Default.GetBytes(message).Length + 1;

         var result = SendMessage((int)c_hwnd, WM_COPYDATA, 0, ref cds);
         Marshal.FreeHGlobal(cds.lpData);
         return result == 0;
     }
 }

 public class WinMsgData<T>
 {
     public char Interface { get; set; }

     public string Status { get; set; }
     public T Datas { get; set; } = default;

 }

Tip

  • 其实代码逻辑清楚还是很简单的,这个封装的结构体可以说是固定的,除了一些intIntptr的转换。
0
博主关闭了所有页面的评论