一、进程间嵌套
有时候项目的架构因为一些特殊原因需要采用多进程方式,我们需要把模块(单独进程)的嵌入到我们的主进程界面中。
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;
}
}
参考资料:
https://www.cnblogs.com/DoNetCShap/archive/2012/06/26/2564462.html