i'm part of a (small) development team who took over the development and support of a C#.Net program. It has ~ 300.000 LOC and the software design makes it impossible to change anything big without causing millions of side effects. It's like trying to turn a jungle full of poisenous snakes into a nice little garden. Using only a small scissor.
The application is a big WinForms-Application with Database access.
About the problem: A customer received our software and cannot run it. Unlike other customers, they have multiple Windows Server 2008 R1 terminal servers and installed the software on a network drive. Their users connect to one of the terminal servers and run our application (and others, like windows office etc) from the network drive. Our application however crashes after ~ 5 seconds without any notice. Our loading screen appears and closes again. The application produces a log file which shows this exception:
2014-08-04 11:15:23 [3372] ERROR – An exception occurred:
'OutOfMemoryException': Not enough memory.
System.Drawing
at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback, IntPtr callbackData)
at System.Drawing.Bitmap.MakeTransparent(Color transparentColor)
at System.Windows.Forms.ImageList.CreateBitmap(Original original, Boolean& ownsBitmap)
at System.Windows.Forms.ImageList.CreateHandle()
at System.Windows.Forms.ImageList.get_Handle()
at System.Windows.Forms.ImageList.GetBitmap(Int32 index)
at System.Windows.Forms.ImageList.ImageCollection.GetEnumerator()
at <our application>.InitializeHtmlResources()
The method that calls ImageCollection.GetEnumerator() is this:
void InitializeHtmlResources()
{
string baseDirPath = ... //It's in AppData/Local/<ourFolder>/Icons;
int index = -1;
foreach (Image image in UIResources.Icons.Images)
{
index += 1;
image.Save(baseDirPath + Path.DirectorySeparatorChar + index.ToString(), ImageFormat.Png);
}
}
Those images are stored inside a Resource.Icons.dll. The icons have their own project in the solution that contains of a few helper classes (like UIResources) and contains a folder with every icon we use + an xml where they are all listed, named and indexed. UIResources is a static class that allows access to the icons and the image list. This is how the property "Images" is initialized:
...
// Code snippet of Images Initialization
var ilist = new ImageList();
ilist.ColorDepth = ColorDepth.Depth32Bit;
ilist.ImageSize = new Size(16, 16);
ilist.TransparentColor = Color.Fuchsia;
UIResources.Icons.Images = ilist;
...
This method is used to extract an Icon from the DLL file.
static IEnumerable<IconInfo> GetIcons()
{
XDocument doc;
using (var stream = GetResourceStream("Our.Namespace.Resources.Icons.Icons.xml"))
{
doc = XDocument.Load(stream);
}
// ReSharper disable once PossibleNullReferenceException
foreach (var elem in doc.Element("Icons").Elements("Icon"))
{
int index = (int)elem.Attribute("Index");
var bmp = ReadIcon("Icons", (string)elem.Attribute("FileName"));
string name = (string)elem.Attribute("Name");
yield return new IconInfo(bmp, index, name);
}
}
static Bitmap ReadIcon(string kind, string fileName)
{
using (var stream = GetResourceStream("Our.Namespace.Resources." + kind + "." + fileName))
{
return new Bitmap(stream);
}
}
static Stream GetResourceStream(string resourceName)
{
return typeof(IconProvider).Assembly.GetManifestResourceStream(resourceName);
}
IconInfo is only a record containing the values.
And finally, the ImageList is filled with values:
foreach (var icon in GetIcons())
UIResources.Icons.Images.Add(icon.Name, icon.Image);
The application works fine. But when that customer runs the software on his terminal server via Remote Desktop, the application crashes and throws an OutOfMemory Exception in InitializeHtmlResources() right at the foreach-loop (when accessing the enumerator of ImageList).
The confusing part: A "OutOfMemory" exception is thrown, though the memory is neither full, nor is the 2 GB limit for 32bit application reached. The app peaks at 120 MB during loading.
I have absolutely no idea how this error is caused and spent the last 2-3 days trying to find a solution. I haven't.
I appreciate every bit of advice you can give me.
EDIT:
I tried disabling the InitializeHtmlResources-Method. This enabled the application to start. However: After working a few seconds with the application, an outofmemory exception appeared anyway. The cause is another ImageList accessor.
It works fine with Server 2012. We created a VM with Windows Server 2008 and the error happens there too.
We found the issue! Instead of
UIResources.Icons.Images.Add(icon.Name, icon.Image);
to fill the ImageList, we now use
UIResources.Icons.Images.Add(icon.Name, new Bitmap(icon.Image));
Now our application works on Windows Server 2008 :-)