EmbedPanel.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. using System;
  2. using System.ComponentModel;
  3. using System.Windows.Forms;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. namespace Y.Skin.YoPanel
  8. {
  9. public partial class EmbedPanel : Panel
  10. {
  11. //1 焦点问题,焦点导致内外两个窗口标题栏颜色不一致;
  12. //2 有些应用嵌入不了,会直接被单独打开;
  13. //3 有些应用嵌入不正常,位置与预计的不同;
  14. //4 有些应用嵌入关闭时会在后台继续运行;
  15. //5 调试期间 vs 不能强行退出 否则嵌入的程序不会退出;
  16. //embedPanel1.ReEmbed();
  17. //embedPanel1.Start(@"D:\CoCo\GitHub\Temp\ClipboardMonitor\ClipboardMonitor\ClipboardMonitor\bin\Debug\ClipboardMonitor.exe");
  18. //embedPanel1.Start(@"D:\Soft\DisplayX.1034260498.exe");
  19. Action<object, EventArgs> appIdleAction = null;
  20. EventHandler appIdleEvent = null;
  21. public EmbedPanel()
  22. {
  23. InitializeComponent();
  24. appIdleAction = new Action<object, EventArgs>(Application_Idle);
  25. appIdleEvent = new EventHandler(appIdleAction);
  26. }
  27. public EmbedPanel(IContainer container)
  28. {
  29. container.Add(this);
  30. InitializeComponent();
  31. appIdleAction = new Action<object, EventArgs>(Application_Idle);
  32. appIdleEvent = new EventHandler(appIdleAction);
  33. }
  34. /// <summary>
  35. /// 嵌入程序(保证程序嵌入)
  36. /// </summary>
  37. /// <param name="sender"></param>
  38. /// <param name="e"></param>
  39. void Application_Idle(object sender, EventArgs e)
  40. {
  41. if (_AppProcess == null || _AppProcess.HasExited)
  42. {
  43. _AppProcess = null;
  44. Application.Idle -= appIdleEvent;
  45. return;
  46. }
  47. if (_AppProcess.MainWindowHandle == IntPtr.Zero) return;
  48. Application.Idle -= appIdleEvent;
  49. EmbedProcess(_AppProcess, this);
  50. }
  51. #region 常规属性
  52. private string AppFile = "";
  53. private Process _AppProcess = null;
  54. public Process AppProcess
  55. {
  56. get { return _AppProcess; }
  57. set { _AppProcess = value; }
  58. }
  59. public bool IsStarted { get { return (_AppProcess != null); } }
  60. #endregion
  61. #region 控件面板属性
  62. #endregion
  63. /// <summary>
  64. /// 启动嵌入程序
  65. /// </summary>
  66. /// <param name="appFilename"></param>
  67. public void Start(string appFile)
  68. {
  69. if (appFile.ToLower() == Application.ExecutablePath.ToLower()) return;//禁止嵌入自己
  70. if (!appFile.ToLower().EndsWith(".exe")) return;//禁止嵌入非exe文件
  71. if (!File.Exists(appFile)) return;//禁止嵌入不存在文件
  72. AppFile = appFile;
  73. if (_AppProcess != null) Stop();//停止正在运行的进程
  74. try
  75. {
  76. ProcessStartInfo info = new ProcessStartInfo(AppFile);
  77. info.UseShellExecute = true;
  78. info.WindowStyle = ProcessWindowStyle.Minimized;
  79. //info.WindowStyle = ProcessWindowStyle.Hidden;
  80. _AppProcess = Process.Start(info);
  81. //等待创建进程
  82. _AppProcess.WaitForInputIdle();
  83. //todo:下面这两句会引发 NullReferenceException 异常,不知道怎么回事
  84. //m_AppProcess.Exited += new EventHandler(m_AppProcess_Exited);
  85. //m_AppProcess.EnableRaisingEvents = true;
  86. Application.Idle += appIdleEvent;
  87. }
  88. catch (Exception ex)
  89. {
  90. //嵌入失败杀死进程
  91. if (_AppProcess != null)
  92. {
  93. if (!_AppProcess.HasExited)
  94. _AppProcess.Kill();
  95. _AppProcess = null;
  96. }
  97. }
  98. }
  99. /// <summary>
  100. /// 重新嵌入
  101. /// </summary>
  102. public void ReEmbed()
  103. {
  104. EmbedProcess(_AppProcess, this);
  105. }
  106. /// <summary>
  107. /// 退出嵌入的程序
  108. /// </summary>
  109. public void Stop()
  110. {
  111. if (_AppProcess != null)// && m_AppProcess.MainWindowHandle != IntPtr.Zero)
  112. {
  113. try
  114. {
  115. if (!_AppProcess.HasExited)
  116. _AppProcess.Kill();
  117. }
  118. catch (Exception) { }
  119. _AppProcess = null;
  120. }
  121. }
  122. /// <summary>
  123. /// 将程序嵌入控件
  124. /// </summary>
  125. /// <param name="app">嵌入程序</param>
  126. /// <param name="control">指定控件</param>
  127. private void EmbedProcess(Process app, Control control)
  128. {
  129. //验证程序和控件非空
  130. if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
  131. try
  132. {
  133. //核心代码:嵌入程序
  134. SetParent(app.MainWindowHandle, control.Handle);
  135. }
  136. catch (Exception) { }
  137. try
  138. {
  139. //移除嵌入的窗口的窗口标题栏
  140. SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
  141. }
  142. catch (Exception) { }
  143. try
  144. {
  145. //将嵌入的窗口欧放置到合适位置,填满宽高
  146. MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
  147. }
  148. catch (Exception) { }
  149. }
  150. #region 重写部分方法
  151. /// <summary>
  152. /// 窗体句柄销毁
  153. /// </summary>
  154. /// <param name="e"></param>
  155. protected override void OnHandleDestroyed(EventArgs e)
  156. {
  157. Stop();//将应用关闭
  158. base.OnHandleDestroyed(e);
  159. }
  160. /// <summary>
  161. /// 窗体大小修改
  162. /// </summary>
  163. /// <param name="eventargs"></param>
  164. protected override void OnResize(EventArgs eventargs)
  165. {
  166. if (_AppProcess != null)
  167. {
  168. MoveWindow(_AppProcess.MainWindowHandle, 0, 0, this.Width, this.Height, true);
  169. }
  170. base.OnResize(eventargs);
  171. }
  172. /// <summary>
  173. /// 窗台属性修改
  174. /// </summary>
  175. /// <param name="e"></param>
  176. protected override void OnSizeChanged(EventArgs e)
  177. {
  178. this.Invalidate();
  179. base.OnSizeChanged(e);
  180. }
  181. #endregion
  182. #region Win32 API 常量
  183. private const int SWP_NOOWNERZORDER = 0x200;
  184. private const int SWP_NOREDRAW = 0x8;
  185. private const int SWP_NOZORDER = 0x4;
  186. private const int SWP_SHOWWINDOW = 0x0040;
  187. private const int WS_EX_MDICHILD = 0x40;
  188. private const int SWP_FRAMECHANGED = 0x20;
  189. private const int SWP_NOACTIVATE = 0x10;
  190. private const int SWP_ASYNCWINDOWPOS = 0x4000;
  191. private const int SWP_NOMOVE = 0x2;
  192. private const int SWP_NOSIZE = 0x1;
  193. private const int GWL_STYLE = (-16);
  194. private const int WS_VISIBLE = 0x10000000;
  195. private const int WM_CLOSE = 0x10;
  196. private const int WS_CHILD = 0x40000000;
  197. private const int SW_HIDE = 0; //{隐藏, 并且任务栏也没有最小化图标}
  198. private const int SW_SHOWNORMAL = 1; //{用最近的大小和位置显示, 激活}
  199. private const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL}
  200. private const int SW_SHOWMINIMIZED = 2; //{最小化, 激活}
  201. private const int SW_SHOWMAXIMIZED = 3; //{最大化, 激活}
  202. private const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED}
  203. private const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置显示, 不激活}
  204. private const int SW_SHOW = 5; //{同 SW_SHOWNORMAL}
  205. private const int SW_MINIMIZE = 6; //{最小化, 不激活}
  206. private const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE}
  207. private const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE}
  208. private const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL}
  209. private const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL}
  210. private const int SW_MAX = 10; //{同 SW_SHOWNORMAL}
  211. #endregion
  212. #region Win32 API 方法声明
  213. [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
  214. CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
  215. private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
  216. [DllImport("user32.dll", SetLastError = true)]
  217. private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
  218. [DllImport("user32.dll", SetLastError = true)]
  219. public static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
  220. [DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
  221. private static extern long GetWindowLong(IntPtr hwnd, int nIndex);
  222. public static IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
  223. {
  224. if (IntPtr.Size == 4)
  225. {
  226. return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
  227. }
  228. return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
  229. }
  230. [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
  231. public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
  232. [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
  233. public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);
  234. [DllImport("user32.dll", SetLastError = true)]
  235. private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
  236. [DllImport("user32.dll", SetLastError = true)]
  237. private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
  238. [DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
  239. private static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam);
  240. [DllImport("user32.dll", SetLastError = true)]
  241. private static extern IntPtr GetParent(IntPtr hwnd);
  242. [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
  243. static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
  244. #endregion Win32 API
  245. }
  246. }