Thursday, March 24, 2011

MDI Child .Show() Method Raises NullReferenceException in Framework

The below is an involved problem, I've done a great amount of research, but have been unable to solve it thus far. I'm happy to find either a solution or a workaround. Although the framework is old (1.0) I am not at this stage able to upgrade to 1.1 or later (the client isn't willing to pay for the conversion at present), and I'm not even sure that this would solve the problem... Any help is much appreciated :

Platform : .NET 1.0 VB.NET Windows Forms MDI application running on Windows XP

Problem : An MDI child window (child1) calls the .Show() method of a different MDI child window (child2) and a NullReferenceException is thrown.

This only happens from one particular form (child1), and only when that child form is in a maximized state, and the MDI parent window is also in a maximized state.

The exception is thrown for any other child form that is opened from code within child1 (i.e. the error is not isolated to opening one type of form).

The exception is only thrown when running from the debugger with 'Break Into The Debugger When An Exception Is Thrown' on (although this error is manifesting itself later on which is the real problem). This behaviour seems to be explained in the framework in so far as an exception is caught but nothing is done with it (see reflected code below).

The exception seems to be being thrown from within the framework (1.0) itself. See the stack trace below:

system.windows.forms.dll!System.Windows.Forms.NativeWindow::WindowClassCallback(__int32 hWnd = 461572, __int32 msg = 36, __int32 wparam = 0, __int32 lparam = 1239164) + 0x48 bytes 
    system.windows.forms.dll!System.Windows.Forms.UnsafeNativeMethods::CreateWindowEx(__int32 dwExStyle = 327744, String* lpszClassName = "WindowsForms10.Window.8.app13", String* lpszWindowName = "Diary Entry: ROSIE MILES", __int32 style = 1204748288, __int32 x = -2147483648, __int32 y = -2147483648, __int32 width = 480, __int32 height = 380, __int32 hWndParent = 920114, __int32 hMenu = 0, __int32 hInst = 285212672, System.Object pvParam = null) + 0x3c bytes 
    system.windows.forms.dll!System.Windows.Forms.NativeWindow::CreateHandle(System.Windows.Forms.CreateParams cp = {System.Windows.Forms.CreateParams}) + 0x1d6 bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::CreateHandle() + 0x19a bytes 
    system.windows.forms.dll!System.Windows.Forms.Form::CreateHandle() + 0x12f bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::get_Handle() + 0x2f bytes 
    system.windows.forms.dll!System.Windows.Forms.Form::SetVisibleCore(bool value = true) + 0x1e9 bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::set_Visible(bool value = true) + 0x1e bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::Show() + 0x14 bytes 
>   SpecMed.exe!SpecMed.ScheduleForm.viewScheduleMenuItem_Click(Object sender = {System.Windows.Forms.MenuItem}, System.EventArgs e = {System.EventArgs}) Line 1508 + 0xb bytes Basic
    system.windows.forms.dll!System.Windows.Forms.MenuItem::OnClick(System.EventArgs e = {System.EventArgs}) + 0x8d bytes 
    system.windows.forms.dll!MenuItemData::Execute() + 0x1e bytes 
    system.windows.forms.dll!System.Windows.Forms.Command::Invoke() + 0x4c bytes 
    system.windows.forms.dll!System.Windows.Forms.Command::DispatchID(__int32 id = 299) + 0x2c bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::WmCommand(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x4a bytes 
    system.windows.forms.dll!System.Windows.Forms.Control::WndProc(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x1f3 bytes 
    system.windows.forms.dll!ControlNativeWindow::OnMessage(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x19 bytes 
    system.windows.forms.dll!ControlNativeWindow::WndProc(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0xda bytes 
    system.windows.forms.dll!System.Windows.Forms.NativeWindow::Callback(__int32 hWnd = 6621754, __int32 msg = 273, __int32 wparam = 299, __int32 lparam = 0) + 0x4a bytes 
    system.windows.forms.dll!System.Windows.Forms.Application::ComponentManagerSystem.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(__int32 dwComponentID = 1, __int32 reason = -1, __int32 pvLoopData = 0) + 0x2c1 bytes 
    system.windows.forms.dll!ThreadContext::RunMessageLoop(__int32 reason = -1, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.ApplicationContext}) + 0x1c5 bytes 
    system.windows.forms.dll!System.Windows.Forms.Application::Run(System.Windows.Forms.Form mainForm = {SpecMed.MainForm}) + 0x34 bytes 
    SpecMed.exe!SpecMed.Program.Main() Line 15 + 0x1d bytes Basic

I have reflected this method on the off-chance of gaining some insight into the problem. The code for WindowClass.Callback() method is:

public IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
{
    try
    {
        UnsafeNativeMethods.SetWindowLong(hWnd, -4, this.defWindowProc);
        this.targetWindow.AssignHandle(hWnd);
        return this.targetWindow.Callback(hWnd, msg, wparam, lparam);
    }
    catch (Exception)
    {
    }
    return IntPtr.Zero;
}

As you can see, the exception is caught but nothing is done with it.

UI Manifestation :

The cause of this exception then seems to manifest itself later on, with terrible behaviour of the calling (child1) and called (child2) child forms .

In the UI, even thought he exception is ignored the MDI parent displays two MDI Child control boxes (i.e. two sets of minimise, restore and close boxes) and two child icons (see screenshots). One control box is disabled.

alt text alt text

After closing the child2 form, child1 becomes non-responsive and fails to repaint.

Research :

Searching on the web, I've found the following articles, which seem to describe similar or almost identical problems (but on different frameworks), but none of these solutions have helped.

http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/66cee5a3-c743-4be6-ae63-7416b81a9688/ (Sounds like exactly the same problems, but no solution seems to have ever been posted).

http://support.microsoft.com/kb/871045 (Similar but not the same problems, solution doesn't seem to help).

http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic39112.aspx : (Sounds like the same problem, but for different framework).

The calling child form (child1), has some third-party controls on it (Janus WinForms Suite), which I suspect are somehow causing the problem. These controls are .NET controls and not Activex.

Attempted Solutions

I've tried raising an event on child1, that the MDI Parent handles, and then the MDI Parent opens child2. Still the same problem.

I've tried setting the MDI parent of child2 , after child1 calls the child2.Show(). Still no improvement.

I've tried setting the MDI Parent of child2 prior to the child2.InitialiseComponents() call. No improvement.

After typing all this, I'm starting to think that the problem isn't with calling child2 as such, but rather how child1 is being handled when child2 is opened, maximized and therefore sending child1 behind it?

If anyone can help me out, or point in me the right direction, I sure would appreciate it.

Thanks

From stackoverflow
  • Workaround :

    I've found a workaround, that isn't particularly elegant, but currently seems to work:

    By checking if the calling form is maximised and storing this, it is then possible to minimise (or restore) the calling form prior to the .Show() call (preventing the problem), then setting the called forms WindowState to maximised. Setting the called forms state to maximised emulates the behaviour of what should normally happen (apart from a bit of flickering with the resizing of forms).

    Code :

    '-- workaround to prevent UI problem with maximised child form calling another maxmised child form --'
    Dim isMaximised As Boolean = (WindowState = FormWindowState.Maximized)
    
    '-- minimise the current form so the problem wont occur when we call the new child Form --'
    If isMaximised Then WindowState = FormWindowState.Minimized
    
    Dim form As DiaryEntryForm = DiaryEntryForm.GetForm(scheduleId, Me)
    '-- if the calling child was originally maximised, then maximsed the called form to similute what should normally happen --'
    If isMaximised Then form.WindowState = FormWindowState.Maximized
    form.Show()
    

    I'll keep trying to find a solution however. Any help appreciated. Thanks.

  • Are you attempting to merge menus as a part of the MDI child showing? If so you may be running into this issue

    http://support.microsoft.com/kb/895579

    The hotfix there will work around the problem if it's the one you're seeing.

    Jayden : Yes I am. Will have a look and get back soon!
    Jayden : The problem definitely seems to be being caused by the merge menu items. The hotfix you refer to is for .NET Framework 1.1 and 1.1 SP1. I'm using .NET Framework 1.0. I can't seem to find a similarly documented problem for 1.0?

0 comments:

Post a Comment