在開發(fā)Windows桌面應(yīng)用程序時(shí),我們經(jīng)常需要在自己的應(yīng)用程序中嵌入并控制其他可執(zhí)行程序。本文將詳細(xì)介紹如何在C# WinForms應(yīng)用程序中實(shí)現(xiàn)這一功能,并提供多個(gè)實(shí)用示例。
基本概念 嵌入外部程序意味著將其他Windows應(yīng)用程序的窗口作為子窗口嵌入到我們的WinForms應(yīng)用程序中。這種技術(shù)通常用于:
集成第三方工具 創(chuàng)建復(fù)合應(yīng)用程序 提供統(tǒng)一的用戶界面 控制和管理多個(gè)應(yīng)用程序 技術(shù)實(shí)現(xiàn) 基本實(shí)現(xiàn)類 創(chuàng)建一個(gè)專門用于管理嵌入程序的類:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace AppEmbedding { public class ExternalProcessManager { // Windows API 常量 private const int GWL_STYLE = -16 ; private const int WS_CHILD = 0x40000000 ; private const int WS_VISIBLE = 0x10000000 ; // Windows API 聲明 private static class WindowsAPI { [DllImport( "user32.dll" )] public static extern IntPtr SetParent (IntPtr hWndChild, IntPtr hWndNewParent) ; [DllImport( "user32.dll" )] public static extern int GetWindowLong (IntPtr hWnd, int nIndex) ; [DllImport( "user32.dll" )] public static extern int SetWindowLong (IntPtr hWnd, int nIndex, int dwNewLong) ; [DllImport( "user32.dll" )] public static extern bool ShowWindow (IntPtr hWnd, int nCmdShow) ; [DllImport( "user32.dll" )] public static extern bool MoveWindow (IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint) ; } private Process _embeddedProcess; private Control _parentControl; public ExternalProcessManager (Control parentControl) { _parentControl = parentControl; } public void EmbedProcess ( string processPath) { try { // 啟動(dòng)進(jìn)程 - 使用 UseShellExecute = false 可能對某些應(yīng)用程序更有效 ProcessStartInfo startInfo = new ProcessStartInfo(processPath) { UseShellExecute = false , CreateNoWindow = false }; _embeddedProcess = Process.Start(startInfo); // 使用超時(shí)機(jī)制等待窗口句柄創(chuàng)建,避免無限等待 _embeddedProcess.WaitForInputIdle( 3000 ); // 設(shè)置等待主窗口句柄的超時(shí)時(shí)間 DateTime timeout = DateTime.Now.AddSeconds( 5 ); while (_embeddedProcess.MainWindowHandle == IntPtr.Zero) { if (DateTime.Now > timeout) { throw new TimeoutException( "無法獲取進(jìn)程主窗口句柄" ); } Application.DoEvents(); // 保持UI響應(yīng) System.Threading.Thread.Sleep( 100 ); } // 設(shè)置父窗口 WindowsAPI.SetParent(_embeddedProcess.MainWindowHandle, _parentControl.Handle); // 設(shè)置窗口樣式 int style = WindowsAPI.GetWindowLong(_embeddedProcess.MainWindowHandle, GWL_STYLE); style |= WS_CHILD | WS_VISIBLE; // 添加WS_VISIBLE確保窗口可見 WindowsAPI.SetWindowLong(_embeddedProcess.MainWindowHandle, GWL_STYLE, style); // 調(diào)整窗口位置和大小 WindowsAPI.MoveWindow( _embeddedProcess.MainWindowHandle, 0 , 0 , _parentControl.ClientSize.Width, _parentControl.ClientSize.Height, true ); // 確保父控件在尺寸變化時(shí)調(diào)整嵌入程序的大小 _parentControl.SizeChanged += (sender, e) => { if (_embeddedProcess != null && !_embeddedProcess.HasExited) { WindowsAPI.MoveWindow( _embeddedProcess.MainWindowHandle, 0 , 0 , _parentControl.ClientSize.Width, _parentControl.ClientSize.Height, true ); } }; } catch (Exception ex) { MessageBox.Show($ "嵌入進(jìn)程時(shí)出錯(cuò): {ex.Message}" , "錯(cuò)誤" , MessageBoxButtons.OK, MessageBoxIcon.Error); } } public void CloseEmbeddedProcess () { if (_embeddedProcess != null && !_embeddedProcess.HasExited) { try { _embeddedProcess.CloseMainWindow(); if (!_embeddedProcess.WaitForExit( 3000 )) { _embeddedProcess.Kill(); } } catch (Exception ex) { MessageBox.Show($ "關(guān)閉進(jìn)程時(shí)出錯(cuò): {ex.Message}" , "錯(cuò)誤" , MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { _embeddedProcess.Dispose(); _embeddedProcess = null; } } } } }
實(shí)際應(yīng)用示例 嵌入記事本示例 public partial class NotePadForm : Form { private ExternalProcessManager _processManager; public NotePadForm () { InitializeComponent(); // 創(chuàng)建一個(gè)Panel用于容納記事本 Panel notepadPanel = new Panel { Dock = DockStyle.Fill }; this .Controls.Add(notepadPanel); _processManager = new ExternalProcessManager(notepadPanel); } private void btnEmbedNotepad_Click (object sender, EventArgs e) { _processManager.EmbedProcess( "notepad.exe" ); } protected override void OnFormClosing (FormClosingEventArgs e) { _processManager.CloseEmbeddedProcess(); base.OnFormClosing(e); } }
嵌入計(jì)算器示例 public partial class CalculatorForm : Form { private ExternalProcessManager _processManager; private Panel _calcPanel; public CalculatorForm () { InitializeComponent(); // 創(chuàng)建計(jì)算器面板 _calcPanel = new Panel { Size = new Size( 300 , 400 ), Location = new Point( 10 , 10 ) }; this .Controls.Add(_calcPanel); _processManager = new ExternalProcessManager(_calcPanel); Button embedButton = new Button { Text = "嵌入計(jì)算器" , Location = new Point( 320 , 10 ) }; embedButton.Click += EmbedCalculator_Click; this .Controls.Add(embedButton); } private void EmbedCalculator_Click (object sender, EventArgs e) { _processManager.EmbedProcess( "calc.exe" ); } protected override void OnFormClosing (FormClosingEventArgs e) { _processManager.CloseEmbeddedProcess(); base.OnFormClosing(e); } }
常見問題解決 窗口無法正確嵌入
// 確保等待窗口句柄創(chuàng)建 while (process.MainWindowHandle == IntPtr.Zero) { System.Threading.Thread.Sleep( 100 ); process.Refresh(); }
DPI縮放問題
protected override void OnLoad (EventArgs e) { base.OnLoad(e); if (Environment.OSVersion.Version.Major >= 6 ) { SetProcessDPIAware(); } } [DllImport( "user32.dll" )] private static extern bool SetProcessDPIAware () ;
窗口大小調(diào)整
protected override void OnResize (EventArgs e) { base.OnResize(e); if (_embeddedProcess != null && !_embeddedProcess.HasExited) { WindowsAPI.MoveWindow(_embeddedProcess.MainWindowHandle, 0 , 0 , _parentControl.Width, _parentControl.Height, true ); } }
總結(jié) 通過本文的詳細(xì)介紹和示例,你應(yīng)該能夠在C# WinForms應(yīng)用程序中成功實(shí)現(xiàn)外部程序的嵌入和控制。記住要注意進(jìn)程管理、窗口處理、性能優(yōu)化和安全性等關(guān)鍵方面。合理使用這些技術(shù)可以幫助你創(chuàng)建更強(qiáng)大和靈活的應(yīng)用程序。
?
閱讀原文:原文鏈接
該文章在 2025/5/26 10:23:05 編輯過