在C#中,我使用它來得到一個窗口的圖標:如何測試由未發佈的GDI對象引起的內存泄漏?
IntPtr IconHandle = SendMessage(hwnd, WM_GETICON ...);
原因,SendMessage函數是
的DllImport( 「user32.dll中」)。
AFAIK,這是需要清理:
DestroyIcon(iconHandle);
(通過的DllImport( 「user32.dll中」)再次DestroyIcon。)
事情似乎做工精細,但
我想要什麼要知道的是:
如何確定內存泄漏正在如果
我註釋掉調用DestroyIcon地方()?
我打算做的是
把獲得圖標碼長的循環
內沒有調用DestroyIcon()。
要檢查內存泄漏,我天真的辦法是
檢查,如果「承諾費」,在
「窗口任務管理器」正在積累。
但是,經過100000次迭代循環後...
沒有什麼事情發生。 Windows XP仍然運行愉快。
我需要找出測試了這一點的方式,因爲
我想確保我的代碼是正確
釋放的非託管資源,在我的機器和
也是今後最終用戶。
我該如何測試?或者是我沒有足夠努力地測試
(例如改爲10^10次迭代測試)?
我張貼下面的測試代碼:
Form1.cs中:
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;
using System.Threading;
using System.Globalization;
namespace TestLeak
{
public partial class Form1 : Form
{
Thread th;
public Form1()
{
InitializeComponent();
}
private class CHwndItem
{
private IntPtr mHWnd;
private string m_Caption;
public string Caption
{
get { return m_Caption; }
set { m_Caption = value; }
}
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[PreserveSig]
public static extern uint GetModuleFileName
(
[In]
IntPtr hModule,
[Out]
StringBuilder lpFilename,
[In]
[MarshalAs(UnmanagedType.U4)]
int nSize
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);
private Icon m_Icon;
public Icon Icon
{
get { return m_Icon; }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
public const int GCL_HICONSM = -34;
public const int GCL_HICON = -14;
public const int ICON_SMALL = 0;
public const int ICON_BIG = 1;
public const int ICON_SMALL2 = 2;
private const Int32 ANYSIZE_ARRAY = 1;
private const UInt32 TOKEN_QUERY = 0x0008;
private const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint FILE_SHARE_DELETE = 0x00000004;
private const uint FILE_ATTRIBUTE_READONLY = 0x00000001;
private const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002;
private const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004;
private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
private const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
private const uint FILE_ATTRIBUTE_DEVICE = 0x00000040;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100;
private const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200;
private const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;
private const uint FILE_ATTRIBUTE_COMPRESSED = 0x00000800;
private const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000;
private const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;
private const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint GENERIC_EXECUTE = 0x20000000;
private const uint GENERIC_ALL = 0x10000000;
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private const int SHGFI_ICON = 0x100;
private const int SHGFI_USEFILEATTRIBUTES = 0x10;
public IntPtr HWnd
{
get { return mHWnd; }
set
{
mHWnd = value;
m_Icon = GetAppIcon(mHWnd);
uint thID;
GetWindowThreadProcessId(value, out thID);
IntPtr processHwnd = OpenProcess(0, false, (int)thID);
StringBuilder path = new StringBuilder(' ', 255);
GetModuleFileName(processHwnd, path, path.Length);
SHFILEINFO fi = new SHFILEINFO();
SHGetFileInfo(@"C:\Program Files\Mozilla Firefox\firefox.exe", FILE_ATTRIBUTE_NORMAL, ref fi, (uint)System.Runtime.InteropServices.Marshal.SizeOf(fi), SHGFI_LARGEICON | SHGFI_USEFILEATTRIBUTES);
//IntPtr hIcon = new IntPtr(
//CloseHandle(processHwnd);
//m_Icon = Icon.FromHandle(hIcon);
//DestroyIcon(hIcon);
}
}
public static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size > 4)
return GetClassLongPtr64(hWnd, nIndex);
else
return // new IntPtr(
GetClassLongPtr32(hWnd, nIndex);
}
[DllImport("user32.dll", EntryPoint = "GetClassLong")]
public static extern IntPtr GetClassLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetClassLongPtr")]
public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
public const int WM_GETICON = 0x7F;
public static Icon GetAppIcon(IntPtr hwnd)
{
int try_icon_type = ICON_SMALL2;
IntPtr iconHandle = SendMessage(hwnd, WM_GETICON, ICON_SMALL2, 0);
if (iconHandle == IntPtr.Zero)
{
try_icon_type = ICON_SMALL;
iconHandle = SendMessage(hwnd, WM_GETICON, try_icon_type, 0);
}
if (iconHandle == IntPtr.Zero)
{
try_icon_type = ICON_BIG;
iconHandle = SendMessage(hwnd, WM_GETICON, try_icon_type, 0);
}
// if (iconHandle == IntPtr.Zero)
// {
//try_icon_type = GCL_HICON;
// iconHandle = GetClassLongPtr(hwnd, try_icon_type);
// }
if (iconHandle == IntPtr.Zero)
{
try_icon_type = GCL_HICONSM;
iconHandle = GetClassLongPtr(hwnd, try_icon_type);
}
if (iconHandle == IntPtr.Zero)
return null;
System.Diagnostics.Debug.WriteLine(try_icon_type);
Icon icn = Icon.FromHandle(iconHandle);
DestroyIcon(iconHandle);
return icn;
}
}
int GetHandle()
{
if (txt_Dec.Text.Trim().Length > 0)
{
return int.Parse(txt_Dec.Text);
}
else
{
return int.Parse(txt_Hex.Text, NumberStyles.HexNumber);
}
}
private void button1_Click(object sender, EventArgs e)
{
th = new Thread(new ThreadStart(ThreadProc));
th.IsBackground = true;
th.Start();
}
private void ThreadProc()
{
for (int i = 0; i < int.Parse(textBox1.Text); i++)
{
CHwndItem hi = new CHwndItem();
hi.HWnd = new IntPtr(GetHandle());
Invoke(new MethodInvoker(delegate()
{
lbl_incr.Text = i.ToString();
}));
}
MessageBox.Show("Done");
}
private void button2_Click(object sender, EventArgs e)
{
CHwndItem hi = new CHwndItem();
hi.HWnd = new IntPtr(GetHandle());
pictureBox1.Image = hi.Icon.ToBitmap();
}
private void button3_Click(object sender, EventArgs e)
{
if (th.ThreadState == ThreadState.Running)
{
btn_Pause.Text = "Resume";
th.Suspend();
}
else
{
btn_Pause.Text = "Pause";
th.Resume();
}
}
}
}
Form1.Designer.cs:
namespace TestLeak
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.txt_Dec = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.button2 = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.lbl_incr = new System.Windows.Forms.Label();
this.btn_Pause = new System.Windows.Forms.Button();
this.txt_Hex = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// button1
//
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
this.button1.Location = new System.Drawing.Point(15, 99);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "Start";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(90, 64);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(81, 20);
this.textBox1.TabIndex = 1;
//
// txt_Dec
//
this.txt_Dec.Location = new System.Drawing.Point(90, 23);
this.txt_Dec.Name = "txt_Dec";
this.txt_Dec.Size = new System.Drawing.Size(81, 20);
this.txt_Dec.TabIndex = 2;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 29);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(86, 13);
this.label1.TabIndex = 3;
this.label1.Text = "Handle (decimal)";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 67);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(31, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Loop";
//
// button2
//
this.button2.Location = new System.Drawing.Point(167, 153);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(103, 23);
this.button2.TabIndex = 4;
this.button2.Text = "Test Handle Icon";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(167, 182);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(100, 50);
this.pictureBox1.TabIndex = 5;
this.pictureBox1.TabStop = false;
//
// lbl_incr
//
this.lbl_incr.AutoSize = true;
this.lbl_incr.Location = new System.Drawing.Point(23, 166);
this.lbl_incr.Name = "lbl_incr";
this.lbl_incr.Size = new System.Drawing.Size(10, 13);
this.lbl_incr.TabIndex = 3;
this.lbl_incr.Text = "-";
//
// btn_Pause
//
this.btn_Pause.Location = new System.Drawing.Point(15, 182);
this.btn_Pause.Name = "btn_Pause";
this.btn_Pause.Size = new System.Drawing.Size(75, 23);
this.btn_Pause.TabIndex = 6;
this.btn_Pause.Text = "Pause";
this.btn_Pause.UseVisualStyleBackColor = true;
this.btn_Pause.Click += new System.EventHandler(this.button3_Click);
//
// txt_Hex
//
this.txt_Hex.Location = new System.Drawing.Point(236, 23);
this.txt_Hex.Name = "txt_Hex";
this.txt_Hex.Size = new System.Drawing.Size(81, 20);
this.txt_Hex.TabIndex = 2;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(189, 29);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(32, 13);
this.label3.TabIndex = 3;
this.label3.Text = "(Hex)";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(318, 266);
this.Controls.Add(this.btn_Pause);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.button2);
this.Controls.Add(this.lbl_incr);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.txt_Hex);
this.Controls.Add(this.txt_Dec);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox txt_Dec;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label lbl_incr;
private System.Windows.Forms.Button btn_Pause;
private System.Windows.Forms.TextBox txt_Hex;
private System.Windows.Forms.Label label3;
}
}
你說「內存句柄」,它是KB還是每單元計數? 此外,我已經檢查了任務管理器的「選擇列」中的「GDI對象」,它仍然保持在4x ... 我會發布測試代碼,以便你們測試... – 2010-03-03 09:43:24
GDI對象是在單元。內存句柄指的是創建和使用多少個GDI句柄。如果你繼續創造並現在釋放,你會看到它的數量不斷增加。我猜如果你沒有看到它增加,WM_GETICON可能只給你一個參考地址,但不會創建新的對象。它是創建圖標的對象必須負責銷燬它 – 2010-03-03 09:49:43
其實,如果我泄漏GDI對象, 是否明顯是 我不需要專門測試它? 象例如,XP會出現不穩定等 – 2010-03-03 10:08:37