Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#
Tip/Trick

Windows Resize and Move

Rate me:
Please Sign up or sign in to vote.
5.00/5 (19 votes)
8 May 2019GPL36 min read 48.9K   1.8K   30   20
Automatic positioning and resizing of explorer windows, without overlapping (for obsessive superusers)

Introduction

In Windows 7, when I open WindowsExplorer (not Internet Explorer) to see and manage my files, upon opening a new window, it remembers the last closed windows size, and it usually opens in screen position overlapping other explorer windows:

I wanted to open explorer windows always the same size, and never partially/completely hiding other explorer windows:

After searching (with no luck) for some Windows7 options to enable these features, one rainy Saturday I started developing this. (BTW: if you happen to know how to do the same thing in a better/faster way (i.e. some windows settings or some existing utility), I would like to know it, thanks.)

Background

It is a simple program that uses:

  • user32.dll functions to manage desktop windows
  • a form overriding the WndProc to manage windows messages and to set a shell hook for the Creation/Activation/Destruction of a window (although only the creation event will be used here; the other are for 'future use')
  • a Mutex to allow only one running instance of the application
  • XML serialization of the config object, that will be written to and read from the hard disk (in the same folder of the application)

Using the Code

The code is structured into 4 files, herein below described, starting from the least important:

  • WindowsApi.cs contains only DllImports to Windows API, in particular: user32.dll; this one doesn't need any further explanation
  • XmlHelper.cs is a very simple bare-bone abstract class for XML serialization. By inheriting this one, a class can be saved as an XML file, without further effort
  • Config.cs contains the configuration that is read/written to disk
  • WindowsHookForm.cs is a form that is never shown to the user; it is provided only to use the from's WndProc, that allows to easily hook to Windows shell events and consequently do something when a window is opened.

    It provides three events: WindowCreatedEvent, WindowActivatedEvent and WindowDestroyedEvent, but only the first one is actually used in the program.

    In the constructor, a hook is registered:

    C#
    private readonly int windowMessage_ShellHook;        
    public WindowsHookForm()
        {
        windowMessage_ShellHook = WindowsApi.RegisterWindowMessage("SHELLHOOK");
        WindowsApi.RegisterShellHookWindow(this.Handle);
        }

    then, in the WndProc, messages are intercepted and, if the messageID is our shellhook, then an event is risen (but only for the three kinds mentioned above):

    C#
    public event Action<IntPtr> WindowCreatedEvent;  
    public event Action<IntPtr> WindowActivatedEvent;
    public event Action<IntPtr> WindowDestroyedEvent;
    
    protected override void WndProc(ref Message message)
        {
        if(message.Msg == windowMessage_ShellHook)
            {
            WindowsApi.ShellEvents shellEvent = (WindowsApi.ShellEvents)message.WParam.ToInt32();
            IntPtr windowHandle = message.LParam;
    
            switch(shellEvent)
                {
                case WindowsApi.ShellEvents.HSHELL_WINDOWCREATED:
                    if(WindowCreatedEvent != null)
                        WindowCreatedEvent(windowHandle);
                    break;
                case WindowsApi.ShellEvents.HSHELL_WINDOWACTIVATED:
                    if(WindowActivatedEvent != null)
                        WindowActivatedEvent(windowHandle);
                    break;
                case WindowsApi.ShellEvents.HSHELL_WINDOWDESTROYED:
                    if(WindowDestroyedEvent != null)
                        WindowDestroyedEvent(windowHandle);
                    break;
                }
            }
        base.WndProc(ref message);
        }
  • Program.cs contains the main logic of the program:

Upon start, a Mutex allows only one instance of the program to be run and, if an instance is already running, it kindly asks if you want to terminate it (and in this case, it calls the RemoveAllInstancesFromMemory method, that gets the running processes with the "WindowsMover" name and kills 'em).

Once started, three actions are performed: first the config file (if it exists) is read (and if it does not exist, then it is written with default values, so that you may find it easy to edit); then, in the ArrangeExplorerWindows method, every existing explorer window on the desktop is arranged (i.e. resized and moved); and finally the resident part is started, where the WindowsHookForm and the WindowCreatedEvent defined in it are used:

C#
WindowsHookForm whf = new WindowsHookForm();
whf.WindowCreatedEvent += (data) => { ArrangeOneWindow(data); };

Every time we open a new window, the ArrangeOneWindow method receives the handle to the new window that has just been created; then it checks if it is an Explorer window:

C#
private static void ArrangeOneWindow(IntPtr newhandle)
    {
    List<windowInfo> wlist = GetActiveExplorerWindowsInfo();
    windowInfo newWindow = (from windowInfo wi in wlist
                            where wi.handle == newhandle
                            select wi).FirstOrDefault();
    if (newWindow != null)

and resizes and (optionally) moves that window, using the WindowsApi functions to do the work, and the config data to know how to proceed.

Basically, any time an explorer window is opened, the program searches a position to put it, starting at the top left corner of the desktop and then moving right (like in a row) until a suitable place is found; once the right of the desktop is reached, the same search is repeated in the row below. If too many explorer windows are open on the desktop, and there is no empty place to put the new opening one, then nothing is done, and the new window will be left where Windows7 decided to put it.

The Config File

XML
<width>346</width>
<height>460</height>
<startLeft>0</startLeft>
<startTop>0</startTop>
<left>0</left>
<top>0</top>
<horizontalGap>0</horizontalGap>
<verticalGap>0</verticalGap>
<doResizeOnly>false</doResizeOnly>
<canArrangeFixedPositions>true</canArrangeFixedPositions>
<canAppendToNext>false</canAppendToNext>
<ExcludedWindowsTitles>
  <string>Control Panel\All Control Panel Items\Personalization</string>
  <string>Shut Down Windows</string>
</ExcludedWindowsTitles>
<logFileName>WindowsMover.log</logFileName>
<doLogFile>false</doLogFile>
  • width and height are the default resizing dimensions for the resized windows. You know that explorer always uses the last closed window size as default for the new one, so these values in config file override the ones set by explorer.
  • The two gap values should be the distance between the arranged windows.
  • doResizeOnly if true does not move the windows.
  • canArrangeFixedPositions allows the search of a suitable position even if there is no free place available next to an existing window (it maps the windows as if it was made of tiles starting at startLeft and startRight, with the defaut width and height set in the first lines of the config file; then it searches if one of this tiles is free)
  • canAppendToNext if true it first tries to append the new window to the right of an existing explorer window.
  • ExcludedWindowsTitles is a list of explorer window titles the we don't want to be arranged
  • doLogFile if true, a logFileName is written for every arranged window (mainly for debug purposes)

And that's it. Structurally, it is very simple and it could give some interesting insights to the newbie programmer.

Update

After receiving a couple of requests, I've changed the program so that it can resize and move every kind of window, not only explorer windows (So I changed also the title of the article). To allow this feature, I've added a new field in the config file:

XML
<ProcessesToWatch>
  <string>explorer</string>
</ProcessesToWatch>

It is a list of strings and you may replace 'explorer' with something else, or just add the name of a new process to be watched.

For example, the following xml in the config file:

XML
<ProcessesToWatch> 
  <string>explorer</string> 
  <string>notepad</string> 
</ProcessesToWatch>

will make the program watch for windows opened by both explorer and notepad, and it will resize/move them.

If unsure about the process name to use in the list, change temporarly the following line in the config file:

XML
<doLogFile>false</doLogFile>

to true, and, upon start, in the log file there will be a list of all the active processes names, where the needed one can be picked.

Update 2019

1) Made minor changes to better adapt this program to Windows10 (it was about time...)

2) In config file added support for a wildcard: '*' (but only at the end of a name), in tag <ExcludedWindowsTitles>
Example:    

<ExcludedWindowsTitles>
    <string>Control Panel\*</string>

3) Added a new boolean key in configuration file:

<enableKeyboardShortcutsToArrangeWindows>

When this key is true (which is also the default), then by pressing the combination of keys:

 CTRL+SHIFT+ALT+A

all defined windows (which by default are all Explorer windows) are rearranged and shown on top of every other window.

To intercept the key combination, a new hook (similar to the one used to intercept the windows messages, already used in the file 'WindowsHookForm.cs') has been used:

public static bool RegisterKeyHandler(Form form, int key, int mod = 0) => WindowsApi.RegisterHotKey(form.Handle, mod ^ key ^ form.Handle.ToInt32(), mod, key);

which is used in a manner similar to this:

RegisterKeyHandler(this, (int)Keys.A, ALT + CTRL + SHIFT);

Attached files (src + exe) have been updated.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
Switzerland Switzerland
C#, SQL (and in the past also: C++, C, VBA)

Comments and Discussions

 
QuestionMessage Closed Pin
23-Oct-21 8:14
owo its Mr donut girl23-Oct-21 8:14 
QuestionModify code to resize and move other app windows Pin
gorgorybmus19-Apr-20 1:59
gorgorybmus19-Apr-20 1:59 
AnswerRe: Modify code to resize and move other app windows Pin
Bruno Tabbia13-Jun-20 3:27
Bruno Tabbia13-Jun-20 3:27 
QuestionHow to modify this code if using two monitor? Pin
seekhelp5-Dec-19 21:13
seekhelp5-Dec-19 21:13 
AnswerRe: How to modify this code if using two monitor? Pin
Bruno Tabbia13-Jun-20 3:31
Bruno Tabbia13-Jun-20 3:31 
QuestionWindows 7? Does this also work for today's OS (Windows 10) ? Pin
tlhIn`toq10-May-19 8:02
tlhIn`toq10-May-19 8:02 
AnswerRe: Windows 7? Does this also work for today's OS (Windows 10) ? Pin
Bruno Tabbia12-May-19 1:09
Bruno Tabbia12-May-19 1:09 
QuestionCan create visual button to replace W+arrow ? Pin
help2ray4-Jun-16 6:05
help2ray4-Jun-16 6:05 
QuestionCan be custom size & position for notepad ? Pin
help2ray2-Mar-16 2:56
help2ray2-Mar-16 2:56 
Questionlet notepad Pin
help2ray19-Feb-16 5:26
help2ray19-Feb-16 5:26 
AnswerRe: let notepad Pin
Bruno Tabbia20-Feb-16 0:45
Bruno Tabbia20-Feb-16 0:45 
PraiseRe: let notepad Pin
help2ray20-Feb-16 1:46
help2ray20-Feb-16 1:46 
PraiseRe: let notepad Pin
help2ray20-Feb-16 1:46
help2ray20-Feb-16 1:46 
GeneralRe: let notepad Pin
Bruno Tabbia20-Feb-16 1:59
Bruno Tabbia20-Feb-16 1:59 
PraiseRe: let notepad Pin
help2ray2-Mar-16 2:51
help2ray2-Mar-16 2:51 
QuestionAuto resize Windows to create a grid Pin
CarlosOnline15-Feb-16 6:48
CarlosOnline15-Feb-16 6:48 
AnswerRe: Auto resize Windows to create a grid Pin
Bruno Tabbia15-Feb-16 9:44
Bruno Tabbia15-Feb-16 9:44 
AnswerRe: Auto resize Windows to create a grid Pin
Bruno Tabbia20-Feb-16 10:22
Bruno Tabbia20-Feb-16 10:22 
QuestionTaskbar Pin
Frits van Veen15-Feb-16 1:56
Frits van Veen15-Feb-16 1:56 
AnswerRe: Taskbar Pin
Bruno Tabbia15-Feb-16 10:01
Bruno Tabbia15-Feb-16 10:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.