ParkingWindow revisited

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

This issue was already reported by several others but I did not find any useful solution / workaround.

The problem happens in a random non-frequent manner when I run my WindowsForms application. I get the following exception:

Cannot access a disposed object named "ParkingWindow"

In another post in this NG titled "Derived combo box controls in forms - MessageBox.Show breaks the control's message routing" Clive Dixon describes a potential reason for the problem. However, no practical workaround is suggested.

Could someone from MS suggest how to avoid this issue?

Thanks in advance,
 
Hi Benzi,

I had read some issues related to the ParkingWindow, but the causes and
resolutions are different , there is no general workaround for this issue.
To my knowledge, the ParkingWindow is a hidden , per-thread window that is
used as a prent for control handles who have not been parented),

In your scenario, it seems the ParkingWindow is disposed when your form
tries to use it. I'm really not clear about what caused this exception, did
you save the call stack when the exception was thrown?

further investigating on this issue requires a small sample program to
reproduce this issue, if this problem could be sepearated into a small
project, please send me to let me take a look, if this issue could not be
easily sperated down or hard to reproduce in other environment, you may
need contact our Phone service for further assistance.

Thanks!

Best regards,

Ying-Shen Yu [MSFT]
Microsoft Community Support
Get Secure! - www.microsoft.com/security

This posting is provided "AS IS" with no warranties and confers no rights.
This mail should not be replied directly, please remove the word "online"
before sending mail.
 
Benzi,

I found a workaround for my particular circumstance. Here I reproduce the
report I produced for circulation within my company. Hope it helps for you
too.

Ying,

Also as part of this is a description of how to recreate the problem with a
very simple project, with C# code included. I hope that you can take this
and find the exact nature of the problem.

The problem in its barest form

The problem can be easily recreated as follows (example C# code attached):

Create a .NET windows control library project.

Add an owner draw control and a button to the user control.

Add a derived control class to handle the owner draw, modify the IDE
generated code to use this class.

Explicitly create the owner draw control in InitializeComponent using
CreateControl. (This part is the crux - in our company's product, there are
components where InitializeComponent is setting certain properties of child
owner draw controls which cause the child window handle to be created.)

Add button click handler that shows a message box.

Add COM attributes to user control class.

Add ComRegister function to register component as ActiveX control.

Set project to register for interop.

Compile.

Add control to tstcon32.exe (or add to MFC app dialog).

Press button on user control to show message box.

Owner draw control is now broken.







Code for UserControl.cs:

using System;

using System.Collections;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Runtime.InteropServices;

using System.Windows.Forms;

using Microsoft.Win32;

namespace WindowsControlLibrary

{

[Guid("BDD17877-B805-4970-B3BB-2F1A2C1E5A38")]

[ProgId("WindowsControlLibrary.UserControl")]

[ClassInterface(ClassInterfaceType.AutoDual)]

public class UserControl : System.Windows.Forms.UserControl

{

private WindowsControlLibrary.OwnerDrawCombo comboBox1;

private System.Windows.Forms.Button button1;

private System.ComponentModel.Container components = null;

[ComRegisterFunction]

static void ComRegister(Type t)

{

string keyName = @"CLSID\" + t.GUID.ToString("B");

using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(keyName,true) )

{

key.CreateSubKey("Control").Close();

}

}

[ComUnregisterFunction]

static void ComUnregister(Type t)

{

string keyName = @"CLSID\" + t.GUID.ToString("B");

Registry.ClassesRoot.DeleteSubKeyTree(keyName);

}

public UserControl()

{

InitializeComponent();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if( components != null )

components.Dispose();

}

base.Dispose( disposing );

}

#region Component Designer generated code

private void InitializeComponent()

{

this.comboBox1 = new WindowsControlLibrary.OwnerDrawCombo();

this.button1 = new System.Windows.Forms.Button();

this.SuspendLayout();

/*

Workaround #1

this.comboBox1.Parent = this;

this.CreateControl();

*/

this.comboBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;

this.comboBox1.DropDownStyle =
System.Windows.Forms.ComboBoxStyle.DropDownList;

this.comboBox1.Items.AddRange(new object[] {

"Owner Draw Combo",

"One",

"Two",

"Three"});

this.comboBox1.Location = new System.Drawing.Point(16, 32);

this.comboBox1.Name = "comboBox1";

this.comboBox1.Size = new System.Drawing.Size(121, 21);

this.comboBox1.TabIndex = 0;

this.comboBox1.SelectedIndex = 0;

// Creating the control here causes the combo box

// draw item messaging to go through the parking window,

// and a message box invocation will break the combo.

this.comboBox1.CreateControl();

this.button1.Location = new System.Drawing.Point(32, 80);

this.button1.Name = "button1";

this.button1.TabIndex = 1;

this.button1.Text = "Click Me!";

this.button1.Click += new EventHandler(button1_Click);

this.Controls.Add(this.button1);

this.Controls.Add(this.comboBox1);

this.Name = "UserControl";

this.ResumeLayout(false);

}

#endregion

void button1_Click(object sender, System.EventArgs e)

{

MessageBox.Show("The owner draw combo box is now broken!\n" +

"Click on the drop down to see.");

}

}

}







Code for OwnerDrawCombo.cs:

using System;

using System.Collections;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Windows.Forms;

using System.Runtime.InteropServices;

using Microsoft.Win32;

namespace WindowsControlLibrary

{

public class OwnerDrawCombo : System.Windows.Forms.ComboBox

{

protected override void OnDrawItem(DrawItemEventArgs e)

{

e.DrawBackground();

if ( e.Index >= 0 && e.Index < this.Items.Count )

{

e.Graphics.DrawString(this.Items[e.Index].ToString(),

e.Font,Brushes.Black,

e.Bounds.Left,e.Bounds.Top);

}

e.DrawFocusRectangle();

}

// Workaround #2

/*

protected override void OnParentChanged(EventArgs e)

{

if ( this.Parent != null )

{

this.CreateParams.Parent = this.Parent.Handle;

this.RecreateHandle();

}

base.OnParentChanged (e);

}

*/

}

}



What is going wrong

If a child control window handle is created without a parent (i.e. before
the parent has been created), then the child is assigned a parent window of
class System.Windows.Forms.Application.ParkingWindow, of which there is at
most 1 per thread and which is created if necessary.

If the parent control is used by another .NET component, then when the
parent control is created, the .NET framework correctly reassigns the parent
and the internal objects used for message routing appropriately.

However if the parent control is being created as a result of ActiveX
activation, then the .NET framework does not correctly reassign the internal
objects, and messages such as owner draw messages to the parent continue to
be routed through the ParkingWindow object. By good fortune, message
reflection ensures that these messages get to the child which handles them
correctly. But as soon as a message box is invoked, .NET destroys the
parking window. At best, the child control will stop drawing itself because
the owner draw messages never get processed, at worst the CLR will throw an
exception because of null references.

Ways to get around the problem

1. Ensure that nothing happens in the parent control's InitializeComponent
or constructor to cause child controls to have their window handle created
before the parent window handle is created by the ActiveX activation. (Note
that having trawled through .NET framework code, it appears entirely
possible that setting some standard framework control object properties
could cause the underlying window handle to be created.)

2. Failing that, call the parent window's CreateControl and set the child
control's parent object before the thing happens which causes the child
control window to be created (see comment Workaround #1 in file
UserControl.cs above).

3. Better still, add the following code within the child control itself so
that it is guaranteed that the parent and child controls will be created and
parent object set in the right order irrespective of what happens in
InitializeComponent (see comment Workaround #2 in file OwnerDrawCombo.cs
above):

protected override void OnParentChanged(EventArgs e)

{

if ( this.Parent != null )

{

this.CreateParams.Parent = this.Parent.Handle;

this.RecreateHandle();

}

base.OnParentChanged (e);

}

What I have not been able to fully ascertain is quite how in the case where
the parent control is used 'normally' by another .NET component (i.e. as
opposed to used as an ActiveX control by unmanaged code), the .NET framework
correctly reassigns the message routing. Knowing this might offer a better
solution in some sense (having said that - much of what goes on here
involves private and internal methods and properties and thus stuff we
cannot use).
 
Thanks for your input Clive,

Your investigation is very thorough, I'll look into this issue and forward
it to our product team.
Thanks for your feedback!

To Benzi,
Could you provide more information about your appliction? Do you have a
similiar scenario that hosting the winform control in a MFC application?
Thanks!

Best regards,

Ying-Shen Yu [MSFT]
Microsoft Community Support
Get Secure! - www.microsoft.com/security

This posting is provided "AS IS" with no warranties and confers no rights.
This mail should not be replied directly, please remove the word "online"
before sending mail.
 
Ying-Shen,
Sorry, but I do not have the call stack tracing info (I waited with my answer as I thought that we will bump on this again, but...). The application is relatively big and our attempts to simulate the problem with a small demo failed (until now).

We do not have the same scenario as described by Clive. Our application is VB only, no MFC involved. It interfaces with several interop dlls and also uses Win32 for Basic IO.

The typical call stack (as I remember) shows exception in an Invoke statement from a worker thread.

I will post a reply here when (and if) we hit this again.

Thanks for your help (Clive, you too :-) )
 
Hi Benzi,

Thanks for your reply,
Please feel free to post to the group if you have any update information on
this issue.

Thanks!
Best regards,

Ying-Shen Yu [MSFT]
Microsoft Community Support
Get Secure! - www.microsoft.com/security

This posting is provided "AS IS" with no warranties and confers no rights.
This mail should not be replied directly, please remove the word "online"
before sending mail.
 
Back
Top