none
problems with modal dialogs and messageboxes

    Question

  • I have a problem with opening a messagebox from a modal form. Either I'm doing it wrong or IE is getting confused with which windows should be modal with respect to which others. Can anyone tell me what I'm doing wrong or how to achieve this?

    The situation is:

    I have a BHO. On the DocumentComplete event I sometimes open a modal form. I'm using the IWebBrowser2.HWND as the owner of my form - this is the handle of the top level IE window. Depending on what the user does I might show a MessageBox from that form. My form is passed as the owner to the MessageBox function, so the messagebox should be modal with respect to my form. This works fine normally, my problem is when:

    - someone opens a link in a new tab,

    - the DocumentComplete event fires for the new tab, and the new tab isn't the currently selected one

    - my form opens modal with respect to the IE window

    - my form then opens a MessageBox.

    - IE becomes frozen, because the messagebox seems to be opened from the new tab (that tab goes orange) but that tab isn't selected. I can't select that tab because my modal form is still open. Stuck!

    I don't see why the messagebox is opening modal to the new tab, since I'm opening it from my form using MessageBox.Show( this, "blah blah" ), and since my form was opened modal using the HWND of the IWebBrowser2.

    My thoughts on how to fix this are:

    - try to set the new tab to be selected before opening my modal form.

    - use a different HWND when opening my modal form - either the HWND of the new tab (but I don't think it's possible to get this if the tab isn't active) or maybe the HWND of the opening tab - on the offchance it would work.

    Any ideas?

     

    thanks,

     

    Rory

    Wednesday, January 28, 2009 2:04 PM

Answers

  • Playing around some more with Spy++ and the handles it exposes, I find that if I use a different hwnd then I can make my window modal to the tab:

    I was using the tab's hwnd as suggested above, which in Spy++ appears as class TabWindowClass. It has a child of class Shell DocObject View, which has a child of Internet Explorer_Server. If I use the hwnd of the Internet Explorer_Server then it works correctly, eg when I click with the mouse on other tabs IE reacts normally, when i click with the mouse on the tab of interest it plays the windows d'oh sound and doesn't do anything.

    I don't yet know how to programatically get the Internet Explorer_Server hwnd but it should be possible.

    However, I think I have a different solution to the problem. I've found that if instead of using MessageBox I use a custom form and display it modal to my first form then it correctly appears in front of all other windows and tabs, and is correctly modal to my first form and to IE. This makes me think that the problem is actually with MessageBox (or a combination of MessageBox and IE7 and/or .net).

    I don't know what it means, but after the call to MessageBox.Show(), when IE is frozen and the message box isn't visible but the IE tab is flashing, I can't find any new Window in Spy++ that could correspond to the MessageBox. Maybe I missed it, but maybe the problem occurs before the messagebox is created...

    Anyway, the solution to this problem seems to be: don't use MessageBox from forms displayed from IE.

    • Marked as answer by Rory PS Tuesday, February 24, 2009 4:28 PM
    Tuesday, February 24, 2009 4:26 PM

All replies

  • You need to use the tab's HWND as your modal form's parent. Your BHO would have another instance that can display the modal dialog in the new tab if you told it to (e.g. set up some shared memory to store initial parameters for the BHO to decide whether to show a modal dialog or not).

    From IWebBrowser2.HWND's documentation:

    For applications that absolutely require an HWND to the current tab, the following example returns the same logical window as IWebBrowser2::HWND in a previous version of Internet Explorer. This technique works equally well in both Internet Explorer 7 and Microsoft Internet Explorer 6.

    #include <shlguid.h>
    
    IServiceProvider* pServiceProvider = NULL;
    if (SUCCEEDED(pWebBrowser2->QueryInterface(
                        IID_IServiceProvider, 
                        (void**)&pServiceProvider)))
    {
        IOleWindow* pWindow = NULL;
        if (SUCCEEDED(pServiceProvider->QueryService(
                        SID_SShellBrowser, 
                        IID_IOleWindow,
                        (void**)&pWindow)))
        {
            HWND hwndBrowser = NULL;
            if (SUCCEEDED(pWindow->GetWindow(&hwndBrowser)))
            {
                // hwndBrowser is the handle of TabWindowClass
            }
    
            pWindow->Release();
        }
     
        pServiceProvider->Release();
    } 

    MSMVP VC++

    Thursday, January 29, 2009 1:05 AM
  • yes, but this will give me the HWND of the "current" tab. My form is supposed to be modal with respect to the new tab, but presumably the current tab is the one that is currently displayed, not the one that is newly opened? So you're right that I need the tab's HWND, but i need the newly opened tab's HWND. 

    I'll give this code a go and see what happens.
    Tuesday, February 24, 2009 11:56 AM
  • ok, I've tried this and the code sample above does return the HWND of the newly opened tab - yay! - however, when I use that as the Owner of my modal dialog it isn't opened as modal!? I can click back to the IE window, change tabs, navigate, etc while my "modal" form is displayed over the top of IE.  If I use Spy++ I can see that my form's Owner Window handle is set correctly to the tab's HWND.

    What could be causing it to not be modal, and any suggestions on how to make it correctly modal?

    I'm using c# .net 2.0 and showing my form using this:

    MyForm f = new MyForm(); 
    // ... Set various properties on f 
     
    // Get the hwnd of the tab. Uses above code. 
    IntPtr hwndTab = GetHwndOfTab(webBrowser); 
     
    // Wrap the hwnd to return an IWin32Window - see below for class defn. 
    IWin32Window tab = new WindowWrapper(hwndTab); 
     
    // This shows the form but it's not modal. 
    f.ShowDialog(tab);   


    Where WindowWrapper is:
     
        /// <summary> 
        /// Wrapper class so that we can return an IWin32Window given a hwnd 
        /// </summary> 
        public class WindowWrapper : System.Windows.Forms.IWin32Window 
        { 
            public WindowWrapper(IntPtr handle) 
            { 
                _hwnd = handle; 
            } 
            public IntPtr Handle 
            { 
                get { return _hwnd; } 
            } 
            private IntPtr _hwnd; 
        } 




    Tuesday, February 24, 2009 1:45 PM
  • Playing around some more with Spy++ and the handles it exposes, I find that if I use a different hwnd then I can make my window modal to the tab:

    I was using the tab's hwnd as suggested above, which in Spy++ appears as class TabWindowClass. It has a child of class Shell DocObject View, which has a child of Internet Explorer_Server. If I use the hwnd of the Internet Explorer_Server then it works correctly, eg when I click with the mouse on other tabs IE reacts normally, when i click with the mouse on the tab of interest it plays the windows d'oh sound and doesn't do anything.

    I don't yet know how to programatically get the Internet Explorer_Server hwnd but it should be possible.

    However, I think I have a different solution to the problem. I've found that if instead of using MessageBox I use a custom form and display it modal to my first form then it correctly appears in front of all other windows and tabs, and is correctly modal to my first form and to IE. This makes me think that the problem is actually with MessageBox (or a combination of MessageBox and IE7 and/or .net).

    I don't know what it means, but after the call to MessageBox.Show(), when IE is frozen and the message box isn't visible but the IE tab is flashing, I can't find any new Window in Spy++ that could correspond to the MessageBox. Maybe I missed it, but maybe the problem occurs before the messagebox is created...

    Anyway, the solution to this problem seems to be: don't use MessageBox from forms displayed from IE.

    • Marked as answer by Rory PS Tuesday, February 24, 2009 4:28 PM
    Tuesday, February 24, 2009 4:26 PM
  • In case this is useful, here's my c# translation of the recommended way to get an hwnd of the tab instead of the whole window. The hwnd is returned wrapped as an IWin32Window so it can be passed as the owner parameter to MessageBox.Show or Form.ShowDialog. Note as per my previous post this doesn't work for modal dialogs etc! But it does get the hwnd of the tab, so it migt be useful for something.

            /// Returns an IWin32Window representing the IWebBrowser2 tab's HWND. 
            /// If it can't get the HWND of the tab then it returns the top-level IE  
            /// window's HWND 
            /// 
            /// Note that using the tab's HWND doesn't seem to work very well! At least,  
            /// not for modal dialogs. See comments in previous post for suggestion of  
            /// which HWND appears to work better. 
            public IWin32Window OwnerWindow 
            { 
                get 
                { 
                    IWin32Window result = null
                    if (webBrowser != null
                    { 
                        // Try to get the HWND of the current tab. 
                        IServiceProvider sp = webBrowser as IServiceProvider;   
                        if (sp != null
                        {                                                                
                            Guid SID_SShellBrowser = new Guid("{000214E2-0000-0000-C000-000000000046}"); 
                            Guid IID_IOleWindow = typeof(IOleWindow).GUID; 
     
                            IntPtr pWindow = IntPtr.Zero; 
                            sp.QueryService(ref SID_SShellBrowser, ref IID_IOleWindow, out pWindow); 
                            IOleWindow iWindow = Marshal.GetObjectForIUnknown(pWindow) as IOleWindow; 
                            if (iWindow != null
                            { 
                                IntPtr tabHwnd = IntPtr.Zero; 
                                iWindow.GetWindow(out tabHwnd); 
                                if (tabHwnd != IntPtr.Zero) 
                                    result = new WindowWrapper(tabHwnd); 
                                Marshal.ReleaseComObject(iWindow); 
                            } 
                            Marshal.ReleaseComObject(sp); 
                        } 
                        if (result == null
                        { 
                            // Couldn't get HWND of the tab, use the HWND of the whole IE window. 
                            result = new WindowWrapper(new IntPtr(webBrowser.HWND)); 
                        } 
                    } 
                    return result; 
                } 
            } 
    ... 
     
        /// <summary> 
        /// Wrapper class so that we can return an IWin32Window given a hwnd 
        /// </summary> 
        public class WindowWrapper : System.Windows.Forms.IWin32Window 
        { 
            public WindowWrapper(IntPtr handle) 
            { 
                _hwnd = handle; 
            } 
     
            public IntPtr Handle 
            { 
                get { return _hwnd; } 
            } 
     
            private IntPtr _hwnd; 
        } 
     
        /// <summary> 
        /// Implemented and used by containers and objects to obtain window handles 
        /// and manage context-sensitive help. 
        /// </summary> 
        /// <remarks> 
        /// The IOleWindow interface provides methods that allow an application to obtain 
        /// the handle to the various windows that participate in in-place activation, 
        /// and also to enter and exit context-sensitive help mode. 
        /// </remarks> 
        /// See http://pinvoke.net/default.aspx/Interfaces.IOleWindow for this definition. 
        [ComImport] 
        [Guid("00000114-0000-0000-C000-000000000046")] 
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
        public interface IOleWindow 
        { 
            /// <summary> 
            /// Returns the window handle to one of the windows participating in in-place activation 
            /// (frame, document, parent, or in-place object window). 
            /// </summary> 
            /// <param name="phwnd">Pointer to where to return the window handle.</param> 
            void GetWindow(out IntPtr phwnd); 
     
            /// <summary> 
            /// Determines whether context-sensitive help mode should be entered during an 
            /// in-place activation session. 
            /// </summary> 
            /// <param name="fEnterMode"><c>true</c> if help mode should be entered; 
            /// <c>false</c> if it should be exited.</param> 
            void ContextSensitiveHelp([In, MarshalAs(UnmanagedType.Bool)] bool fEnterMode); 
        } 
     
     

    Tuesday, February 24, 2009 4:40 PM
  • You should be able to do this without worrying about the Parent....using WindowStateChangeEvent:

     Form frmDialog;

     bool showDialog;
            

            void Explorer_DownloadComplete()

            {

                showDialog = true;

            }



            void Explorer_WindowStateChanged(uint dwWindowStateFlags, uint dwValidFlagsMask)

            {

                if (dwWindowStateFlags == 3 && dwValidFlagsMask == 3)

                {

                    if (showDialog)

                    {

                        showDialog = false;

                        frmDialog = new Form();

                        frmDialog.ShowDialog();

     

     

                    }

                    else

                        if(frmDialog != null)

                        frmDialog.Dispose();

     

                }

     

           }

          


    I need a job!
    Tuesday, February 24, 2009 7:00 PM