winform 将untiy程序嵌入到一个panel里
核心脚本
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SimulationTraining
{
public class ExetowinformClass//将应用程序嵌入winform
{
EventHandler appIdleEvent = null;//这个事件是程序处于空闲时触发 这里用于出发当程序加载完成后要做的事
public Form ParentForm = null;
string strGUID = "";
#region 属性
//标识内嵌程序是否已经启动
public Process m_AppProcess = null;
public bool IsStarted { get { return (this.m_AppProcess != null); } }
#endregion 属性
public ExetowinformClass(string Titlestr)
{
appIdleEvent = new EventHandler(Application_Idle);
strGUID = Titlestr;
}
//将属性AppFilename指向的应用程序打开并嵌入此容器
public IntPtr Start(string FileNameStr, string arg)
{
if (m_AppProcess != null)
{
Stop();
}
try
{
ProcessStartInfo info = new ProcessStartInfo(FileNameStr);
info.UseShellExecute = true;
info.WindowStyle = ProcessWindowStyle.Minimized;
info.Arguments = arg;
m_AppProcess = System.Diagnostics.Process.Start(info);
m_AppProcess.WaitForInputIdle();
Application.Idle += appIdleEvent;
}
catch
{
if (m_AppProcess != null)
{
if (!m_AppProcess.HasExited) m_AppProcess.Kill();
m_AppProcess = null;
}
}
return m_AppProcess.Handle;
}
//程序加载完成时要做的事
private void Application_Idle(object sender, EventArgs e)
{
if (this.m_AppProcess == null || this.m_AppProcess.HasExited)
{
this.m_AppProcess = null;
Application.Idle -= appIdleEvent;
return;
}
//Thread.Sleep(300); //这里加阻塞,时间可以大些
Application.DoEvents();
if (m_AppProcess.MainWindowHandle == IntPtr.Zero) return;
Application.Idle -= appIdleEvent;
//EmbedProcess(panel_play);
}
//将属性AppFilename指向的应用程序关闭
public void Stop()
{
if (m_AppProcess != null)
{
try
{
if (!m_AppProcess.HasExited)
m_AppProcess.Kill();
}
catch (Exception)
{
}
m_AppProcess = null;
}
}
//将指定的程序嵌入指定的控件 这一步会真正嵌入到winform里
public void EmbedProcess(Control control)
{
if (m_AppProcess == null || m_AppProcess.MainWindowHandle == IntPtr.Zero || control == null) return;
try
{
//put it into this form
ShowWindow(m_AppProcess.MainWindowHandle, 0);//先将窗体隐藏,避免闪烁
SetParent(m_AppProcess.MainWindowHandle, control.Handle);
}
catch (Exception)
{ }
try
{
//Remove border and whatnot
SetWindowLong(new HandleRef(this, m_AppProcess.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
SendMessage(m_AppProcess.MainWindowHandle, WM_SETTEXT, IntPtr.Zero, strGUID);
}
catch (Exception)
{ }
try
{
//Move the window to overlay it on this window
MoveWindow(m_AppProcess.MainWindowHandle, 0, 0, control.Width, control.Height, true);
ShowWindow(m_AppProcess.MainWindowHandle, 3);//窗体显示并最大化
}
catch (Exception)
{ }
}
#region Win32 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", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassname, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern long 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)]
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);
public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, 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", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string 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 = 0x40;
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
const int WM_SETTEXT = 0x000C;
private const int WM_COPYDATA = 0x004A;//接收外部消息
#endregion Win32 API
}
}
使用方法:
//伪代码
unityExeManager = new ExetowinformClass("");//参数是窗口的标题名
unityExeManager.Start(exeFullPath, "");//参数2 启动参数
//我用了一个timer和ProcessBar,Untiy程序以最小化启动,等进度条wiform的processBar执行完成后,再将unity嵌入panel,并最大化Unity,这么做是为了不让用户看到madewithunity
//等timer执行完毕后嵌入panel
unityExeManager.EmbedProcess(panel_play);
//如果不需要进度条,那么可以直接在ExetowinformClass的Application_Idle方法里,取消注释//EmbedProcess(panel_play);来自动嵌入,注意这里panel_play只是演示用的参数,取消注释后需要稍微修改一下代码才能正确执行
//这一部分是timer的代码,仅供参考
private void timer_UnityLoad_Tick(object sender, EventArgs e)//itmer事件
{
progressBar_UnityLoad.Value += 1;//timer每0.1秒执行一次,一秒钟为进度条填充10个单位,我们希望他能执行5-7秒
if (progressBar_UnityLoad.Value == progressBar_UnityLoad.Maximum)
{
panel_progressBar.Visible = false;//隐藏进度条面板
GameApp.Interface.SendEvent(new OnEmbedExe_unityManager(playPanel));
timer_UnityLoad.Stop();
}
}