有时候给程序添加新功能时,原程序结构可能要面临很大的改变,这时候更倾向于使用类似插件的形式去处理新功能。
具体为新建一个窗体界面项目去处理新功能,并生成exe文件。在主项目需要的时候执行该exe文件,并进行通信,使用SendMessage进行进程间通信。
Process pro = new Process();
string arguments = string.Format("{0} {1}", "传入的参数1", "传入的参数2"); //可不传,也可传入多个参数,每个参数之间用空格隔开
ProcessStartInfo proStartInfo = new ProcessStartInfo(@"E:\yuanyuxin\测试代码\嵌入4gexe\bin\Debug\嵌入4gexe.exe", arguments);//exe的路径
//proStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pro.StartInfo = proStartInfo;
pro.Start();//运行exe
主程序关闭的时候,exe也对应关闭。
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(!pro.HasExited)
pro?.Kill();//结束进程
}
或者如果不是全局变量,可以通过名称查找
Process[] pro = Process.GetProcesses();//获取已开启的所有进程
//遍历所有查找到的进程
for (int i = 0; i < pro.Length; i++)
{
//判断此进程是否是要查找的进程
if (pro[i].ProcessName == "exe名称")
{
if(!pro.HasExited)
pro?.Kill();//结束进程
}
}
,修改Main方法,如果不需要传参数则不用修改:
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (args.Length < 2)
{
args = new string[2] { "参数1", "参数2" };//这里为了独自运行exe文件时默认值
}
Application.Run(new Form_show(args[0], args[1]));//args[0]就是传入的参数1,args[1]就是传入的参数2
}
在主程序需要发送消息的地方添加如下代码:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
const int WM_COPYDATA = 0x004A;//当一个应用程序传递数据给另一个应用程序时发送此消息
public struct COPYDATASTRUCT
{
public IntPtr dwData;//用户定义数据
public int cbData;//数据大小
//public string devId;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData; //指向数据的指针
}
需要和上面步骤(1)在同一个类中:
data="要发送的消息";
byte[] sarr = System.Text.Encoding.Default.GetBytes(data);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)Convert.ToInt16(1);//可以是任意值
cds.cbData = len + 1;//指定lpData内存区域的字节数
cds.lpData = data;//发送给目标窗口所在进程的数据
//cds.devId = devid;
SendMessage((int)pro.MainWindowHandle, WM_COPYDATA, 0, ref cds);//发送消息
一般来说,会把要传递的消息放在lpData 中,如果有两个数据要发送,可以用逗号或者其他字符连接,然后接收的地方去处理,还有另一种方式是用dwData保存存储地址,在接收的地方去解析,这样的方式要注意可能不是同一个指向地址,可能取不到,需要进行处理,保存在统一的地址,一般不复杂的数据格式不建议这样做。
另外需要说明的是,如果exe被隐藏,上面的句柄是无法发送成功的,需要用FindWindow函数获取句柄
var hanl = FindWindow(null, "嵌入4gexe");//第一个填类名,第二个填窗口的标题
SendMessage(hanl, WM_COPYDATA, 0, ref cds);
protected override void DefWndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
COPYDATASTRUCT cds = new COPYDATASTRUCT();
Type t = cds.GetType();
cds = (COPYDATASTRUCT)m.GetLParam(t);//得到主程序发送的数据
//以下为逻辑处理
string strResult = cds.lpData;
string strType = cds.dwData.ToString();
switch (strType)
{
case "1":
MessageBox.Show("接收到数据:"+strResult );
break;
default:
break;
}
break;
default:
base.DefWndProc(ref m);
break;
}
}
同样的,exe添加2(1)的代码,同样的给主程序发送消息
data="要发送的消息";
byte[] sarr = System.Text.Encoding.Default.GetBytes(data);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)Convert.ToInt16(1);//可以是任意值
cds.cbData = len + 1;//指定lpData内存区域的字节数
cds.lpData = data;//发送给目标窗口所在进程的数据
var pro = Process.GetProcesses();//查找所有的进程
foreach (Process p in pro)
{
if (p.ProcessName == "WinForm嵌入EXE程序"|| p.Handle== HWnd)//进程名为主程序名,或者主程序传入消息的时候就保存主程序的句柄
{
richTextBox1.AppendText("发送消息的名称和句柄:" + p.ProcessName.ToString()+","+HWnd + "\n");
SendMessage((int)p.MainWindowHandle, WM_COPYDATA, 0, ref cds);
}
}
同2(3)的步骤所示,主程序也添加DefWndProc函数来接收消息。
这里注意的是因为主程序逻辑比较复杂,有时候给exe发送消息的时候是用的子界面或者其他的地方,要注意此时exe接收到的句柄一般也是主程序主界面的句柄,那返回的消息,也是传递到主界面DefWndProc中,在子界面是无法接收到DefWndProc消息的。一般的处理方法是在主程序接收到消息后,再传递给子界面。
消息大全汇总:https://blog.csdn.net/Yyuanyuxin/article/details/106767561