启动进程时是否可以隐藏鼠标沙漏忙状态指示器?
背景
我正在开发一款大量使用SetParent功能的应用,用于将多个视频播放器嵌入同一主应用内,同时控制主应用内存占用。用户每次请求播放视频时,会启动新的player.exe并挂载到主窗口,绝大多数场景下运行正常。
但我遇到一个棘手问题:当用户快速连续播放大量视频时,主应用需要频繁销毁、创建新的播放器进程,每次启动player.exe时鼠标图标都会出现小沙漏,由于进程创建频率极高,沙漏会持续显示。
需求依据
我认为该需求可实现,例如谷歌Chrome浏览器每个标签页为独立进程,新建标签页时并不会每次都出现忙沙漏图标。
细节说明
我可同时控制主应用与播放器应用的源码,可对两端做任意修改。
我编写了一个小型WinForm应用复现该行为,包含2个按钮与1个panel,代码如下:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; namespace SetParentTest { public partial class Form1 : Form { private Process _childProcessPlayer; public Form1() { InitializeComponent(); this.Closing += (sender, args) => { Clear(); }; } public const UInt32 WS_POPUP = 0x80000000; public const UInt32 WS_CHILD = 0x40000000; [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetParent( IntPtr windowChildHandle, IntPtr windowNewParentHandle); [DllImport("user32.dll", SetLastError = true)] public static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); [DllImport ( "user32.dll" )] public static extern int SetWindowLong ( IntPtr hWnd, int nIndex, uint dwNewLong ); public enum WindowLongFlags : int { GWL_EXSTYLE = -20, GWLP_HINSTANCE = -6, GWLP_HWNDPARENT = -8, GWL_ID = -12, GWL_STYLE = -16, GWL_USERDATA = -21, GWL_WNDPROC = -4, DWLP_USER = 0x8, DWLP_MSGRESULT = 0x0, DWLP_DLGPROC = 0x4 } [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool MoveWindow( IntPtr windowHandle, int x, int y, int width, int height, [MarshalAs(UnmanagedType.Bool)] bool repaint); [DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); /// <summary>Enumeration of the different ways of showing a window using /// ShowWindow</summary> private enum WindowShowStyle : uint { /// <summary>Hides the window and activates another window.</summary> /// <remarks>See SW_HIDE</remarks> Hide = 0, /// <summary>Activates and displays a window. If the window is minimized /// or maximized, the system restores it to its original size and /// position. An application should specify this flag when displaying /// the window for the first time.</summary> /// <remarks>See SW_SHOWNORMAL</remarks> ShowNormal = 1, /// <summary>Activates the window and displays it as a minimized window.</summary> /// <remarks>See SW_SHOWMINIMIZED</remarks> ShowMinimized = 2, /// <summary>Activates the window and displays it as a maximized window.</summary> /// <remarks>See SW_SHOWMAXIMIZED</remarks> ShowMaximized = 3, /// <summary>Maximizes the specified window.</summary> /// <remarks>See SW_MAXIMIZE</remarks> Maximize = 3, /// <summary>Displays a window in its most recent size and position. /// This value is similar to "ShowNormal", except the window is not /// actived.</summary> /// <remarks>See SW_SHOWNOACTIVATE</remarks> ShowNormalNoActivate = 4, /// <summary>Activates the window and displays it in its current size /// and position.</summary> /// <remarks>See SW_SHOW</remarks> Show = 5, /// <summary>Minimizes the specified window and activates the next /// top-level window in the Z order.</summary> /// <remarks>See SW_MINIMIZE</remarks> Minimize = 6, /// <summary>Displays the window as a minimized window. This value is /// similar to "ShowMinimized", except the window is not activated.</summary> /// <remarks>See SW_SHOWMINNOACTIVE</remarks> ShowMinNoActivate = 7, /// <summary>Displays the window in its current size and position. This /// value is similar to "Show", except the window is not activated.</summary> /// <remarks>See SW_SHOWNA</remarks> ShowNoActivate = 8, /// <summary>Activates and displays the window. If the window is /// minimized or maximized, the system restores it to its original size /// and position. An application should specify this flag when restoring /// a minimized window.</summary> /// <remarks>See SW_RESTORE</remarks> Restore = 9, /// <summary>Sets the show state based on the SW_ value specified in the /// STARTUPINFO structure passed to the CreateProcess function by the /// program that started the application.</summary> /// <remarks>See SW_SHOWDEFAULT</remarks> ShowDefault = 10, /// <summary>Windows 2000/XP: Minimizes a window, even if the thread /// that owns the window is hung. This flag should only be used when /// minimizing windows from a different thread.</summary> /// <remarks>See SW_FORCEMINIMIZE</remarks> ForceMinimized = 11 } /// <summary> /// Handles the Click event of the button1 control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> private void button1_Click(object sender, EventArgs e) { AttachWindow(); } /// <summary> /// Handles the Click event of the button1 control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> private void button2_Click(object sender, EventArgs e) { Clear(); } private void Clear() { if (_childProcessPlayer == null) return; if (_childProcessPlayer.HasExited) { _childProcessPlayer = null; return; } _childProcessPlayer.Kill(); _childProcessPlayer = null; } private void AttachWindow() { // do it only once per test. if (_childProcessPlayer != null) return; // Instance of the remote process to start. //_childProcessPlayer = Process.GetProcessesByName("notepad").FirstOrDefault(); _childProcessPlayer = new Process { StartInfo = { FileName = @"C:\Windows\System32\notepad.exe", //CreateNoWindow = true, UseShellExecute = true, WindowStyle = ProcessWindowStyle.Minimized }, EnableRaisingEvents = true }; Cursor.Current = Cursors.Default; _childProcessPlayer.Start(); Cursor.Current = Cursors.Default; _childProcessPlayer.WaitForInputIdle(); Cursor.Current = Cursors.Default; ShowWindow(_childProcessPlayer.MainWindowHandle, (int)WindowShowStyle.Hide); // Get process window handle. var mainWindowHandle = _childProcessPlayer.MainWindowHandle; // To prevent focus steal when SetParent is called I need to add WS_CHILD to the style. uint windowLong = GetWindowLong( mainWindowHandle, (int) WindowLongFlags.GWL_STYLE); // add ws_child windowLong |= WS_CHILD; // remove pop_up (most cases this is not necessary as it is already unset) windowLong &= ~WS_POPUP; // modify the style. SetWindowLong( mainWindowHandle, (int)WindowLongFlags.GWL_STYLE, windowLong); // Disable panel to prevent focus being stolen. (necessary in some cases) panel1.Enabled = false; // Execute Set parent. SetParent(mainWindowHandle, panel1.Handle); // Restore child state in order to allow editing in the notepad. windowLong &= ~WS_CHILD; SetWindowLong( mainWindowHandle, (int)WindowLongFlags.GWL_STYLE, windowLong); // Hide panel while notepad is resized. panel1.Visible = false; // Show notepad so resizing work ShowWindow(_childProcessPlayer.MainWindowHandle, (int)WindowShowStyle.ShowNormal); // Resize and move the window to the panel size. MoveWindow(mainWindowHandle, 0, 0, panel1.Width, panel1.Height, true); panel1.Visible = true; panel1.Enabled = true; } } }
我提供完整代码供相关开发者参考,当然我无法控制notepad.exe的源码。这份代码和我原应用使用的逻辑高度近似,由于未知原因直接隐藏启动notepad无法正常运行,因此我选择启动后再隐藏,避免嵌入panel时出现闪烁。
已尝试方案
- 复用已启动的notepad进程不会出现该问题。
- 在Task或其他线程中调用Process.Start无效果。
- 直接在Windows系统中运行程序也会出现沙漏,说明该效果可能需要子进程自行关闭,这也意味着本次用notepad做的测试示例无法复现修复效果。
问题
最终问询:调用Process.Start启动子应用时,是否可以避免鼠标出现沙漏图标?
最坏情况我可以通过Windows注册表关闭沙漏图标(我暂不清楚具体方法),但这是我的最终备选方案。
编辑补充
我还尝试过在调用Process.Start前强制设置鼠标图标,我原本以为该方案可行,因为设置手形等非指针图标时有效,但设置为默认箭头时无法生效,代码如下:
Cursor cr = new Cursor(Cursors.Arrow.Handle); Icon ico = Icon.FromHandle(cr.Handle); cr = new Cursor(ico.Handle); Cursor.Current = cr; _childProcessPlayer.Start(); _childProcessPlayer.WaitForInputIdle(); Cursor.Current = Cursors.Default;
内容的提问来源于stack exchange,提问作者Guillermo Alías
相关产品推荐
相关产品推荐

