Weekend Scripter: Manage Window Placement by Using PInvoke


Summary: Boe Prox shows us how to manage window placement in Windows PowerShell.

Honorary Scripting Guy and Cloud and Datacenter Management MVP, Boe Prox, here today filling in for my good friend, the Scripting Guy.

Today I start a little weekend fun by using a technique known as platform invoke, or PInvoke for short. What is PInvoke, you ask? Well, PInvoke allows us to utilize the low-level Windows API to call functions that would normally not be available to us natively.

This allows us to do some pretty cool things, such as looking at files with a file path that is greater than the MAX_PATH allowed when using normal means, such as Get-ChildItem—or even with privileges in PowerShell. Of course, all of this sounds nice and fun, but how do we actually accomplish something like this?

We have a few options to make this happen:

  • Use reflection to define the methods needed to call the API
  • Use a private reference
  • Find signatures online that are written in C# and copy them to compile into a usable type that we can use in PowerShell (the focus of this post)

I feel the easiest approach to working with PInvoke using PowerShell is looking for the proper PInvoke signatures needed to accomplish my goal. And what is my goal today? It is to show how we can find the X and Y coordinates of a window by using its process and its size. Then I’ll take that one step further by actually moving the window to another location on the screen. It may not be the most useful thing to cross the hallowed halls of this blog, but it is something cool never-the-less!

Find the position of a window

I already know the name of the function that I have to use if I want to locate the position of the window, and it is named GetWindowRect. The next stop on my journey to find the signature I can use. The site of choice that you should be using to find these PInvoke signatures is PINVOKE.NET. Seriously, this site and MSDN are your one stop shops for finding everything you need to know for working with PInvoke.

I simply type the name of the function that I want to look for and search for it. AfterI click the results that match what I am looking for, I am presented with the information that I need, for example:

Image of menu

Here you can see the C# signature that I will copy and paste into a here-string to use in PowerShell. I am not actually done yet because I need to provide a RECT object that is the output of running this function. I quickly look for that struct (thankfully there was a link on this page to make it easier to locate), and I also paste that to my here-string:

Add-Type @"

    using System;

    using System.Runtime.InteropServices;

    public class Window {

    [DllImport("user32.dll")]

    [return: MarshalAs(UnmanagedType.Bool)]

    public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

    }

    public struct RECT

    {

    public int Left;        // x position of upper-left corner

    public int Top;         // y position of upper-left corner

    public int Right;       // x position of lower-right corner

    public int Bottom;      // y position of lower-right corner

    }

"@

Running this will compile the C# code that we can use to find more information about our window.

Image of command output

I covered the RECT parameter, but the other parameter is looking for the handle of a process that has to have a visual window on the screen. We can pull that information by looking at the MainWindowHandle property when calling Get-Process:

(Get-Process -Name PowerShell).MainWindowHandle

Image of command output

Putting this together, we create our RECT object that will be used as our ref object and pull back some information about our PowerShell window:

$Handle = (Get-Process -Name PowerShell).MainWindowHandle

$Rectangle = New-Object RECT

[Window]::GetWindowRect($Handle,[ref]$Rectangle)

Image of command output

The return value of $True tells us that this this was successful, and we can now view the contents of $Rectangle to see the results. The numbers may be a little confusing to look at, but it is simply looking at the top left (Left, Top) and the bottom right (Bottom, Right) of the window and the distance from the screen on those respective sides. The following image shows a visualization of this:

Image of window

Something like this just screams to be a function, right? Well, that is what I though too when I wrote Get-Window which provides this information and the size of the window. You can download it from the Script Center Repository and give it a run for yourself: Get the position of a window. Here is quick example of it in action against the PowerShell process:

Get-Process powershell | Get-Window

Image of command output

Set a window to a new location

I couldn’t have a Get without some sort of Set function, so I also wrote a Set-Window. It has the GetWindowRect PInvoke signature, and it also uses the MoveWindow signature, which allows me to redraw the window in a different location and change its size, if I choose to do so. You can find the signature to use on PINVOKE.NET: MoveWindow (user32).

It is important to set the redraw parameter to $True to ensure that the window is redrawn in its new location. If I don’t do this, the window will appear to be in its current location, but it definitely won’t actually “be there.”

I can use the RECT struct again to find my window’s current location and size, and then I can make use of that information when moving the window—especially if I choose not to change the size of the window. The end result is a function that I can use to move and resize a window. Here is a quick demo of this in action:

Get-Process powershell |

Set-Window -X 900 -Y 142 –Passthru

By specifying the –Passthru parameter, I can see the new coordinates and size of the window:

Image of command output

That is all there is today for working with PInvoke using PowerShell. Tomorrow, I will show another example of how we can utilize this to deal with stubborn files that are buried deep in the file system.

I invite you to follow the Scripting Guy on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!

Boe Prox, Microsoft Cloud and Datacenter Management MVP and Honorary Scripting Guy 

Comments (3)

  1. The images are somehow broken.
    Nice content - Greetings

  2. Markus Heinz says:

    I often have this problem, that pictures are missing. It seems to work only once for a page, that the pictures are shown. Very annoying.

    Beside of this: the content is really great

  3. s.mcknight says:

    Very nice post. Many Thanks.

Skip to main content