Why
- 因为最近有个任务是打算做一个有关于两个程序之间的通讯,使用的是
EXE
,之前稍有一些了解但是没有写过,因为之前要么用的是MQTT
,要么用的是别的消息队列,Windows
之间直接通讯的还是比较少,所以这一次有兴趣做一下,顺便记录。此处是基于WinForm
和MFC
的通讯
How
MFC
- 以下会使用到
json11
的类库,不做过多解释。
- 主页面

- 主要代码
void CMyMFCDlg::OnBnClickedButton1()
{
UpdateData();
CString str;
GetDlgItemText(IDC_EDIT1, str);
std::cout << str << std::endl;
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)
{
switch (message)
{
case WM_COPYDATA:
{
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':
{
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}
};
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);
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
的形式传递的。
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);
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':
{
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;
}
var gCodePath = $@"position.cnc";
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;
}
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 不阻塞线程 直接执行返回 数据不安全 可能在调用数据的时候就已经被释放了
[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);
public static bool SendMessageToWindow<T>(string windowName, WinMsgData<T> winMsgData)
{
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
- 其实代码逻辑清楚还是很简单的,这个封装的结构体可以说是固定的,除了一些
int
到Intptr
的转换。