Reputation: 50235
First off, I found the solution to the exception. I'm more curious why it generated the specific exception it did.
In my scenario I'm adding a POCO to a ListBox like so:
myListBox.Items.Add(myPOCO);
This was generating an OutOfMemoryException
. The problem was that ToString
off the POCO was returning null
. I added a string.IsNullOrEmpty
check to return a "safe" value when null and the exception went away.
Why does this generate an OutOfMemoryException
and not something else (say a NullReferenceException
)?
EDIT: Items are added in a for loop.
Full call stack (company-specific references removed) is below. One thing to note -- the list box is empty when this is called.
System.OutOfMemoryException was unhandled
Message="List box contains too many items."
Source="System.Windows.Forms"
StackTrace:
at System.Windows.Forms.ListBox.NativeAdd(Object item)
at System.Windows.Forms.ListBox.ObjectCollection.AddInternal(Object item)
at System.Windows.Forms.ListBox.ObjectCollection.Add(Object item)
at <FORM>_Load(Object sender, EventArgs e) in <PATH>\<FORM>.cs:line 52
at System.Windows.Forms.Form.OnLoad(EventArgs e)
at System.Windows.Forms.Form.OnCreateControl()
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.Control.CreateControl()
at System.Windows.Forms.Control.WmShowWindow(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WmShowWindow(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow)
at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
at System.Windows.Forms.Control.set_Visible(Boolean value)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.RunDialog(Form form)
at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
at System.Windows.Forms.Form.ShowDialog()
at <APP>.Program.Main() in <PATH>\Program.cs:line 25
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
at System.Runtime.Hosting.ManifestRunner.Run(Boolean checkAptModel)
at System.Runtime.Hosting.ManifestRunner.ExecuteAsAssembly()
at System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext activationContext, String[] activationCustomData)
at System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext activationContext)
at System.Activator.CreateInstance(ActivationContext activationContext)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssemblyDebugInZone()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Upvotes: 10
Views: 7631
Reputation: 11
If you get such error then carefully check your code where you are calling listbox.Items.Add
or listbox.DataSource = xxxxx
. Whatever Object
Type is added or bound to list box must return non Null in ToString()
method. By default .net returns Type name if you do not override ToString()
method.
If you have implemented your own version of ToString()
(with override keyword) then make sure method has fail safe to returns non NULL string. See example below how you can add extra 2 lines to avoid such odd error which many people will find totally misleading.
//listbox.Items.Add(new MyItem());
//--or--
//listbox.DataSource= new List<MyItem>(){ new MyItem() };
public class MyItem
{
public string Name { get; set; }
public string Label { get; set; }
public override string ToString()
{
//Not adding below 2 lines might throw OutOfMemoryException in listbox.Items.Add or listbox.DataSOurce = somelist
if (string.IsNullOrEmpty(Label)) //Added this check to avoid
return this.GetType().Name; //Return string.Empty or something other than null else you will get OutOfMemoryException error when you add or bind this object to listbox
return Label;
}
}
Upvotes: 1
Reputation: 1039080
This is because of the way System.Windows.Forms.ListBox.NativeAdd
method is implemented:
private int NativeAdd(object item)
{
int num = (int) base.SendMessage(0x180, 0, base.GetItemText(item));
switch (num)
{
case -2:
throw new OutOfMemoryException();
case -1:
throw new OutOfMemoryException(SR.GetString("ListBoxItemOverflow"));
}
return num;
}
The GetItemText method uses ToString()
on the object which returns null
and so a message is sent with null
parameter, which in turn returns an invalid pointer and you enter the second case which throws the exception.
Upvotes: 23
Reputation: 46713
When the underlying LB_ADDSTRING
Windows API call fails, WinForms always returns an OutOfMemoryException
. A comment in the .NET Framework Reference Source explains why:
// On some platforms (e.g. Win98), the ListBox control
// appears to return LB_ERR if there are a large number (>32000)
// of items. It doesn't appear to set error codes appropriately,
// so we'll have to assume that LB_ERR corresponds to item
// overflow.
//
throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow));
Upvotes: 12