C#的winform如何嵌套另一个exe程序

发布时间 2023-08-18 10:37:05作者: China Soft
这篇文章主要介绍了C#的winform如何嵌套另一个exe程序问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
 

C#winform嵌套另一个exe程序

一共有二种方法,也不知道作者从哪里复制来的,先感谢原作者。

首先建立一个程序,加2个按钮,为了区分,界面修改成红色。

第一种

1.建立一个主程序,加一个panel1,为了区分背景是绿色

2.代码调用

3.所有代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 嵌入外部exe
        /// </summary>
        public class EmbeddedExeTool
        {
            [DllImport("User32.dll", EntryPoint = "SetParent")]
            private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
            [DllImport("user32.dll", EntryPoint = "ShowWindow")]
            private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
            [DllImport("user32.dll", SetLastError = true)]
            private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
            [DllImport("user32.dll")]
            private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
            [DllImport("user32.dll")]
            private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
            IntPtr WindowHandle = IntPtr.Zero;
            private const int WS_THICKFRAME = 262144;
            private const int WS_BORDER = 8388608;
            private const int GWL_STYLE = -16;
            private const int WS_CAPTION = 0xC00000;
            private Process proApp = null;
            private Control ContainerControl = null;
            private const int WS_VISIBLE = 0x10000000;
            [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
            private static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
            [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
            private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);
            private IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
            {
                if (IntPtr.Size == 4)
                {
                    return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
                }
                return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
            }
            /// <summary>
            /// 加载外部exe程序到程序容器中
            /// </summary>
            /// <param name="control">要显示exe的容器控件</param>
            /// <param name="exepath">exe的完整绝对路径</param>
            public void LoadEXE(Control control, string exepath)
            {
                ContainerControl = control;
                control.SizeChanged += Control_SizeChanged;
                ProcessStartInfo info = new ProcessStartInfo(exepath);
                info.WindowStyle = ProcessWindowStyle.Minimized;
                info.UseShellExecute = false;
                info.CreateNoWindow = false;
                proApp = Process.Start(info);
                Application.Idle += Application_Idle;
                EmbedProcess(proApp, control);
            }
            /// <summary>
            /// 加载外部exe程序到程序容器中
            /// </summary>
            /// <param name="form">要显示exe的窗体</param>
            /// <param name="exepath">exe的完整绝对路径</param>
            public void LoadEXE(Form form, string exepath)
            {
                ContainerControl = form;
                form.SizeChanged += Control_SizeChanged;
                proApp = new Process();
                proApp.StartInfo.UseShellExecute = false;
                proApp.StartInfo.CreateNoWindow = false;
                proApp.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
                proApp.StartInfo.FileName = exepath;
                proApp.StartInfo.Arguments = Process.GetCurrentProcess().Id.ToString();
                proApp.Start();
                Application.Idle += Application_Idle;
                EmbedProcess(proApp, form);
            }
            /// <summary>
            /// 确保应用程序嵌入此容器
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Application_Idle(object sender, EventArgs e)
            {
                if (this.proApp == null || this.proApp.HasExited)
                {
                    this.proApp = null;
                    Application.Idle -= Application_Idle;
                    return;
                }
                if (proApp.MainWindowHandle == IntPtr.Zero) return;
                Application.Idle -= Application_Idle;
                EmbedProcess(proApp, ContainerControl);
            }
            /// <summary>
            /// 将指定的程序嵌入指定的控件
            /// </summary>
            private void EmbedProcess(Process app, Control control)
            {
                // Get the main handle
                if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
                try
                {
                    // Put it into this form
                    SetParent(app.MainWindowHandle, control.Handle);
                    // Remove border and whatnot              
                    SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
                    ShowWindow(app.MainWindowHandle, (int)ProcessWindowStyle.Maximized);
                    MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
                }
                catch (Exception ex3)
                {
                    Console.WriteLine(ex3.Message);
                }
            }
            /// <summary>
            /// 嵌入容器大小改变事件
            /// </summary>
            private void Control_SizeChanged(object sender, EventArgs e)
            {
                if (proApp == null)
                {
                    return;
                }
                if (proApp.MainWindowHandle != IntPtr.Zero && ContainerControl != null)
                {
                    MoveWindow(proApp.MainWindowHandle, 0, 0, ContainerControl.Width, ContainerControl.Height, true);
                }
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            EmbeddedExeTool exetool = new EmbeddedExeTool();
            //WindowsFormsApp4.exe 为要嵌入外部exe的具体路径
            exetool.LoadEXE(panel1, AppDomain.CurrentDomain.BaseDirectory+ "WindowsFormsApp4.exe");//debug下面的文件夹
        }
    }
}

4.效果很好。

红配绿,绝配。 

第二种

和第一种方式有点不一样。 

1.建立一个winform主程序,为了区分背景是绿色

2.把代码直接复制进去,修改命名空间,再加上一个load事件即可

3.代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private const int GWL_STYLE = (-16);
        private const int WS_VISIBLE = 0x10000000;
        EventHandler appIdleEvent = null;
        Action<object, EventArgs> appIdleAction = null;
        Process m_AppProcess;
        [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
        public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
        public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);
        [DllImport("user32.dll", SetLastError = true)]
        private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        //SetParent(IntPtr hWndChild, IntPtr hWndNewParent);这个方法很重要,就是将hWndChild指向开启exe的窗体嵌入到hWndNewParent窗体的某个控件上,或者是窗体本 身的容器
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
        // MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);这个方法是windows的api,见名知意,是移动hwnd所指的窗体到指定的位置,并且指定是否需要重绘
        public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
        {
            if (IntPtr.Size == 4)
            {
                return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
            }
            return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
        }
        public Form1()
        {
            InitializeComponent();
            appIdleAction = new Action<object, EventArgs>(Application_Idle);
            appIdleEvent = new EventHandler(appIdleAction);
        }
        /// <summary>
        /// 确保应用程序嵌入此容器,再次确认exe嵌入,如果不调用这个方法,程序不一定能嵌入外部exe
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Application_Idle(object sender, EventArgs e)
        {
            if (this.m_AppProcess == null || this.m_AppProcess.HasExited)
            {
                this.m_AppProcess = null;
                Application.Idle -= appIdleEvent;//这一步一直不知道有什么用,但是不用这行代码程序有时候能嵌入有时候又不行
                return;
            }
            if (m_AppProcess.MainWindowHandle == IntPtr.Zero) return;
            Application.Idle -= appIdleEvent;
            EmbedProcess(m_AppProcess, this);
        }
        /// <summary>
        /// 将指定的程序嵌入指定的控件
        /// </summary>
        private void EmbedProcess(Process app, Control control)
        {
            // Get the main handle
            if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
            try
            {
                // Put it into this form
                SetParent(app.MainWindowHandle, control.Handle);
            }
            catch (Exception ex1)
            {
                Console.WriteLine(ex1.Message);
            }
            try
            {
                // Remove border and whatnot              
                SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
            }
            catch (Exception ex2)
            {
                Console.WriteLine(ex2.Message);
            }
            try
            {
                MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
            }
            catch (Exception ex3)
            {
                Console.WriteLine(ex3.Message);
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //以下这段代码是通过命令行方式调起一个exe程序,获取这个程序的句柄然后嵌入主的winform窗体中
            ProcessStartInfo info = new ProcessStartInfo(AppDomain.CurrentDomain.BaseDirectory + "WindowsFormsApp4.exe");//debug下面的文件夹
            info.WindowStyle = ProcessWindowStyle.Minimized;
            info.UseShellExecute = false;
            info.CreateNoWindow = false;
            m_AppProcess = System.Diagnostics.Process.Start(info);
            Application.Idle += appIdleEvent;
            try
            {
                EmbedProcess(m_AppProcess, this);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

4.效果

可见,第一种和第二种的效果有所区别的。

拓展。

WPF签入winform

1.依然用上面的winform程序,把输出类型改成类库

  

2.建立一个WPF程序,引用System.Windows.Forms和WindowsFormsIntegration,红色。绿色是步骤1生产的dll

3.在wpf中增加WindowsFormsHost控件

4.cs后台代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WindowsFormsApp4;
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Form1 mainform = new Form1();
            mainform.TopLevel = false;
            winform.Child = mainform;
        }
    }
}

5.效果

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。