winform进程间嵌套、拖拽、消息通讯

一、进程间嵌套

  有时候项目的架构因为一些特殊原因需要采用多进程方式,我们需要把模块(单独进程)的嵌入到我们的主进程界面中。

  1.首先我们利用ProcessStartInfo类将外部程序启动

            ProcessStartInfo info = new ProcessStartInfo(FileNameStr); //FileNameStr 外部程序完整的路径+程序名称
            info.UseShellExecute = true;
            info.WindowStyle = ProcessWindowStyle.Minimized;
            m_AppProcess = System.Diagnostics.Process.Start(info);
            m_AppProcess.WaitForInputIdle();

            Application.Idle += appIdleEvent;

   2.我们在 Idle事件内确认程序是否启动,并进行嵌入操作

          if (this.m_AppProcess == null || this.m_AppProcess.HasExited)  //子程序是否启动
        {
            this.m_AppProcess = null;
            Application.Idle -= appIdleEvent;
            return;
        }
        while (m_AppProcess.MainWindowHandle == IntPtr.Zero) //等待子程序界面加载
        {
            Thread.Sleep(100);
            m_AppProcess.Refresh();
        }
        Application.Idle -= appIdleEvent;

        EmbedProcess(m_AppProcess, ParentCon);  //进行嵌套,以及界面处理

3.在EmbedProcess进行嵌套,以及界面处理

        if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
        try
        {      
            SetParent(app.MainWindowHandle, control.Handle);  //嵌套
        }
        catch (Exception)
        { }
        try
        {              
            SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
            SendMessage(app.MainWindowHandle, WM_SETTEXT, IntPtr.Zero, strGUID);
        }
        catch (Exception)
        { }
        try
        {
            // Move the window to overlay it on this window  
            MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
        }
        catch (Exception)

        { }

      到此嵌套的核心部分全部说完,网上还有种方式是把子程序作为资源文件嵌入,没做具体尝试,有兴趣的可以去看看


二、进程间拖拽

           进程内的拖拽网上资料很多,其实进程间的拖拽也是差不多,只是需要注意控件是不可以跨进程拖拽的,因为拖过去就是DataFormats.Serializable类型,msdn上Serializable的说明里也明确讲了,该类型跨进程是不可逆的。基本类型DataFormats内标识的我们都可以拖拽,另外自定义对象我们可以通过序列化的方式转化位string进行变向解决,下面主要说下对象的跨进程拖拽

 1.在主程序界面内允许拖拽,并编写代码

 首先将要拖拽的控件设置AllowDrop属性为true;接着在MouseDown事件内进行数据序列化

                      MesContext ms = new MesContext();  //MesContext 自定义的类

                        ms.Ss = CurrentNode.Name;

                        MesWithList mslist = new MesWithList(); //含有列表的自定义类
                        mslist.Ss = "拖拽列表测试";
                        mslist.MesList = new List<MesContext>();
                        mslist.MesList.Add(ms);

                        MesContext ms1 = new MesContext();
                        ms1.Ss = CurrentNode.Name+"111";
                        mslist.MesList.Add(ms1);

                         //进行序列化,此处借助了一个三方库转换了json,你也可以转化问你别的格式

                        string node = JsonConvert.SerializeObject(mslist);    
                      treeView1.DoDragDrop(node, DragDropEffects.Copy);    //拖拽操作

2.在目标程序内允许拖拽,并编写相应代码

        1)首先将要目标控件设置AllowDrop属性为true;

       2)在DragEnter事件内 

           添加代码  

        e.Effect = DragDropEffects.All;

          或者进行类型判断     

         if ( e.Data.GetDataPresent(DataFormats.Text))
         {
             e.Effect = DragDropEffects.Copy;
         }

       3)DragDrop事件内对接受的内容处理       

           if (e.Data.GetDataPresent(DataFormats.Text))
            {
                string mes =e.Data.GetData(DataFormats.Text).ToString();  //拖过来的内容
               MesWithList node = JsonConvert.DeserializeObject<MesWithList>(mes);  //反序列化
                label2.Text = node.Ss;  //内容应用

            }      


3.进程间消息通讯

    进程间的消息通讯主要尝试使用了WM_COPYDATA这个消息传送的,当然这种方式网上很多此处简单整理

    1)定义自定义结构体

      public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int cbData;
        [MarshalAs(UnmanagedType.LPStr)]

        public string lpData;

     //可扩展

    } 

    2).发送进程设置 

      先找到已打开的子程序句柄,然后使用SendMessage进行消息发送,注意不能用PostMessage,原因可取msdn上看

       const int WM_COPYDATA = 0x004A;

        IntPtr hWnd = IntPtr.Zero;
        if (m_AppProcess == null || m_AppProcess.MainWindowHandle == IntPtr.Zero)
        {
            hWnd = FindWindow(null, @"目标程序名");
            if (hWnd == IntPtr.Zero)
            {
                MessageBox.Show("未找到  目标程序名!");
            }
        }
        else
        {
            hWnd = m_AppProcess.MainWindowHandle;
        }
        byte[] sarr = System.Text.Encoding.Default.GetBytes(msg);
        int len = sarr.Length;
        COPYDATASTRUCT cds=new COPYDATASTRUCT();
        cds.dwData = IntPtr.Zero;//(IntPtr)Convert.ToInt16(msg);//可以是任意值  
        cds.cbData = len + 1;//指定lpData内存区域的字节数  
        cds.lpData = msg;//发送给目标窗口所在进程的数据         
        IntPtr Pmsg=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(COPYDATASTRUCT)));
        Marshal.StructureToPtr(cds,Pmsg,true);
        int ret = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, Pmsg);
        Marshal.FreeHGlobal(Pmsg);
        if (ret == 0) return true;

        return false;

    3).在目标程序设置接受代码,识别消息类型后,再进行处理

        const int WM_COPYDATA = 0x004A;
        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.dwData.ToString() + ":" +cds.lpData;
                    MessageBox.Show(strResult);
                    break;
                default:
                    base.DefWndProc(ref m);
                    break;
            }
        }

    本文完整Demo

参考资料:

  https://docs.microsoft.com/zh-cn/dotnet/framework/winforms/advanced/how-to-perform-drag-and-drop-operations-between-applications

               https://www.cnblogs.com/DoNetCShap/archive/2012/06/26/2564462.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值