控件進程化,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,有需要的可以移步下載,下面是解決方案摘要

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章