Winform 程序嵌入WPF程序 并发送消息
废话不多说,先看解决方案目录
WindowsFormsDemo是主程序,WpfApp是嵌入的WPF程序,先看WPF程序,程序默认启动的页面是MainWindow.xaml,这里注释掉App.xaml里的StartupUri="MainWindow.xaml",后台设置启动的Uri,将原来的空的App类改成一些内容(也可以不改)
/// <summary> /// App.xaml 的交互逻辑 /// </summary> public partial class App : Application { public App() { Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException; } void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { // 这里通常需要给用户一些较为友好的提示,并且后续可能的操作 MessageBox.Show(e.Exception.Message, "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information); e.Handled = true;//我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员. } }
添加Program启动类并加入主方法Main,尽管修改了但是启动程序仍然会报错,需要右键设置工程属性设置启动对象
下面看看启动类里的东西
class Program { [STAThread] static void Main(string[] args) { // 校验启动的参数,未设置参数为非法启动 if (null == args || args.Length <= 0) { MessageBox.Show("非法启动!"); return; } // 获取参数,在WindowsFormsDemo里启动的时候设置,参数的规则自己设置 string activeParameter = args[0]; string[] parameters = activeParameter.Split(','); try { App app = new App(); string activeName = parameters[0]; if (activeName == "UploadFile") { MainWindow mainWindow = new MainWindow(); // 获取主界面的句柄(最后一个参数),向主窗体发送消息需要根据主窗体的句柄来发送 // 在启动这个WpfApp 进程的时候设置最后一个参数为主窗体句柄 OperationContainer.ContainerPlayPtr = new IntPtr(Convert.ToInt32(parameters[parameters.Length - 1])); app.MainWindow = mainWindow; app.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative); } // 参数就是拼接的字符串,看WindowsFormsDemo主窗体的拼接方式 GetParameters(parameters[1]); app.Run(); App.Main(); } catch (Exception e) { MessageBox.Show(e.Message); } } /// <summary> /// 分割参数(参数规则看WindowsFormsDemo启动时设置的参数字符串) /// </summary> /// <param name="param"></param> static void GetParameters(string param) { string[] strs = param.Split('&'); ; Dictionary<string, string> dic = new Dictionary<string, string>(); foreach (var item in strs) { string[] key = item.Split('='); dic.Add(key[0], key[1]); } // 静态来存放参数值 OperationContainer.ServerIP = dic["ServerIP"]; OperationContainer.Type = dic["Type"]; OperationContainer.UserID = dic["UserID"]; OperationContainer.CaseID = dic["CaseID"]; }
这个地方要注意的是,我开始设置参数值的时候是给MainWindow.xaml.cs里的MainWindow设置属性的方式传的,在启动App并对MainWindow对象实例化,取得参数赋给MainWindow的属性,但是程序启动之后过了一会儿才会赋值过去,不知道是不是窗体渲染的次序问题
操作容器缓存类OperationContainer
/// <summary> /// 常见操作缓存类 /// </summary> public class OperationContainer { /// <summary> /// 主容器窗口句柄 /// </summary> public static IntPtr ContainerPlayPtr = IntPtr.Zero; #region 长连接属性 public static string ServerIP = ""; public static string Type = ""; public static string UserID = ""; public static string UserName = "admin"; public static string CaseID = ""; #endregion }
MainWindow.xaml里的内容,一个按钮和按钮的点击事件,点击的时候向主窗体发送消息
<Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded"> <Grid Background="#FFA1E5DB"> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="btn_Upload" /> </Grid> </Window>
MainWindow.xaml里的后台代码,消息发送
/// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { /// <summary> /// 当前进程句柄 /// </summary> //public IntPtr CurWindows { get; set; } /// <summary> /// 主进程句柄 /// </summary> public IntPtr ContainerPlayPtr { get; set; } public int UploadType { get; set; } public string CaseID { get; set; } #region 进程消息处理 [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); /// <summary> /// 向主窗体发送消息 /// </summary> /// <param name="success">标识是否操作成功,可根据自己使用情况来定</param> private void Send(int success) { // 需要根据主窗体的句柄来发送,OperationContainer.ContainerPlayPtr在App程序初始化的时候已经取得的主窗体的句柄 SendMessage(OperationContainer.ContainerPlayPtr, Message.WM_MSG, success, 0); } #endregion public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { //MsgForm form = new MsgForm(); //CurWindows = form.Handle; //ContainerPlayPtr = OperationContainer.ContainerPlayPtr; //MessageBox.Show("容器窗体句柄:" + ContainerPlayPtr.ToInt32()); // 取得参数 UploadType = Convert.ToInt32(OperationContainer.Type); CaseID = OperationContainer.CaseID; } private void btn_Upload(object sender, RoutedEventArgs e) { MessageBox.Show(CaseID); // 向主窗体发送消息 Send(0); } }
其中的消息枚举
/// <summary> /// 消息枚举 /// </summary> public class Message { public const int USER = 0x0400; public const int WM_MSG = USER + 101; }
至于Controls里的内容暂时还没用到
有了上面的内容,这个时候启动WpfApp应该会提示非法启动,给启动参数赋初始值 args = new string[] { "UploadFile,ServerIP=127.0.0.1&Type=2&UserID=xx&CaseID=604dffdd-0f8f-430d-8ca4-1d9714ba7609,11202" };即可成功启动
下面看看主窗体里的内容,先看下容器类AppContainer
public class AppContainer { #region 注册API函数 [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId); [DllImport("user32.dll")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)] private static extern long GetWindowLong(IntPtr hwnd, int nIndex); [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)] internal static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll", SetLastError = true)] private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags); [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", EntryPoint = "PostMessageA", SetLastError = true)] private static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetParent(IntPtr hwnd); [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); private const int SWP_NOOWNERZORDER = 0x200; private const int SWP_NOREDRAW = 0x8; private const int SWP_NOZORDER = 0x4; private const int SWP_SHOWWINDOW = 0x0040; private const int WS_EX_MDICHILD = 0x40; private const int SWP_FRAMECHANGED = 0x20; private const int SWP_NOACTIVATE = 0x10; private const int SWP_ASYNCWINDOWPOS = 0x4000; private const int SWP_NOMOVE = 0x2; private const int SWP_NOSIZE = 0x1; private const int GWL_STYLE = (-16); private const int WS_VISIBLE = 0x10000000; private const int WM_CLOSE = 0x10; private const int WS_CHILD = 0x40000000; private const int SW_HIDE = 0; //{隐藏, 并且任务栏也没有最小化图标} private const int SW_SHOWNORMAL = 1; //{用最近的大小和位置显示, 激活} private const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL} private const int SW_SHOWMINIMIZED = 2; //{最小化, 激活} private const int SW_SHOWMAXIMIZED = 3; //{最大化, 激活} private const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED} private const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置显示, 不激活} private const int SW_SHOW = 5; //{同 SW_SHOWNORMAL} private const int SW_MINIMIZE = 6; //{最小化, 不激活} private const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE} private const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE} private const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL} private const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL} private const int SW_MAX = 10; //{同 SW_SHOWNORMAL} private const int HWND_TOP = 0x0; private const int WM_COMMAND = 0x0112; private const int WM_QT_PAINT = 0xC2DC; private const int WM_PAINT = 0x0001; private const int WM_SIZE = 0x0001; #endregion [SecuritySafeCritical] internal static void MoveWindow(Process app, Control control) { if (app != null) { MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true); } } [SecuritySafeCritical] internal static void SetWindowLong(HandleRef handleRef) { SetWindowLong(handleRef, GWL_STYLE, WS_VISIBLE); } [SecuritySafeCritical] internal static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong) { if (IntPtr.Size == 4) { return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); } return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); } }
这段代码是一同事写的,拿来直接用就可以了,其中上半段是windows的函数,下面的三个方法是拖动主窗体改变主窗体大小时,设置嵌套的窗体内容
看下主窗体里的代码
public partial class MainForm : Form { public MainForm() { InitializeComponent(); } Process _process; bool isFirst = false; private void MainForm_Load(object sender, EventArgs e) { Process[] processs = Process.GetProcessesByName("WpfApp.exe"); foreach (var item in processs) { item.Kill(); } //string strPath = this.GetType().Assembly.Location; //string baseDir = Path.Combine(strPath.Substring(0, strPath.LastIndexOf("\\")), ""); string fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WpfApp.exe"); if (File.Exists(fileName)) { // 设置消息参数 string param = "ServerIP=127.0.0.1&Type=2&UserID=xx&CaseID=604dffdd-0f8f-430d-8ca4-1d9714ba7609"; ProcessStartInfo startInfo = new ProcessStartInfo(fileName); startInfo.Arguments = "UploadFile," + param + "," + this.Handle; startInfo.UseShellExecute = true; startInfo.CreateNoWindow = true; startInfo.WorkingDirectory = ""; startInfo.WindowStyle = ProcessWindowStyle.Hidden; _process = Process.Start(startInfo); _process.EnableRaisingEvents = true; WaitForInputIdle(); EmbedProcess(_process); } else MessageBox.Show("未找到需要启动的exe文件"); isFirst = true; } private void WaitForInputIdle() { while (_process.MainWindowHandle == IntPtr.Zero) { _process.Refresh(); _process.MainWindowHandle.ToInt32(); } } /// <summary> /// 嵌入程序 /// </summary> private void EmbedProcess(Process process) { if (process != null && !(process.MainWindowHandle == IntPtr.Zero)) { try { // Put it into this form AppContainer.SetParent(process.MainWindowHandle, panel1.Handle); // Remove border and whatnot AppContainer.SetWindowLong(new HandleRef(this, process.MainWindowHandle)); // Move the window to overlay it on this window AppContainer.MoveWindow(_process, panel1); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } private void MainForm_Resize(object sender, EventArgs e) { // 设置窗体填充主窗体 if (isFirst) AppContainer.MoveWindow(_process, this); } public string Prams { get; set; } [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(IntPtr hWnd, int msg, uint wParam, uint lParam); // 响应和处理自定义消息 protected override void DefWndProc(ref System.Windows.Forms.Message m) { switch (m.Msg) { case Message.WM_MSG://处理接收到的消息 int success = m.WParam.ToInt32(); if (success == 0) MessageBox.Show("成功"); break; default: base.DefWndProc(ref m); break; } } public class Message { public const int USER = 0x0400; public const int WM_MSG = USER + 101; } }
以上就是所有的代码,这个主要是最近开发ActiveX控件,考虑Winform 实现特效比较麻烦,所以用嵌入WPF的方式实现