如何锁定外部应用程序的大小和位置?

编程语言 2026-07-11

我的目标是在我自己的WinForms应用程序中打开外部应用,然后把它们的大小和位置锁定在我在程序中设定的数值上。我已经能够让它们在我的应用程序内打开,实际上是在我的WinForm的边框内,但问题是,外部程序仍然可以被调整大小和重新定位。

我写这个程序的原因,是因为我在不同的项目之间工作不太一致,所以我总得记住我给哪个项目用的是哪个工具,等我再处理时还要把它们一个个摆好。就像一个有多台显示器的安保室——一旦我打开一个程序,我希望它的位置和大小被锁定,直到我完成并关闭它为止,这样它就不会动。

我唯一头疼的问题,是如何锁定我程序打开的这些外部应用的大小和位置。比如说,如果我打开Microsoft Paint(画图),我希望把它的大小锁定为大约200 × 600的尺寸,无论我怎么点击、拖动,它都一丝不动。

有人知道该怎么做吗?我一直在查找指南或文档,但问题是因为我是自学成才,不太清楚正确的术语,遇到了点阻碍。

另外需要澄清的是,我不是在问如何调整WinForm应用本身的大小,这部分我已经处理好了,我指的是我程序在内部打开的外部应用。

有什么建议吗?

解决方案

有很多方式可以实现,尽管Windows本身没有开箱即用的函数来把窗口锁定在原位。

注:所有这些方法都需要一个 IntPtr(HWND),可以通过 Form.Handle 属性获取,或者通过从Windows API得到的 HWND / HANDLE 来获得,它们指向你外部应用中的那个窗口。

最可靠、最简单的方法是修改窗口边框样式并移除最小化按钮。

public static void LockResize(IntPtr hWnd, Rectangle desiredBounds)
{
    Form form = (Form)Form.FromHandle(hWnd)!;

    form.FormBorderStyle = FormBorderStyle.FixedSingle;
    form.MaximizeBox = false;
    form.Bounds = desiredBounds;
}

下一种方法是在完成调整大小后把窗口位置重新吸附回去。我在一些旧应用中见过这种做法。有人可能会认为这种体验不佳,所以请评估一下在你的用例中是否可靠。

public static void LockResize(IntPtr hWnd, Rectangle desiredBounds)
{
    Form form = (Form)Form.FromHandle(hWnd)
        ?? throw new InvalidOperationException("Could not get form by handle...");

    form.Resize += (s, e) =>
    {
        if (form.InvokeRequired)
        {
            form.Invoke((MethodInvoker)(() =>
            {
                form.Bounds = desiredBounds;
            }));
        }
        else
        {
            form.Bounds = desiredBounds;
        }
    };
}

另一种方法是暴力法,持续不断地改变窗口大小。也就是说在你真正尝试调整大小时,窗口会来回快速调整,但最终会把它锁定在原位。

private const int Interval = 25; //25ms

public static Thread LockResize(IntPtr hWnd, Rectangle desiredBounds)
{
    var thread = new Thread(() =>
    {
        try
        {
            Form form = (Form)Form.FromHandle(hWnd)!;

            while (true)
            {
                if (form.InvokeRequired)
                {
                    form.Invoke((MethodInvoker)(() =>
                    {
                        form.Bounds = desiredBounds;
                    }));
                }
                else
                {
                    form.Bounds = desiredBounds;
                }

                Thread.Sleep(Interval);
            };
        }
        catch
        {
        }
    });

    thread.Start();
    return thread;
}

这种方法会创建一个后台线程。请注意,在应用程序自然退出之前(例如所有窗口都关闭时),你需要手动对返回值的 Thread 调用 Interrupt() 方法,否则它会一直在后台驻留。

var thread = LockResize(hwnd, bounds);
//...
thread.Interrupt();

你也可以修改 Interval 常量来让调整大小的速度更慢或更快,但使用低于 ~3的值可能会让操作系统因高开销而变慢。

顺便提醒一下:这些方法在WinUI/UWP应用(如计算器或声音记录器)以及最近改用WinUI的 Microsoft Paint上,可能不能按你预期工作,因为这些应用的实际窗口在不同的进程中——称为 ApplicationFrameHost.exe——因此你需要从那里获取窗口句柄。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章