0
votes

I am using Windows MFC to create a small program. I would like to make multiple instances of the program appear in a cascaded position(s).

Currently the program always appear centered, i.e. it is not possible to see it multiple windows.

Is there an automatic way to let windows create multiple instance in cascaded positions?

To test i use a batch script with multiple lines of:

 "start MyProgram.exe"
 "start MyProgram.exe"
 "start MyProgram.exe"

The dialogs i use are derived from CDialogEx (but i had same using CDialog)

I expected this to be a flag/properties of the dialog.

Before changing the .rc-file have properties like this

IDD_MAIN_DLG DIALOGEX 0, 0, 260, 185 STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION 

I am aware of the CascadeWindows() function, but to my knowledge it requires more awareness of which instances that already run

1
No there isn't, you have to do this on your own by MoveWindowing the dialog in the OnInitDialog method. - Jabberwocky
IMO the c++ and MFC tags should be removed since you want "windows" to do the job without modifying your program - Robson
I do not agree with Robson. I would like to configure my MFC code so that windows could handle it the position... The solution might be outside the scope of MFC though. If other people search for a MFC-solution (like me) they will find out they need to use raw WinApi. - Steffen Villadsen

1 Answers

0
votes

How about the following code as a starting point?

#include <Psapi.h>
namespace {
    size_t nWnds = 0;
    HWND hWnds[10];

    BOOL CALLBACK enumerate(HWND hWnd, LPARAM This)
    {
        HWND hWndThis = reinterpret_cast<HWND>(This);

        TCHAR nameThis[MAX_PATH], nameOther[MAX_PATH];
        VERIFY(GetWindowModuleFileName(hWndThis, nameThis, _countof(nameThis)));

        TCHAR wndclass[32];
        VERIFY(RealGetWindowClass(hWnd, wndclass, _countof(wndclass)));

        if (_tcscmp(wndclass, _T("#32770")) == 0) {
            DWORD pid;
            GetWindowThreadProcessId(hWnd, &pid);
            HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
            if (hProcess != NULL) {
                if (GetModuleFileNameEx(hProcess, NULL, nameOther, _countof(nameOther))) {
                    if (_tcscmp(nameThis, nameOther) == 0) {
                        hWnds[nWnds++] = hWnd;
                    }
                }
                VERIFY(CloseHandle(hProcess));
                hProcess = NULL;
            }
        }

        return TRUE;
    }
}

BOOL CMFCApplication1Dlg::OnInitDialog()
{
    // ...

    VERIFY(EnumWindows(enumerate, reinterpret_cast<LPARAM>(m_hWnd)));
    if (nWnds > 1) {
        VERIFY(CascadeWindows(NULL, MDITILE_ZORDER, NULL, nWnds, hWnds));
    }

    return TRUE;
}

It consists of a change to OnInitDialog to scan for all top level dialogs that have been created by your executable and then call CascadeWindows. Of course in enumerate you can also move every window you find to a point that starts at CPoint(x, y) and changes by CSize(xoffset, yoffset) with every found window.

Some things to keep in mind:

  1. CascadeWindows does not look like the right solution, it restores all maximized windows and does not only touch windows created by your process (which I would prefer).
  2. If your process creates multiple top level dialogs, then you might need to detect which dialogs to move.
  3. If the user makes a copy of your program file then the module file name will be different.
  4. Just proof of concept code, you'll need to add error checking and bounds checking.