如何判断进程主窗口加载完成,将Stellarium正确嵌入WinForm的Panel控件
根因分析
Stellarium启动时会先加载启动屏窗口,此时你读取到的Process.MainWindowHandle实际是启动屏的句柄,将启动屏嵌入Panel后,启动屏销毁时不会自动把后续生成的主窗口挂载到Panel下,就会出现主窗口在Panel外打开的问题。固定时长等待的方案兼容性极差,不同性能设备的启动速度差异很大,无法适配所有场景。
解决方案
核心思路是异步循环检测进程的主窗口句柄,匹配到主窗口后再执行嵌入操作,不会阻塞UI线程,也不需要固定等待时长。
第一步:声明所需Windows API
using System.Text; using System.Runtime.InteropServices; [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll", SetLastError = true)] private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern bool IsWindow(IntPtr hWnd);
第二步:修改按钮点击逻辑
private async void Notepad_Button_Click(object sender, EventArgs e) { var psi = new ProcessStartInfo(@"C:\Program Files\Stellarium\stellarium.exe") { WindowStyle = ProcessWindowStyle.Minimized }; using (var p = Process.Start(psi)) { IntPtr stellariumMainHandle = IntPtr.Zero; // 最多等待30秒,避免进程异常时无限循环 for (int retryCount = 0; retryCount < 300; retryCount++) { // 刷新进程缓存属性,否则MainWindowHandle不会更新 p.Refresh(); IntPtr currentHandle = p.MainWindowHandle; if (currentHandle != IntPtr.Zero && IsWindow(currentHandle)) { StringBuilder windowTitle = new StringBuilder(256); GetWindowText(currentHandle, windowTitle, 256); // 匹配Stellarium主窗口标题,可根据实际版本调整匹配规则 if (windowTitle.ToString().Contains("Stellarium")) { stellariumMainHandle = currentHandle; break; } } // 每100ms检测一次,不阻塞UI线程 await Task.Delay(100); } if (stellariumMainHandle != IntPtr.Zero) { SetParent(stellariumMainHandle, panel1.Handle); MoveWindow(stellariumMainHandle, 0, 0, panel1.Width, panel1.Height, true); } else { MessageBox.Show("Stellarium主窗口加载超时,请检查程序是否正常启动"); } } }
额外优化建议
如果后续Stellarium版本更新修改了主窗口标题,你可以替换为GetClassNameAPI检测窗口类名做匹配,判断准确率更高。
内容的提问来源于stack exchange,提问作者Selim Boz
相关产品推荐
相关产品推荐

