Microsoft KB Archive/815634

= BUG: Unhandled exception error occurs when you enumerate through the Hashtable =

Article ID: 815634

Article Last Modified on 9/13/2005

-

APPLIES TO


 * Microsoft .NET Framework 1.1
 * Microsoft .NET Framework 1.0
 * Microsoft Visual Basic .NET 2003 Standard Edition
 * Microsoft Visual Basic .NET 2002 Standard Edition
 * Microsoft Visual C# .NET 2003 Standard Edition
 * Microsoft Visual C# .NET 2002 Standard Edition

-



SYMPTOMS
In a Remoting scenario, you may try to move the Enumerator for a Hashtable from the server side to the client side. You do this so you can iterate through the Hashtable on the client side. While you are enumerating through the Hashtable, you may receive the following error message:

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: Collection was modified; enumeration operation may not execute.



CAUSE
The error occurs because the Hashtable Enumerator is a MarshalByValue component. Therefore, the Hashtable stores the owner of the Hashtable internally. When you request the Hashtable on the client side, the Hashtable is deserialized on the client side. During deserialization, the internal Hashtable is reconstructed. Therefore, the enumerator fails.



WORKAROUND
To work around this problem, return the Hashtable directly to the client. Do this instead of returning IEnumerator for the Hashtable. For example, locate the following code in the ISystemSetting.cs file or in the ISystemSetting.vb file of the InterfaceDll project. Sample code that can be used is included in the &quot;More Information&quot; section of this article.

Visual C# .NET Code IEnumerator GetEnumerator; Visual Basic .NET Code Function GetEnumerator As IEnumerator Replace the IEnumerator function with the HashTable function as follows:

Visual C# .NET Code Hashtable GetHashtable; Visual Basic .NET Code Function GetHashTable As Hashtable



STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.



Steps to Reproduce the Problem
 Run Visual Studio .NET. Create a new Class Library project.

You can use either Visual Basic .NET or Visual C# .NET. Name the project InterfaceDLL .  Replace the existing code in the class with the following code:

Visual Basic .NET Code Option Strict On Imports System Imports System.Collections Imports System.Runtime.Serialization Imports System.Collections.Specialized

Namespace test.SystemSettingInterface

Public Interface ISystemSetting ' Add elements to the Hashtable Sub Add(ByVal Key As Object, ByVal Value As Object)

' Function Returning IEnumerator for Hashtable ' This function throws error Function GetEnumerator As IEnumerator

' Function Returning Hashtable directly ' This function can be used as a workaround for function GetEnumerator Function GetHashTable As Hashtable

' Default Property for Enumeration Default Property Item(ByVal key As Object) As Object ' Count Returns the number of elements in the Hashtable ReadOnly Property Count As Integer ' Test function Sub Test

End Interface End Namespace

Visual C# .NET Code using System; using System.Collections; using System.Runtime.Serialization; using System.Collections.Specialized;

namespace test.SystemSettingInterface {   public interface ISystemSetting {     // Add elements to the Hashtable void Add( object Key, object Value );

// Function Returning IEnumerator for Hashtable // This function throws error IEnumerator GetEnumerator; // Function Returning Hashtable directly // This function can be used as workaround for function GetEnumerator Hashtable GetHashtable;

// Default Property for Enumeration object this[object key] {        get; set; }

// Count Returns the number of elements in the Hashtable int Count {           get; }

// Test function void Test; } }  On the Build menu, click Build Solution to create InterfaceDLL.dll. On the File menu, point to Add Project and then click New Project. Add a new Console Application project to the solution.

You can use either Visual Basic .NET or Visual C# .NET. Name the project Server . In Solution Explorer, right-click Server and then click Add Reference.</li> In the Reference dialog box, click the Projects tab. Double-click InterfaceDLL and then click OK.</li>  Replace the existing code with the following code:

Visual Basic .NET Code Option Strict On Imports System Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports System.Collections Imports System.Collections.Specialized Imports InterfaceDLL.test Imports InterfaceDLL.test.SystemSettingInterface

Namespace test.server Public Class SystemSettingTest : Inherits MarshalByRefObject Implements ISystemSetting

' HashTable used for Enumeration Public Shared m_HashTable As Hashtable Private Shared m_initialized As Boolean

Shared Sub Main Dim m_systemSettingTest As SystemSettingTest = New SystemSettingTest m_systemSettingTest.Init Console.WriteLine(&quot;Test Server Running.&quot;) Console.ReadLine End Sub

Public Sub Test Implements ISystemSetting.Test If IsNothing(m_HashTable) Then ' Create new instance the HashTable m_HashTable = New Hashtable

' Add elements to the Hashtable m_HashTable.Add(&quot;testkey1&quot;, &quot;testvalue1&quot;) m_HashTable.Add(&quot;testkey2&quot;, &quot;testvalue2&quot;) End If     End Sub

Private Sub Init Try ' Register Channel for Remoting ChannelServices.RegisterChannel(New TcpChannel(9999)) RemotingConfiguration.RegisterWellKnownServiceType(GetType(SystemSettingTest), &quot;SystemSettingTest&quot;, WellKnownObjectMode.SingleCall) m_initialized = True

Catch ex As Exception m_initialized = False Throw New System.Exception(&quot;Unable to establish WKS: &quot; + ex.Message) End Try End Sub

' Add method implementation Public Sub Add(ByVal Key As Object, ByVal Value As Object) Implements ISystemSetting.Add m_HashTable.Add(Key, Value) End Sub

' Count method implementation Public ReadOnly Property Count As Integer Implements ISystemSetting.Count Get Return m_HashTable.Count End Get End Property

' GetEnumerator method implementation. This method generates error Public Function GetEnumerator As System.Collections.IEnumerator Implements ISystemSetting.GetEnumerator ' Return the Enumerator for the Hashtable Dim T As IDictionaryEnumerator = m_HashTable.GetEnumerator Return T     End Function

' This is the Workaround method that directly returns the Hashtable instead of Enumerator Public Function GetHashTable As System.Collections.Hashtable Implements InterfaceDLL.test.SystemSettingInterface.ISystemSetting.GetHashTable Return m_HashTable End Function

' Item property method implementation Default Public Property Item(ByVal key As Object) As Object Implements ISystemSetting.Item Get Return m_HashTable(key) End Get Set(ByVal Value As Object) End Set End Property

End Class

End Namespace Visual C# .NET Code using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Collections; using System.Collections.Specialized; using test.SystemSettingInterface; using test;

namespace test.server {   public class SystemSettingTest : MarshalByRefObject, ISystemSetting {       static void Main(string[] args) {           SystemSettingTest m_systemSettingTest = new SystemSettingTest; m_systemSettingTest.Init; Console.WriteLine (&quot;Test Server Running.&quot;); Console.ReadLine; }

// HashTable used for Enumeration public static Hashtable m_HashTable; private static bool m_initialized;

public void Test {           if(m_HashTable==null) {               // Create new instance the HashTable m_HashTable= new Hashtable; // Add elements to the Hashtable m_HashTable.Add(&quot;testkey1&quot;, &quot;testvalue1&quot;); m_HashTable.Add(&quot;testkey2&quot;, &quot;testvalue2&quot;); }       }        private void Init {           try {              // Register Channel for Remoting ChannelServices.RegisterChannel(new TcpChannel(9999)); RemotingConfiguration.RegisterWellKnownServiceType(typeof(SystemSettingTest),                   &quot;SystemSettingTest&quot;, WellKnownObjectMode.SingleCall); m_initialized=true; }           catch (System.Exception ex) {               m_initialized=false; throw new System.Exception(&quot;Unable to establish WKS: &quot; + ex.Message); }       }      // Add method implementation public void Add(object Key, object Value) {           m_HashTable.Add(Key,Value); }     // GetEnumerator method implementation. This method generates error public IEnumerator GetEnumerator {        // Return the Enumerator for the Hashtable IDictionaryEnumerator T = m_HashTable.GetEnumerator; return T;       }

// This is the Workaround method that directly returns the Hashtable instead of Enumerator public Hashtable GetHashtable {        return m_HashTable; }     // Default property for Enumeration public object this [object key] {           get {               return m_HashTable[(string)key]; }           set {           }        }

// Count property implementation public int Count {           get {               return m_HashTable.Count; }       }    } } </li> In Solution Explorer, right-click Server and then click Set as StartUp Project.</li> On the Debug menu, click Start.</li> Open a new instance of Visual Studio .NET development environment.</li> Create a new Windows application project.

You can use either Visual Basic .NET or Visual C# .NET. Name the project Client .</li> In Solution Explorer, right-click Client and then click Add Reference.</li> Click Browse and then select InterfaceDLL.dll that you created in step 3.</li> Click OK to add the reference.</li>  Replace the existing code with the following code:

Visual Basic .NET Code Option Strict On

Imports System.Net Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports InterfaceDLL.test Imports InterfaceDLL.test.SystemSettingInterface

Public Class Form1 Inherits System.Windows.Forms.Form


 * 1) Region &quot; Windows Form Designer generated code &quot;

Public Sub New MyBase.New

'This call is required by the Windows Form Designer. InitializeComponent

'Add any initialization after the InitializeComponent call

End Sub

'Form overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose End If     End If      MyBase.Dispose(disposing) End Sub

'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents button3 As System.Windows.Forms.Button Friend WithEvents button2 As System.Windows.Forms.Button Friend WithEvents button1 As System.Windows.Forms.Button Friend WithEvents Button4 As System.Windows.Forms.Button <System.Diagnostics.DebuggerStepThrough> Private Sub InitializeComponent Me.button3 = New System.Windows.Forms.Button Me.button2 = New System.Windows.Forms.Button Me.button1 = New System.Windows.Forms.Button Me.Button4 = New System.Windows.Forms.Button Me.SuspendLayout '     'button3 '     Me.button3.Location = New System.Drawing.Point(32, 106) Me.button3.Name = &quot;button3&quot; Me.button3.Size = New System.Drawing.Size(208, 32) Me.button3.TabIndex = 5 Me.button3.Text = &quot;GetEnumerator (Error)&quot; '     'button2 '     Me.button2.Location = New System.Drawing.Point(32, 61) Me.button2.Name = &quot;button2&quot; Me.button2.Size = New System.Drawing.Size(208, 32) Me.button2.TabIndex = 4 Me.button2.Text = &quot;Count Hashtable elements&quot; '     'button1 '     Me.button1.Location = New System.Drawing.Point(32, 16) Me.button1.Name = &quot;button1&quot; Me.button1.Size = New System.Drawing.Size(208, 32) Me.button1.TabIndex = 3 Me.button1.Text = &quot;Initialize Remote Component&quot; '     'Button4 '     Me.Button4.Location = New System.Drawing.Point(32, 151) Me.Button4.Name = &quot;Button4&quot; Me.Button4.Size = New System.Drawing.Size(208, 32) Me.Button4.TabIndex = 6 Me.Button4.Text = &quot;GetHashtable (Work around)&quot; '     'Form1 '     Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(280, 206) Me.Controls.AddRange(New System.Windows.Forms.Control {Me.Button4, Me.button3, Me.button2, Me.button1}) Me.Name = &quot;Form1&quot; Me.Text = &quot;Form1&quot; Me.ResumeLayout(False)

End Sub

Private m_SystemSetting As ISystemSetting
 * 1) End Region

' Connect to server using Remoting Private Sub init Dim connect As String = &quot;tcp://&quot; + Dns.GetHostName + &quot;:9999/SystemSettingTest&quot; m_SystemSetting = CType(RemotingServices.Connect(GetType(ISystemSetting), connect), ISystemSetting) m_SystemSetting.Test End Sub

' Initialize the Hashtable Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click Try init MessageBox.Show(&quot;Init success.&quot;) Catch ex As Exception

MessageBox.Show(&quot;Unable to initialize remoting connection with test server:&quot; + ex.Message) End Try End Sub

' Verify the Hashtable has elements Private Sub button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button2.Click Try MessageBox.Show(m_SystemSetting.Count.ToString) Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub

' Problem method call Private Sub button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button3.Click ' Get the Enumerator for Hashtable Dim T As IDictionaryEnumerator = CType(m_SystemSetting.GetEnumerator, IDictionaryEnumerator) MessageBox.Show(T.Value.ToString) End Sub

' Work Around method call Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click Dim ht As Hashtable ht = m_SystemSetting.GetHashTable Dim obj As Object For Each obj In ht.Values MessageBox.Show(obj.ToString) Next End Sub End Class Visual C# .NET Code using System; using System.Net; using System.Drawing; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Windows.Forms; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using test.SystemSettingInterface;

namespace test.client {  public class Form1 : System.Windows.Forms.Form {     private ISystemSetting m_SystemSetting; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button3; private System.Windows.Forms.Button button4; private System.ComponentModel.Container components = null;

public Form1 {        InitializeComponent;

}

protected override void Dispose( bool disposing ) {        if( disposing ) {           if (components != null) {              components.Dispose; }        }         base.Dispose( disposing ); }

#region Windows Form Designer generated code ///      /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///      private void InitializeComponent {        this.button1 = new System.Windows.Forms.Button; this.button2 = new System.Windows.Forms.Button; this.button3 = new System.Windows.Forms.Button; this.button4 = new System.Windows.Forms.Button; this.SuspendLayout; //         // button1 //         this.button1.Location = new System.Drawing.Point(32, 16); this.button1.Name = &quot;button1&quot;; this.button1.Size = new System.Drawing.Size(208, 32); this.button1.TabIndex = 0; this.button1.Text = &quot;Initialize Remote Component&quot;; this.button1.Click += new System.EventHandler(this.button1_Click); //         // button2 //         this.button2.Location = new System.Drawing.Point(32, 58); this.button2.Name = &quot;button2&quot;; this.button2.Size = new System.Drawing.Size(208, 32); this.button2.TabIndex = 1; this.button2.Text = &quot;Count Hashtable elements&quot;; this.button2.Click += new System.EventHandler(this.button2_Click); //         // button3 //         this.button3.Location = new System.Drawing.Point(32, 100); this.button3.Name = &quot;button3&quot;; this.button3.Size = new System.Drawing.Size(208, 32); this.button3.TabIndex = 2; this.button3.Text = &quot;GetEnumerator (Error)&quot;; this.button3.Click += new System.EventHandler(this.button3_Click); //         // button4 //         this.button4.Location = new System.Drawing.Point(32, 142); this.button4.Name = &quot;button4&quot;; this.button4.Size = new System.Drawing.Size(208, 32); this.button4.TabIndex = 3; this.button4.Text = &quot;GetHashtable (work around)&quot;; this.button4.Click += new System.EventHandler(this.button4_Click); //         // Form1 //         this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(280, 198); this.Controls.AddRange(new System.Windows.Forms.Control[] {                                                                     this.button4,                                                                      this.button3,                                                                      this.button2,                                                                      this.button1}); this.Name = &quot;Form1&quot;; this.Text = &quot;Form1&quot;; this.ResumeLayout(false);

}       #endregion

[STAThread] static void Main {        Application.Run(new Form1); }

// Initialize the Hashtable private void button1_Click(object sender, System.EventArgs e)     {

try {           init; MessageBox.Show(&quot;Init success.&quot;); }        catch(System.Exception ex) {           MessageBox.Show(&quot;Unable to initialize remoting connection with test server:&quot; + ex.Message); }     }

// Connect to server using Remoting private void init {        string connect=&quot;tcp://&quot; + Dns.GetHostName + &quot;:9999/SystemSettingTest&quot;; m_SystemSetting=(ISystemSetting)RemotingServices.Connect(typeof(ISystemSetting), connect); m_SystemSetting.Test; }

// Verify the Hashtable has elements private void button2_Click(object sender, System.EventArgs e)     { try {           MessageBox.Show(this.m_SystemSetting.Count.ToString); }        catch(System.Exception ex) {           MessageBox.Show(ex.Message); }     }

// Problem method call private void button3_Click(object sender, System.EventArgs e)     { IDictionaryEnumerator T = (IDictionaryEnumerator)m_SystemSetting.GetEnumerator; // Get the Enumerator for Hashtable MessageBox.Show(T.Value.ToString); }

// Work Around method call private void button4_Click(object sender, System.EventArgs e)     { Hashtable ht; ht=m_SystemSetting.GetHashtable; foreach(Object obj in ht.Values) {           MessageBox.Show(obj.ToString); }     }   } } </li> On the Debug menu, click Start.</li> Click Initialize Remote Component. Click OK to close the Init success dialog box.</li> Click Count Hashtable elements.

You see 2 appear.</li> Click GetEnumerator (Error).

You receive the error in the &quot;Symptoms&quot; section.</li> If you click GetHashtable (work around) after step 18, you can successfully iterate through the Hashtable.</li></ol>

<div class="references_section">