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