流程:由于证书的问题,外部exe会在进入最终界面之前弹出两个对话框,所以需要点击"嵌入SA"两次。第一次启动exe,在手动点击弹出的两个无用对话框后出现最终界面,再点击一次实现嵌入。
思路:遍历电脑所有进程,通过窗口标题查找对应的进程。如果没找到,说明窗口已存在,直接使用该找到的进程进行嵌入;否则,先利用exe路径打开开启进程。另外,还要在程序退出时关闭进程,这里是在构造函数中实现了该功能,需要检查进程是否为空和是否已经退出,如果进程在则kill掉进程。
界面:要填充的parent control控件为panel,其dock属性为Fill,嵌入的窗口可以根据panel尺寸大小而变动。主要也是参考网上的一个帖子,然后加上了另外一个帖子的窗口尺寸自适应变化代码,最终界面效果如下图。
代码实现:
public class WinfromUtil
{
Process m_SAProcess;//SA进程
bool m_isEmbeded = false;//是否已经嵌入
Control m_parentControl = null;
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
~WinfromUtil()
{
if (m_SAProcess != null)
{
if (!m_SAProcess.HasExited)
m_SAProcess.Kill();
}
}
public void exbedExistingWindow(string mainWindowTitle, Control parentControl)
{
Process[] pro = Process.GetProcesses();//获取已开启的所有进程
for (int i = 0; i < pro.Length; i++)
{
string title = pro[i].MainWindowTitle;
if(title.Contains(mainWindowTitle))
{
MessageBox.Show(title, "进程" + i.ToString());
SetParent(pro[i].MainWindowHandle, parentControl.Handle);
ShowWindow(pro[i].MainWindowHandle, 3);
m_SAProcess = pro[i];
}
}
}
public bool embedExternalWindow(string exePath, string mainWindowTitle, Control parentControl)
{
//遍历所有电脑进程,通过窗口标题找到对应的进程
Process[] pro = Process.GetProcesses();//获取已开启的所有进程
int saProcessIndex = -1;
for (int i = 0; i < pro.Length; i++)
{
if (pro[i].MainWindowTitle.Contains(mainWindowTitle))
{
saProcessIndex = i;
break;
}
}
if (saProcessIndex == -1)//sa没启动的话就启动exe
{
string param = "";
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, param);
startInfo.FileName = exePath;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = false;
startInfo.WindowStyle = ProcessWindowStyle.Minimized;//为了美观,启动的时候最小化程序
process.StartInfo = startInfo;
process.Start();
// Wait for process to be created and enter idle condition
process.WaitForInputIdle();
return false;
}else if(saProcessIndex != -1)//如果启动了,就进行嵌入
{
if (m_isEmbeded)
return true;
m_SAProcess = pro[saProcessIndex];
IntPtr mainHandle = m_SAProcess.MainWindowHandle;//通过进程得到对应的句柄
SetParent(m_SAProcess.MainWindowHandle, parentControl.Handle);
ShowWindow(m_SAProcess.MainWindowHandle, (int)ProcessWindowStyle.Maximized);//3
MoveWindow(m_SAProcess.MainWindowHandle, 0, 0, parentControl.Width, parentControl.Height, true);
m_parentControl = parentControl;
m_parentControl.SizeChanged += Control_SizeChanged;
m_isEmbeded = true;
return true;
}
else
{
return false;
}
}
/// <summary>
/// 获取窗体的句柄函数
/// </summary>
/// <param name="lpClassName">窗口类名</param>
/// <param name="lpWindowName">窗口标题名</param>
/// <returns>返回句柄</returns>
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll ", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll ", EntryPoint = "ShowWindow")]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
/// <summary>
/// 【函数类型:工具类对外接口】
/// 【函数功能:加载外部exe程序到容器】
/// 【创建作者:LZHRyan】
/// 【创建时间:2018-4-5 13:52:24】
/// </summary>
/// <param name="exePath">exe程序路径</param>
/// <param name="param">exe初始化参数</param>
/// <param name="parentControl">依赖容器</param>
/// <param name="formTitle">主窗体标题</param>
/// <param name="canReLoad">是否允许重复运行程序</param>
public void ExeLoader(string exePath, string param, Control parentControl, string formTitle, bool canReLoad)
{
if (exePath.Contains(".exe") || exePath.Contains(".EXE"))
{
if (File.Exists(exePath))
{
try
{
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, param);
startInfo.FileName = exePath;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = false;
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
process.StartInfo = startInfo;
IntPtr mainHandle = FindWindow(null, formTitle);
process.Start();
int count = 0;
while (count < 20 * 30 && mainHandle == IntPtr.Zero)
{
count++;
Thread.Sleep(100);
mainHandle = FindWindow(null, formTitle);
}
//不允许重复启动,如果在这里限制了不能多启动相同进程,则调用的进程不能做相同的限制
if (!canReLoad && Process.GetProcessesByName(process.ProcessName).Length > 1)
{
//杀掉进程
process.Kill();
return;
}
else
{
SetParent(process.MainWindowHandle, parentControl.Handle);
ShowWindow(process.MainWindowHandle, 3);
}
MessageBox.Show("退出", "提示");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "异常", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
string content = "未能找到文件:" + exePath;
string title = "错误提示";
MessageBox.Show(content, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
string content = "文件格式不是可执行程序:" + exePath;
string title = "错误提示";
MessageBox.Show(content, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void Control_SizeChanged(object sender, EventArgs e)
{
if (m_SAProcess == null || !m_isEmbeded)
return;
if (m_SAProcess.MainWindowHandle != IntPtr.Zero && m_parentControl != null)
MoveWindow(m_SAProcess.MainWindowHandle, 0, 0, m_parentControl.Width, m_parentControl.Height, true);
}
}
使用:
string SAPath = "D:\\Program Files\\SpatialAnalyzer 2018.01.12_35218\\Spatial Analyzer.exe";
string title = "无标题 - SpatialAnalyzer";
m_externalWindow = new WinfromUtil();
m_externalWindow.embedExternalWindow(SAPath, title, panel_exmbedSA);
嵌入后的外部窗体悬浮
嵌入外部进程窗体后,可能想让嵌入的窗体悬浮,独立出去。既然嵌入窗体是给外部窗体一个parent,那如果该parent为null,那就是独立出去了,用到的函数为SetParent。代码如下:
//悬浮窗口,即不嵌入
public void floatEmbededWindow()
{
if (m_SAProcess == null)
return;
SetParent(m_SAProcess.MainWindowHandle, IntPtr.Zero);//m_SAProcess是外部窗口进程
m_isEmbeded = false;
}