12

For any given window I handle, I need a way to find out whether or not the given window is Modal.

Far as I can tell, there are no methods that do exactly that, which is why I need some clever workaround to work this out!

Help is appreciated!

EDIT : Why is my GetWindow(,GW_OWNER) failing? :(

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
    [DllImport("user32.dll", ExactSpelling = true)]
    internal static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestor_Flags gaFlags);
    [DllImport("user32.dll", SetLastError = false)]
    internal static extern IntPtr GetDesktopWindow();
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    const UInt32 WS_DISABLED = 0x8000000;


    internal enum GetAncestor_Flags
    {
        GetParent = 1,
        GetRoot = 2,
        GetRootOwner = 3
    }

    internal enum GetWindow_Cmd : uint
    {
        GW_HWNDFIRST = 0,
        GW_HWNDLAST = 1,
        GW_HWNDNEXT = 2,
        GW_HWNDPREV = 3,
        GW_OWNER = 4,
        GW_CHILD = 5,
        GW_ENABLEDPOPUP = 6
    }



IntPtr _inspHwnd = FindWindow("rctrl_renwnd32", inspector.Caption); // searching for a window with this name
        if (_inspHwnd.ToInt32() != 0) // found window with this name
        {
            IntPtr _ownerHwnd = GetWindow(_inspHwnd, GetWindow_Cmd.GW_OWNER);
            if (_ownerHwnd.ToInt32() != 0)
            {
                IntPtr _ancestorHwnd = GetAncestor(_ownerHwnd, GetAncestor_Flags.GetParent);
                if (_ancestorHwnd == GetDesktopWindow())
                {
                    if (GetWindowLong(_ancestorHwnd, -16) == WS_DISABLED) 
                    { 
                        // inspector is probably modal if you got all the way here
                        MessageBox.Show("modal flag tripped");
                    }
                }
            }
        }
CodeMinion
  • 653
  • 2
  • 10
  • 24
  • Just thinking aloud here: 1) Look for its parent 2) See if the parent window can be activated – BoltClock Apr 25 '11 at 23:06
  • hello boltclock, my understanding of window properties is limited, so I want to ask, what does being 'activated' mean within this context. is it like being visible or checking if it exists? – CodeMinion Apr 25 '11 at 23:08
  • It basically means check if you can bring the window to focus (normally by clicking it or Alt+Tabbing to it). – BoltClock Apr 25 '11 at 23:15
  • On non-modal inspectors it seems as if 'parent' is not equal to the main body of outlook, so I'm not sure if this method will work. – CodeMinion Apr 25 '11 at 23:42
  • Wrong approach. If you find the *particular* window you are looking for then whether it is modal or not doesn't matter. – Hans Passant Apr 26 '11 at 00:21
  • the goal is to discover modal windows and replace them with non-modal windows as a workaround to the blank pane bug detailed here : http://stackoverflow.com/questions/5723444/need-help-with-very-specific-issue-with-vsto-outlook-2007-addin-running-on-window – CodeMinion Apr 26 '11 at 01:44

3 Answers3

10

Modal windows usually work by disabling their owner, where the owner is a top-level window. So if you test for this situation, you should catch whether a dialog is modal or not.

  • Check that the HWND is actually a top-level dialog, and not a child window
  • Get the owner (GetWindow(GW_OWNER))
  • Check that the owner is itself a top-level window (eg. GetAncestor(GA_PARENT)==GetDesktopWindow())
  • Check that the owner is disabled (GetWindowLong(GWL_STYLE) & WS_DISABLED)

That should catch all standard Win32-style modal dialogs.

Note that parent and owner are subtly different concepts; it's the owner you want to check here. This can get confusing, because GetParent can return the owner... - more details from Raymond Chen here.

4LegsDrivenCat
  • 1,247
  • 1
  • 15
  • 24
BrendanMcK
  • 14,252
  • 45
  • 54
  • I'm going to flag this as the answer, but I don't know win API well enough to know how to actually put your suggestions into action :( Anyone have a good page for me to learn that from? – CodeMinion Apr 26 '11 at 02:24
  • Ok, I have almost everything working now thanks to pinvoke.net, but the GWL_STYLE bit confuses me, is that supposed to be a constant UInt32? – CodeMinion Apr 26 '11 at 04:54
  • It's a plain int (or Int32), value -16 - more details on MSDN http://msdn.microsoft.com/en-us/library/ms633584(v=vs.85).aspx . In this case, can also use IsWindowEnabled ( http://msdn.microsoft.com/en-us/library/ms646303(v=vs.85).aspx ) – BrendanMcK Apr 26 '11 at 05:05
  • thanks, brandon! just one issue left to iron out! my GetWindow function seems to be returning 0! i'll add some code to my question incase i'm doing something wrong. – CodeMinion Apr 26 '11 at 05:11
  • Have you used Spy++ to check that this is how the modals work in this particular case? The above answer is how modals are typically done in win32 apps; but it's always possible that Outlook is doing its own thing. Spy++ is a great tool for figuring out what's going on before writing code. Another thing to watch for - don't compare the output of GetWindowLong directly with WS_DISABLED; GetWindowLong returns a bitfield, so there could be other bits set. – BrendanMcK Apr 26 '11 at 05:44
  • Believe it or not, this is my first time hearing about Spy++! It does seem as if modal windows launched from MS Word have a different owner/parent than the Outlook Explorer window. In stepping through the code I noticed that the parent was just "Outlook", not referencing any open Explorer window in particular. Maybe the background process? I can also just get the Outlook.Explorer's handle at the start of the program and store it, and then check and see if it is disabled when an inspector is created. But how do I check for WS_DISABLED if not by comparison? – CodeMinion Apr 26 '11 at 21:46
  • Easy bit first: Checking for WS_DISABLED: the usual way for checking for one bit set in a bitfield is to use the & operator: if((GetWindowLong(hwnd,GWL_STYLE) & WS_DISABLED) != 0) { disabled case } - bitwise-& strips all the bits except for the one you're checking; so if the result is non-zero, it means WS_DISABLED must have been present on the left hand side. (In C/C++, the !=0 part is usually left out, but I think it's needed in C#.) – BrendanMcK Apr 27 '11 at 04:12
  • Parents vs Owners: Spy++ isn't exactly correct: if the window has an owner, it seems to display that as the 'parent' also. The is likely due to a quirk in the underlying API 'GetParent()' - which returns the parent window; or the owner, if the window has an owner instead. So you have to manually figure out if it's really talking about a parent vs owner. Usually the window's position in the window tree in Spy++ gives it away easily. – BrendanMcK Apr 27 '11 at 04:15
  • The parent/owner has to be an actual HWND, it can't just be a process in general; though sometimes apps will use a technique where the have a dialog that is owned by an invisible window: this prevents the dialog from showing up on the task bar. When using Spy++, take note of the handle values. In the window properties dialog, you can click through to get information about that window - check the styles for WS_VISIBLE to see if it's hidden or not; if you click the Synchronize button at bottom of dialog, it will highlight that window in the tree. – BrendanMcK Apr 27 '11 at 04:18
3

I'm not certain that BrendanMck's solution will always be correct. Let's say that window W displays first a modeless dialog A and then a modal dialog B. Both A and B have W as their parent window. At the time B was displayed, W became disabled and as such applying the algorithm to both A and B will report both of them as being modal dialogs.

eugen_nw
  • 147
  • 7
-2

I just wrote GetWindowLong(GetWindow(Hwnd, GW_OWNER), GWL_STYLE) & WS_DISABLED & WS_POPUP in my code.