You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启动进程时是否可以隐藏鼠标沙漏忙状态指示器?

背景

我正在开发一款大量使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 11:15:03