TreeView checkbox gdi memory leak

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

Guest

hi there i've a test program that creates a treeview and destroys it over and
over,
i keep track of the gdi object count for the process and see if they are ok.

However when i switch on checkboxes for my test app i get a gdi handle leak

anyone got any ideas on this one?

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace TreeViewMemoryLeak
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

// get process
m_processHandle = GetCurrentProcess();

// update status
OnTimer_Tick(this, null);
}

private void OnStartStop_Click(object sender, EventArgs e)
{
// invert state
m_isRunning = !m_isRunning;

// disbale if running
m_chkUseCheckboxes.Enabled = !m_isRunning;
m_btnStartStop.Text = (m_isRunning) ? "Stop loop" : "Start
create / dispose loop";

// running?
if (m_isRunning)
{
// reset
m_lblSummary.Text = "";
m_treeViewCount = 0;
m_initialGdiCount = GetGuiResources(m_processHandle,
(uint)ResourceType.Gdi);

// loop
while (m_isRunning)
{
CreateDisposeTreeView();
++m_treeViewCount;
}

// how many GDI objects per tree view?
uint gdiObjects = GetGuiResources(m_processHandle,
(uint)ResourceType.Gdi) - m_initialGdiCount;
m_lblSummary.Text = string.Format(
"{0} TreeViews were created and disposed with a {1:0.0}
GDI objects leak per TreeView",
m_treeViewCount,
gdiObjects / (float)m_treeViewCount);
}
}

private void CreateDisposeTreeView()
{
// create
TreeView tvw = new TreeView();
tvw.Location = new Point(20, 20);
tvw.Size = new Size(100, 100);
tvw.CheckBoxes = m_chkUseCheckboxes.Checked;
m_pnlContainer.Controls.Add(tvw);

// do some gui processing
Application.DoEvents();

// dispose
m_pnlContainer.Controls.Remove(tvw);
tvw.Dispose();
tvw = null;

// do some gui processing
Application.DoEvents();

}

private void OnTimer_Tick(object sender, EventArgs e)
{
// update status bar
if (m_processHandle != IntPtr.Zero)
{
// memory
PROCESS_MEMORY_COUNTERS counters = new
PROCESS_MEMORY_COUNTERS();
GetProcessMemoryInfo(m_processHandle, out counters, 40);
m_sslMemory.Text = string.Format("Memory usage: {0:#,##0}
KBytes", counters.WorkingSetSize / 1024);
m_sslMemory.Invalidate();

// gdi
m_sslGdi.Text = string.Format("GDI Objects: {0}",
GetGuiResources(m_processHandle, (uint)ResourceType.Gdi));
m_sslGdi.Invalidate();

// objects
m_sslObjectCount.Text = string.Format("TreeViews: {0}",
m_treeViewCount);
m_sslObjectCount.Invalidate();
}
}

#region Win32 API functions

/// uiFlags: 0 - Count of GDI objects
/// uiFlags: 1 - Count of USER objects
/// - Win32 GDI objects (pens, brushes, fonts, palettes, regions,
device contexts, bitmap headers)
/// - Win32 USER objects:
///      - WIN32 resources (accelerator tables, bitmap resources,
dialog box templates, font resources, menu resources, raw data resources,
string table entries, message table entries, cursors/icons)
/// - Other USER objects (windows, menus)
///
[DllImport("user32.dll")]
private static extern uint GetGuiResources(IntPtr hProcess, uint
uiFlags);

private enum ResourceType
{
Gdi = 0,
User = 1
}

[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();

[DllImport("psapi.dll")]
private static extern int GetProcessMemoryInfo(IntPtr hProcess, out
PROCESS_MEMORY_COUNTERS counters, int size);

[StructLayout(LayoutKind.Sequential, Size=40)]
private struct PROCESS_MEMORY_COUNTERS
{
public int cb;
public int PageFaultCount;
public int PeakWorkingSetSize;
public int WorkingSetSize;
public int QuotaPeakPagedPoolUsage;
public int QuotaPagedPoolUsage;
public int QuotaPeakNonPagedPoolUsage;
public int QuotaNonPagedPoolUsage;
public int PagefileUsage;
public int PeakPagefileUsage;
}

#endregion

private IntPtr m_processHandle = IntPtr.Zero;
private bool m_isRunning = false;
private int m_treeViewCount = 0;
private uint m_initialGdiCount = 0;
}
}
 
Back
Top