Click here to Skip to main content
16,016,882 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
I am re-posting my question from Stack Overflow here for more exposure.



I need to move several external application windows at the same time (together) as a group to simulate scrolling functionality.

If your first thought is "why would you want to do that" or "you should never do that", just pretend that it's a good idea and would be fantastic.

So far I have tried the PInvoke methods MoveWindow, SetWindowPos, and (BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos).

All of these methods produce about the same performance. Moving one window is super snappy, two windows is no big deal, but once I try to move three or more windows things start to get ugly. Here is a snippet using the deferred method to show you how I am doing things and keeping track of performance.
C#
private void multiMoveWindows(int moveAmt, int startingIndex)
{
    int tempX;
    int howmanyTimes = 0;
    int numOfWidgetsToMove = 0;
    int moveAmtRemaining = moveAmt;
    IntPtr MultiWindowStructure;
    System.Diagnostics.Stopwatch t = new Stopwatch();            
    WidgetTracker[] tmp_WhichWidgets = new WidgetTracker[10];           

    foreach (KeyValuePair<string, WidgetTracker> entry in m_dictWT)
    {
        WidgetTracker tmp = new WidgetTracker();
        tmp = entry.Value;
        if (tmp.WIndex >= startingIndex)
        {
            tmp_WhichWidgets[numOfWidgetsToMove] = tmp;
            ++numOfWidgetsToMove;
        }
    }

    for (int i = 0; i < moveAmtRemaining; i++)  
    {
    MultiWindowStructure = Native_Methods.BeginDeferWindowPos(numOfWidgetsToMove);
        foreach (WidgetTracker tmp in tmp_WhichWidgets)
        {
            if (tmp != null)
            {
                tmp.surveilWidget();
                tempX = tmp.WidgetRectLeft - 1;                    
                MultiWindowStructure = Native_Methods.DeferWindowPos(
                    MultiWindowStructure, tmp.WidgetHandle, HWND.NOTOPMOST, tempX, tmp.WidgetRectTop, tmp.WidgetWidth, tmp.WidgetHeight, SWP.NOREDRAW);
                howmanyTimes++;
            }
        }
        t.Start();
        Native_Methods.EndDeferWindowPos(MultiWindowStructure);
        t.Stop();             
    }
    MessageBox.Show(howmanyTimes.ToString() + " move requests made." + Environment.NewLine + 
        "Execution of EndDeferWindowPos took " + t.Elapsed.Seconds.ToString() + " sec " + t.Elapsed.Milliseconds.ToString() + " ms." );    
}



The result for moving 4 windows, 320px left, 1px at a time?
> 1280 move requests made in 14.445 sec.

As you can imagine, waiting 14.5 seconds to see 4 windows make their way across the screen 320 pixels is painful to watch.
- How can this be accomplished in a fluid manner?

Yes, I realize that I can move more than 1px at a time, but that won't resolve the underlying issue. The way I understand it, using any of the PInvoke methods to move windows posts messages to the WinAPI message pump (WndProc()) and waits for the message to be closed / caught / handled before posting the next message. I think this is where I am spending that 14.5 seconds - waiting for messages to be handled.

I have tried using SWP ASYNCWINDOWPOS and the result is horrendous. Each window only gets a few dozen of the messages seemingly at random so that windows are overlapping and as a whole the train barely moves.

The closest I've come to a solution is to assign each "widget" a parent (for example a WindowsFormsHost + an MDI window) and putting them in a form/window and then scrolling that form/window. However I have several reasons for not wanting to do that.
Please help!
Posted
Comments
Ron Beyer 23-Jan-14 11:27am    
Are these windows that are owned by your application, or windows that are from other software? Judging by your code you are displaying video cameras or something like a video wall, correct?
Okuma.Scott 23-Jan-14 11:57am    
The windows are processes that I have started, and they aren't owned by the application.
Their content is whatever developers for the platform can dream up.
TnTinMn 23-Jan-14 12:56pm    
Just an observation and guess.

hWndInsertAfter is set to HWND_NOTOPMOST, does setting the SWP_NOZORDER flag help the performance? It may eliminate some z-order checking and the determination if the window is topmost or not.
Okuma.Scott 23-Jan-14 13:25pm    
There is no discernible difference when using NOZORDER or NOOWNERZORDER.
TnTinMn 23-Jan-14 15:02pm    
Darn!, worth a shot though.

What is the surveilWidget method? I'm guessing that it is a call to GetWindowRect. If so,I would think that this could be called at the start of the movemovement and the position updated in your code by adding the requested movement. That would be one less interop call per window on each iteration. May not be much gain though.

Edit: Might as well set the SWP_NOSIZE and SWP_NOACTIVATE flags as well.

1 solution

1. same question Ron Beyer asked you: please define 'external application windows' in this context precisely.

2. is this a WindowsForms project ? if not, what's the context: WPF ? ... C++ ?

3. is there one-and-only-one window that if moved moved all others ? if more than one window can move all other windows, then you have possible recursion to deal with.

4. have you done anything to try and suspend internal refresh of windows being moved as a result of another window being moved ?

5. assuming only one Window can be moved and cause the other windows to "follow" it: is it a requirement that you "track" the move by the user's interaction with the TitleBar ... or, is some action in the Window okay: like click-dragging some graphic element, or click-dragging in the active window area ?

6. are you using a GlobalHook now; if so, what's the source of the code: CP ?
 
Share this answer
 
v2
Comments
Okuma.Scott 23-Jan-14 12:34pm    
1: The external apps are any executable program. For example, notepad.exe.

2: I am doing prototyping with both Winforms and WPF. attaching these external processes to parents works in both contexts but each has different flaws, which is why I am trying to do it externally using the WinAPI (which can be done easily using either context). Also the primary language I'm using is C# as you can see from the snippet.

3: No. The main problem I'm trying to solve now is this scenario: the user has 4 widgets open, and they decide to close the first one (index 0, on the left side). Now the remaining widgets need to collapse to fill the space that has opened ie. they all need to move to the left the number of pixels that has opened up. I have the logic worked out, its actually MOVING the windows that is not working as expected.

4: Not yet, although I have thought about it. "Freezing" the content of the moving windows would be acceptable but I have yet to implement the necessary messages. Therefore I don't know how it would effect the performance.

5: Refer to scenario in point 3. This is something which will come later, after I have a solution to this problem. Eventually I will need to catch move messages from title bars.

6: Refer to point 4: no, but it will be a requirement.
BillWoodruff 23-Jan-14 12:53pm    
I appreciate your detailed responses (a refreshingly rare event here on CP QA).

The issue, #3, of "collapsing to fill space" I interpret as: you are performing a type of tiling.

This is a "deep" problem requiring using the WinAPI, and it's a type of problem I've never worked on. I have created effective ... and rapid ... movement synchronization between multiple independent Forms in Win Forms, but I used mouse-down-and-drag on a UI element on the Forms, which did not require I use WinAPI.

Wish I could help you further; hopefully, with your scenario now more detailed, you'll get other helpful responses.

At the point you are ready to use a GlobalHook, I suggest you try out George Mamaladze's code here on CP:

http://www.codeproject.com/KB/cs/globalhook/globalhook2_src.zip

Here's a recent discussion of using George's code for another purpose:

http://www.codeproject.com/Answers/712448/Global-keyboard-hook-e-Handled-equals-true-does-no
Okuma.Scott 23-Jan-14 13:14pm    
You are correct about tiling.
Thank you for taking the time to read my question and the information you've provided!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900