C#--主程序启动EXE插件,并使用SendMessage进行进程间通信

有时候给程序添加新功能时,原程序结构可能要面临很大的改变,这时候更倾向于使用类似插件的形式去处理新功能。
具体为新建一个窗体界面项目去处理新功能,并生成exe文件。在主项目需要的时候执行该exe文件,并进行通信,使用SendMessage进行进程间通信。

目录

  • 1.主程序启动带参数的EXE
    • (1)在合适的地方启动exe文件:
    • (2)在exe项目文件下找到Program.cs文件:
    • (3)在exe项目文件下找到窗体文件
  • 2.主程序向exe发消息:
    • (1)主程序引用所需的dll文件
    • (2)主程序发送信息给exe
    • (3)exe接收到主程序的消息
  • 3.exe向主程序返回消息:
    • (1)exe项目添加发送代码
    • (2)主程序添加接收消息函数

具体通信如下:
C#--主程序启动EXE插件,并使用SendMessage进行进程间通信_第1张图片

1.主程序启动带参数的EXE

(1)在合适的地方启动exe文件:

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();//结束进程
    }
}

(2)在exe项目文件下找到Program.cs文件:

,修改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
}

(3)在exe项目文件下找到窗体文件

对传入的参数去处理
C#--主程序启动EXE插件,并使用SendMessage进行进程间通信_第2张图片

2.主程序向exe发消息:

(1)主程序引用所需的dll文件

在主程序需要发送消息的地方添加如下代码:

[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; //指向数据的指针 
}

(2)主程序发送信息给exe

需要和上面步骤(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);

(3)exe接收到主程序的消息


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;

    }
}

3.exe向主程序返回消息:

(1)exe项目添加发送代码

同样的,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)主程序添加接收消息函数

同2(3)的步骤所示,主程序也添加DefWndProc函数来接收消息。
这里注意的是因为主程序逻辑比较复杂,有时候给exe发送消息的时候是用的子界面或者其他的地方,要注意此时exe接收到的句柄一般也是主程序主界面的句柄,那返回的消息,也是传递到主界面DefWndProc中,在子界面是无法接收到DefWndProc消息的。一般的处理方法是在主程序接收到消息后,再传递给子界面。

消息大全汇总:https://blog.csdn.net/Yyuanyuxin/article/details/106767561

你可能感兴趣的:(c#,#,c#,非控件常用操作)