0

What I'm aiming to do, is to open external applications within my own WinForms application, then lock their size and position to whatever I've set it to in my program. I've been able to get them to open within my application, so it's actually within my WinForm's borders but the problem is, you can still resize and reposition the external programs.

The reason I'm making this program, is because I work on different projects inconsistently, so I always have to remember which tool I used for what project, then I have to line them all up manually when I work on them again. Imagine like a security room with multiple monitors- once I open a program, I want it's position and size locked so it doesn't budge until I close it when I'm done with it.

The only part I'm struggling with, is locking the size and position of these external applications my program opens. For example, if I were to open Microsoft Paint, I want to lock it's size to something like 200 x 600 and no matter how much I click and drag, that window doesn't budge an inch.

Does anyone know how to go about this? I've been trying to look up guides or at documents, but the problem is I don't know the correct terminology due to being self-taught, so I'm hitting a bit of a roadblock here.

Also to clarify, I'm not asking about resizing the Winform application itself, I've got that sorted, I just mean the external applications my program opens within itself.

Any advice?

7
  • Related. Commented Mar 12 at 12:06
  • 1
    It sounds like you want snapping which is already available on Windows and customizable by Windows. Or utilities like Windows PowerToys Workspaces or FancyZones, which work on top of Windows features. You can't just lock another application's location and size. SetWindowsPos isn't enough - that API goes back to Windows 9x and doesn't handle the newer features Commented Mar 12 at 12:09
  • 3
    The PowerToys code is hosted on Github. You can check the code of eg the WorkspacesWindowArranger class or WindowsCreationHandler. Not only do they modify other app windows (using SetWindowPlacement, SetWindowsPos) but also listen for show/hide events so they can modify those "new" windows Commented Mar 12 at 12:13
  • I'm not sure what you mean by "open external applications within my own WinForms application". Windows have APIs for interacting with windows of other applications, but not all APIs are available in .Net So you will need to be very comfortable with the win32 API and P/Invoke if you want to do this yourself. I also imagine that it will be quite difficult to make this both easy to use and reliable. Commented Mar 12 at 12:32
  • I suggest changing the title to "How do I set the size and position of an external application's window?" or "How do I prevent an external application's main window from being moved or resized?" (These seem like different problems to me as well.) Commented Mar 13 at 17:56

2 Answers 2

0

There are many ways to do this, though Windows doesn't have a function out of the box to lock the window in place.

Note: All these methods require an IntPtr (HWND), you can obtain it using the Form.Handle property, or, through HWND/HANDLE you get from the Windows APIs, which refer to the window in your external application.

The most reliable and simple approach would be changing the window form border style and removing the minimize box.

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

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

The next approach would be snapping the window positions back after it's done resizing. I've seen this approach in some older applications. Some people may consider this experience poor, so please review this approach to ensure it's reliable for your use case.

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;
        }
    };
}

Another approach would be a bruteforce method, that constantly changes the window size. This does mean that the window will be rapidly resizing back and forth when you actually try resizing, however, it does eventually lock it in place.

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;
}

This approach spawns a background thread. Note that you'll have to manually invoke the Interrupt() method on the returned Thread before your application exits naturally (e.g., when all windows are closed), otherwise it will just linger in the background.

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

You can also change the Interval constant to make resizes slower or faster, but using a value below ~3 risks slowing down the operating system from intense overhead.

Just a slight heads up: these approaches may not work as you expect with WinUI/UWP apps like Calculator or Sound Recorder, including Microsoft Paint (which has gone WinUI recently), because the actual window of these apps is in a different process - called ApplicationFrameHost.exe - so you'll have to get the window handle from there.

Sign up to request clarification or add additional context in comments.

Comments

-1

I assume you have managed to bring the main window of the external program into your application by setting its parent to the handle of a container cotrol with SetWindoLong(..).
If so, sending a WM_MAXIMIZE-Message to this window should result in aligning it to the client area of the container control.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.