控件进程化,32位程序做大内存消耗时存在内存不够用时,特此记录解决方案,控件进程化,模块进程化

控件进程化,32位程序做大内存消耗时存在内存不够用时,特此记录解决方案,控件进程化,模块进程化...

文章尾部提供完整demo下载

前端时间公司做了图片视频分析处理的项目,图片支持4k,6k甚至勉强支持8k;因为处理的方式很多,各模块之前不能切换后销毁,用户需要来回切换的,针对这个问题,每个模块都会加载图片,图片过大后程序内存告急,且程序是32位的,即使开启了大内存(32位开启大内存)的支持依然不是很好的解决,内存占用过高很容易被360告警,所以项目上最后的解决方案就是进程化各个模块!


通俗点讲就是,主进程启动后,会再启动子进程,子进程上会加载局部模块,附属于主进程,主进程退出后,子进程也终止,且子进程悬浮在主进程界面之上,所以例如打开遮罩时,需要发送命令到子进程,子进程自己再开启遮罩才能解决。


公司封装了几个dll,服务于快捷进程化开发,为此我精简成2个库Cal.Wpf.Part、Cal.Wpf.PartHost

Cal.Wpf.Part  辅助代码

Cal.Wpf.PartHost 子进程依赖的启动程序exe,如果有多个,该程序会生成多个按进程分

先大致看下效果:

一个主进程,2个子进程

下面是所有相关的dll

实现原理:主程序启动后,传递窗口句柄并启动子程序,设置子程序的所有者句柄为主程序的,让其绑定在一起;程序之间利用TcpChannel实现数据通讯,特别啰嗦一句就是子程序永远悬浮于主程序之上。


下面开始针对demo进行使用说明:

1、引用Cal.Wpf.Part.dll、Cal.Wpf.PartHost.exe

2、针对自定义的控件创建**Host.xaml(承载)、**Patrt.cs(辅助类)

3、**Host.xaml 引用 Cal.Wpf.Part.dll,并显示占位

 1 <UserControl x:Class="ProcessDemo.CustomUserControlHostControl"
 2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 5              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 6              xmlns:part="clr-namespace:Cal.Wpf.Part;assembly=Cal.Wpf.Part"
 7              xmlns:local="clr-namespace:ProcessDemo"
 8              mc:Ignorable="d" 
 9              d:DesignHeight="450" d:DesignWidth="800">
10     <Grid>
11         <part:PartHostControl Name="partHost" />
12     </Grid>
13 </UserControl>
 1 using Cal.Wpf.Part.Remoting;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 using System.Windows;
 8 using System.Windows.Controls;
 9 using System.Windows.Data;
10 using System.Windows.Documents;
11 using System.Windows.Input;
12 using System.Windows.Interop;
13 using System.Windows.Media;
14 using System.Windows.Media.Imaging;
15 using System.Windows.Navigation;
16 using System.Windows.Shapes;
17 
18 namespace ProcessDemo
19 {
20     /// <summary>
21     /// UserControlHostControl.xaml 的交互逻辑
22     /// </summary>
23     public partial class CustomUserControlHostControl : UserControl, IDisposable
24     {
25         public CustomUserControlHostControl()
26         {
27             InitializeComponent();
28         }
29 
30         ~CustomUserControlHostControl()
31         {
32             Dispose();
33         }
34 
35         public void Dispose()
36         {
37             partHost.Dispose();
38         }
39 
40         /// <summary>
41         /// 收到进程的事件推送时
42         /// </summary>
43         public event MessageEventHandler ReceiveCommand
44         {
45             add
46             {
47                 partHost.ReceiveCommand += value;
48             }
49             remove
50             {
51                 partHost.ReceiveCommand -= value;
52             }
53         }
54 
55         /// <summary>
56         /// 设置在独立进程中运行的控件
57         /// </summary>      
58         /// <param name="parentWindowHandler">父窗口句柄</param>
59         public void InitPartControl(IntPtr parentWindowHandler)
60         {
61             partHost.InitPartControl(typeof(CustomUserControlPart), parentWindowHandler);
62         }
63 
64         /// <summary>
65         /// 发送消息
66         /// </summary>
67         /// <param name="msg"></param>
68         public void SendMsg(string msg)
69         {
70             partHost.SendCommand(new ProcessMessage()
71             {
72                 Body = msg,
73                 Command = 1
74             });
75         }
76     }
77 }

4、 **Patrt.cs实现接口IPart

 1 using Cal.Wpf.Part;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 using System.Windows;
 8 
 9 namespace ProcessDemo
10 {
11     public class CustomUserControlPart : IPart
12     {
13         /// <summary>
14         /// 逻辑主控件
15         /// </summary>
16         private CustomUserControl control;
17 
18         /// <summary>
19         /// 向主进程发消息时
20         /// </summary>
21         public event Cal.Wpf.Part.Remoting.MessageEventHandler SendCommand;
22 
23         /// <summary>
24         /// 进程调用,需要支援释放了
25         /// </summary>
26         /// <returns></returns>
27         public void Dispose()
28         {
29             if (control != null)
30             {
31                 control.SendMsg -= Control_SendMsg;
32             }
33         }
34 
35         /// <summary>
36         /// 收到主进程消息时
37         /// </summary>
38         /// <param name="message"></param>
39         public void ExecuteCommand(Cal.Wpf.Part.Remoting.ProcessMessage message)
40         {
41             if (message == null || message.Body == null) return;
42 
43             control.SetMsg(message.Body.ToString());
44         }
45 
46         /// <summary>
47         /// 进程调用,获取当前插件的展示界面
48         /// </summary>
49         /// <returns></returns>
50         public FrameworkElement GetPartControl()
51         {
52             if (control == null)
53             {
54                 control = new CustomUserControl();
55                 control.SendMsg += Control_SendMsg;
56             }
57             return control;
58         }
59 
60         /// <summary>
61         /// CustomUserControl 需要先外部进程发送消息时
62         /// </summary>
63         /// <param name="sender"></param>
64         /// <param name="e"></param>
65         private void Control_SendMsg(object sender, Tuple<int, string> e)
66         {
67             SendCommand?.Invoke(new Cal.Wpf.Part.Remoting.ProcessMessage()
68             {
69                 Command = e.Item1,
70                 Body = e.Item2
71             });
72         }
73 
74         public void Initialize()
75         {
76             //加载CustomUserControl 之前需要做的事情,可以在这里写,比如加载皮肤dll等...
77         }
78     }
79 }

5、真实的自定义控件再原有的逻辑里,需要处理和主程序的消息通讯,因为当前控件是渲染在子控件上的

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Diagnostics;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 using System.Windows;
 9 using System.Windows.Controls;
10 using System.Windows.Data;
11 using System.Windows.Documents;
12 using System.Windows.Input;
13 using System.Windows.Media;
14 using System.Windows.Media.Imaging;
15 using System.Windows.Navigation;
16 using System.Windows.Shapes;
17 
18 namespace ProcessDemo
19 {
20     /// <summary>
21     /// CustomUserControl.xaml 的交互逻辑
22     /// </summary>
23     public partial class CustomUserControl : UserControl
24     {
25         public CustomUserControl()
26         {
27             InitializeComponent();
28 
29             if (!DesignerProperties.GetIsInDesignMode(this))
30             {
31                 pid.Text = $"当前进程id:{Process.GetCurrentProcess().Id}";
32             }
33         }
34 
35         /// <summary>
36         /// 发送消息时
37         /// </summary>
38         public event EventHandler<Tuple<int, string>> SendMsg;
39 
40         /// <summary>
41         /// 发送消息到主进程
42         /// </summary>
43         /// <param name="sender"></param>
44         /// <param name="e"></param>
45         private void Button_Click(object sender, RoutedEventArgs e)
46         {
47             SendMsg?.Invoke(this, new Tuple<int, string>(1, msg.Text));
48         }
49 
50         /// <summary>
51         /// 收到主进程的消息时
52         /// </summary>
53         /// <param name="msg"></param>
54         public void SetMsg(string msg)
55         {
56             Dispatcher.Invoke(() =>
57             {
58                 msgstr.Text = $"收到主进程发来的消息:{msg}";
59             });
60         }
61     }
62 }

6、最后就是在主进程上,把**Host占位,且在恰当的时候初始化子进程即可

 1 <Window x:Class="ProcessDemo.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:ProcessDemo"
 7         xmlns:uc="clr-namespace:ProcessDemo.UC"
 8         WindowStartupLocation="CenterScreen"
 9         mc:Ignorable="d"
10         Title="控件进程化Demo" Height="300" Width="400">
11     <Grid>
12         <DockPanel Margin="5">
13             <Grid DockPanel.Dock="Top">
14                 <StackPanel>
15                     <TextBlock Text="我是主进程上的" />
16                     <TextBlock x:Name="pid"/>
17                     <StackPanel Orientation="Horizontal">
18                         <TextBox x:Name="msg" Width="100" VerticalAlignment="Center" />
19                         <Button Content="发消息给子进程" Margin="2" Click="Button_Click" />
20                     </StackPanel>
21                 </StackPanel>
22             </Grid>
23             <Grid DockPanel.Dock="Top">
24                 <TextBlock Text="收到子进程消息时:..." x:Name="msgstr" />
25             </Grid>
26             <Grid DockPanel.Dock="Bottom">
27                 <!--子进程2-->
28                 <uc:UserControlHost Margin="10" x:Name="hostcontrol2" Height="50" />
29             </Grid>
30             <Grid>
31                 <!-- 子进程存放位置 -->
32                 <local:CustomUserControlHostControl x:Name="hostcontrol" Margin="10" />
33             </Grid>
34         </DockPanel>
35     </Grid>
36 </Window>
 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Diagnostics;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 using System.Windows;
 9 using System.Windows.Controls;
10 using System.Windows.Data;
11 using System.Windows.Documents;
12 using System.Windows.Input;
13 using System.Windows.Interop;
14 using System.Windows.Media;
15 using System.Windows.Media.Imaging;
16 using System.Windows.Navigation;
17 using System.Windows.Shapes;
18 
19 namespace ProcessDemo
20 {
21     /// <summary>
22     /// MainWindow.xaml 的交互逻辑
23     /// </summary>
24     public partial class MainWindow : Window
25     {
26         public MainWindow()
27         {
28             InitializeComponent();
29 
30             if (!DesignerProperties.GetIsInDesignMode(this))
31             {
32                 pid.Text = $"当前进程id:{Process.GetCurrentProcess().Id}";
33                 hostcontrol.ReceiveCommand += Hostcontrol_ReceiveCommand;
34                 Loaded += MainWindow_Loaded;
35             }
36         }
37 
38         /// <summary>
39         /// 窗口加载后
40         /// </summary>
41         /// <param name="sender"></param>
42         /// <param name="e"></param>
43         private void MainWindow_Loaded(object sender, RoutedEventArgs e)
44         {
45             //获取当前句柄
46             var parentWnd = Window.GetWindow(this);
47             var winHelper = new WindowInteropHelper(parentWnd);
48             IntPtr parentWindowHandler = winHelper.EnsureHandle();
49 
50             //初始化子进程
51             hostcontrol.InitPartControl(parentWindowHandler);
52             hostcontrol2.InitPartControl(parentWindowHandler);
53         }
54 
55         /// <summary>
56         /// 当收到子进程发来消息时
57         /// </summary>
58         /// <param name="message"></param>
59         /// <returns></returns>
60         private Cal.Wpf.Part.Remoting.ProcessMessage Hostcontrol_ReceiveCommand(Cal.Wpf.Part.Remoting.ProcessMessage message)
61         {
62             if (message == null || message.Body == null) return null;
63 
64             msgstr.Dispatcher.Invoke(() =>
65             {
66                 msgstr.Text = $"子进程发来消息:{message.Body.ToString()}";
67             });
68             return null;
69         }
70 
71         /// <summary>
72         /// 发给子进程
73         /// </summary>
74         /// <param name="sender"></param>
75         /// <param name="e"></param>
76         private void Button_Click(object sender, RoutedEventArgs e)
77         {
78             hostcontrol.SendMsg(msg.Text);
79         }
80     }
81 }

完整demo,有需要的可以移步下载,下面是解决方案摘要

 

原文地址:https://www.cnblogs.com/xuling-297769461/p/12767369.html