本文是实现窗体内嵌,如果需要实现类似程序加壳功能,嵌入外部程序并运行,可参考本人发表的文章:https://blog.csdn.net/lzl_li/article/details/117026469
近期发现ffplay很好强大,但没有图形控制界面,就想对ffplay进行简单包装,程序运行ffplay然后将ffplay显示的窗体拉入自己的控件上,实现播放器功能。[程序已完成]
在此过程中,本人建立了一个类,通过窗体标题字符串查找目标窗体或通过进程获取进程的主窗体,然后将目标窗体内嵌到自有控件中显示,在此分享给大家。(封装ffplay的程序没有在本文介绍,因为封装ffplay还涉及一些数据分析及控制)
补充说明:通过SetParent内嵌窗体成功后,后台通过GetParent获取内嵌窗体的父窗体句柄还是0,而且内嵌窗体进程的MainWindowHandle的值也变成0
- 内嵌外部窗体主要使用SetParent函数,函数中的第1个参数是我们的目标窗体句柄,第2个参数是要设置的父控件句柄。
- 目标窗体句柄可用EnumWindows函数枚举系统里的所有顶层窗体,结合GetWindowText函数查找窗体标题字符串获取我们想要的目标窗体句柄,或者使用GetProcessHandleFromHwnd从窗体句柄获取所在进程的句柄,再使用GetProcessId将进程句柄转化为进程ID,通过进程ID识别是否为我们要找的窗体。
主要代码如下(全部源代码下载):
/// <summary>
/// 开始通过标题关键字查找并捕捉窗体(找到符合条件的第一个窗体之后中断)
/// </summary>
/// <returns>捕获是否成功</returns>
public bool Start()
{
System.Diagnostics.Process[] pros = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process p in pros)
{
if (p.MainWindowTitle.Contains(titleString))
{
targetForm = p.MainWindowHandle;//将找到的窗体句柄赋值给targetForm
pullForm(p);//将窗体拉入设定的控件中,传递p主要用于监测进程退出事件
break;
}
}
/* 方法二:调用API获取
CallBack cback = new CallBack(this.FindControl);
int r=EnumWindows(cback, 0);
pullForm();
//*/
if (targetForm.Equals(IntPtr.Zero))
return false;
else
return true;
}
/// <summary>
/// 通过进程ID捕捉进程主窗体
/// </summary>
/// <param name="processID">进程ID</param>
/// <returns>捕获是否成功</returns>
public bool Start(int processID)
{
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(processID);
targetForm = p.MainWindowHandle;//将找到的窗体句柄赋值给targetForm
pullForm(p);//将窗体拉入设定的控件中,传递p主要用于监测进程退出事件
if (targetForm.Equals(IntPtr.Zero))
return false;
else
return true;
}
/// <summary>
/// API函数EnumWindows调用的委托函数实现
/// </summary>
/// <param name="hwnd"></param>
/// <param name="lParam"></param>
/// <returns></returns>
private bool FindControl(IntPtr hwnd, int lParam)
{
IntPtr pHwnd;
pHwnd = GetParent(hwnd);
//if (IsWindowVisible(hwnd) == 1)
if (pHwnd == IntPtr.Zero && IsWindowVisible(hwnd) == 1)
{
StringBuilder sb = new StringBuilder(512);
GetWindowText(hwnd, sb, sb.Capacity);
if (sb.ToString().Contains(titleString))
{
targetForm = hwnd;
return false;
}
}
return true;
}
/// <summary>
/// 捕获窗体(前提是目标窗体targetForm已经赋值)
/// </summary>
private void pullForm()
{
if (targetForm == IntPtr.Zero)
return;
bool ok = false;
IntPtr pHandle = GetProcessHandleFromHwnd(targetForm);
int pid = GetProcessId(pHandle);
System.Diagnostics.Process[] pros = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process p in pros)
{
try
{
//if (p.Handle == pHandle)//p.Handle拒绝访问
if (p.Id == pid)//找到窗体所在进程
{
pullForm(p);
ok = true;
break;
}
}
catch { }
}
if (ok == false)
pullForm(null);
}
/// <summary>
/// 捕捉窗体(前提是目标窗体targetForm已经赋值)
/// </summary>
/// <param name="p">窗体所属进程</param>
private void pullForm(System.Diagnostics.Process p)
{
if (targetForm == IntPtr.Zero)
return;
IntPtr curParent = GetParent(targetForm);
process = p;
RECT rect = new RECT();
bool r = GetWindowRect(targetForm, ref rect);//获取当前位置大小信息,便于后续恢复状态
oldState = new FormState(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
SetParent(targetForm, parent);//设置新的父控制(捕捉到自己想要的控件上),要注意的是设置成功后通过GetParent获取内嵌窗体的父窗体句柄还是0
Resize();//调整大小到新父控件大小
threadWaitExit = new System.Threading.Thread(threadWaitAppExit);
threadWaitExit.Start(p);//开启线程:监测进程结束
}
另外,程序加壳功能可参考:https://blog.csdn.net/lzl_li/article/details/117026469