RSS 2.0 Feed
2007-11 Entries
摘要: Paint事件被触发了多少次?比较简单的方式,我们自己做一个perfmon能用的counter。看代码:  1        private void button1_Click(object sender, EventArgs e) 2        { 3             4            if (!PerformanceCounterCategory.Exists("GDI+ Monitor")) 5            { 6                CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); 7 8                CounterCreationData totalPaint = new CounterCreationData(); 9                totalPaint.CounterName = "# operator executed";10                totalPaint.CounterHelp = "Counts of OnPaint events called";11                totalPaint.CounterType = PerformanceCounterType.NumberOfItems32;1213                ccdc.Add(totalPaint);1415                PerformanceCounterCategory.Create("GDI+ Monitor", "Some counters for GDI+ objects", PerformanceCounterCategoryType.MultiInstance, ccdc);16            }17        }好,点一下button1之后,我们就可以在perfmon中看到GDI+ Monitor这个category了,然后只有一个counter,就是# operator executed然后在winform中增加这么一段:            paintcall = new PerformanceCounter();            paintcall.CategoryName = "GDI+ Monitor";            paintcall.CounterName = "# operator executed";            paintcall.MachineName = ".";            paintcall.ReadOnly = false;好了,代码里面可以用paintcall这个变量了。我们现在的需求是监视Paint被触发了多少次,那么可以在代码中这么写:  1            Graphics g2 = e.Graphics; 2 3            Bitmap bmp = new Bitmap(this.Width, this.Height); 4            Graphics g = Graphics.FromImage(bmp); 5 6            paintcall.Increment(); 7 8            Rectangle r = new Rectangle(0, 0, this.Width, this.Height); 9            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);1011            g2.DrawImage(bmp, new Point(0, 0));1213            g.Dispose();14            g = null;1516            bmp.Dispose();17            bmp = null;看上面第6行,这句会把paint call增加一。当然,其他方法有很多,这里不写了。好,跑一下perfmon,然后把我们新增加的counter add上,嗯,可以看到当窗口无效的时候,计数器就增加了。有一个小的细节,当窗口稍微改动大小的时候,你会发现Paint被调用了多次,这个实在很郁闷。so,稍微做点手脚:  1        private bool resize = false; 2        private void Form1_ResizeEnd(object sender, EventArgs e) 3        { 4            resize = false; 5            this.Invalidate(); 6        } 7 8        private void Form1_ResizeBegin(object sender, EventArgs e) 9        {10            resize = true;11        }然后我们修改一下Paint事件的代码如下:  1        private void Form1_Paint(object sender, PaintEventArgs e) 2        { 3            if (true == resize) return; 4 5            Graphics g2 = e.Graphics; 6 7            Bitmap bmp = new Bitmap(this.Width, this.Height); 8            Graphics g = Graphics.FromImage(bmp); 910            paintcall.Increment();1112            Rectangle r = new Rectangle(0, 0, this.Width, this.Height);13            g.FillRectangle(new LinearGradientBrush(r, Color.Red, Color.Blue, LinearGradientMode.BackwardDiagonal), r);1415            g2.DrawImage(bmp, new Point(0, 0));1617            g.Dispose();18            g = null;1920            bmp.Dispose();21            bmp = null;22        }注意第三行,我们判断,如果在resize过程中,那么就直接返回。这样,虽然界面像白板一样,但是却少了很多Paint操作,从性能上会好不少。 ...[阅读全文]

posted @ | Feedback (4) | Filed Under [ VS.NET ]

摘要: GDI+自身是否有leak,我们不去管,现在说的是.NET代码中的处理。首先看我这个简单的helper using System;using System.Diagnostics;using System.Text;using System.Runtime.InteropServices;public class MemoryReport{    [DllImport("user32.dll", CharSet=CharSet.Auto)]    public static extern long GetGuiResources(IntPtr hProcess, long flag);    public static string Write(){        Process p = Process.GetCurrentProcess();        ing hcount = p.HandleCount;        long psize = p.PrivateMemorySize64;        long vsize = p.VirtualMemorySize64;        long workset = p.WorkingSet64;        long gcsize = GC.GetTotalMemory(false);        int gdiobjs = (int)(GetGdiResources(p.Handle,0));        int userobjs = (int)(GetGdiResources(p.Handle,1));        return String.Format("Handle count:{0:N0},Private Bytes:{1:N0}K, Virtual Bytes:{2:N0}K, Working Set:{3:N0}K, GC Heap Size:{4:N0}K, GDI Objects:{5:N0}, User Objects:{6:N0}", hcount, psize>>10, vsize>>10, workset>>10, gcsize>>10, gdiobjs, userobjs);}            }现在我们做一个winform程序,放一个button,在click里面写如下测试代码: for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);}MessageBox.Show(MemoryReport.Write());观察每次的结果,Private Bytes/ Virtual Bytes/ Working Set基本是一个上涨的走向。但是我们感兴趣的是这几个地方:1、Handle count:这个值一般会波动变化,在这里例子里面,你把程序运行起来后,用taskmgr来观察Handle Count一栏(默认的没有,需要你自己手工添加这个column),一般是100以下。然后点一下按钮,handle count会增长1000左右,再点几次,会在1000上下波动,不会继续增长。2、GDI Objects:这个值每次会增加10003、你连续点10次这个button,嘣!程序crash了。。。如果看dump里面的异常,会是什么bitmap的一个构造方法的parameter不正确。4、GC Heap Size很小很小,我这里是2M。但是virtual size很大。对于1,为什么这样,我不清楚;对于2,原因在于GetHbitmap返回的是一个Unmanged resource,GC不会回收(即使你使用了GC.Collect()这个值也不会下降的);对于3,OS默认的每个process的GDI objects上限为10000个,我们代码中是循环了1000次,所以如果你点了10次button,程序就会完蛋。对于4,说明leak的资源是unmanged resource,so,gc heap看起来很乖。那么,如何修复上面的问题2?既然是unmanged resource,我们就要从unmanged找起。 [DllImport("gdi32.dll", CharSet=CharSet.Auto)]public static extern IntPtr DeleteObject(IntPtr hobj);for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);     DeleteObject(ip);}MessageBox.Show(MemoryReport.Write());嗯,再运行一次,好了!GDI objects稳定了,再也没有变化过。不过,我们修改一下循环计数器,到5000吧,然后观察Handle count,波动的比较厉害,内存相关的三组数值也稍有变化。好,我们再修改一次程序 [DllImport("gdi32.dll", CharSet=CharSet.Auto)]public static extern IntPtr DeleteObject(IntPtr hobj);for(int i=0;i<1000;i++){    Bitmap b = new Bitmap("c:\\1.bif");    IntPtr ip = b.GetHbitmap();    Bitmap b2 = Bitmap.FromHbitmap(ip);     b.Dispose();     b2.Dispose();     DeleteObject(ip);}MessageBox.Show(MemoryReport.Write());重新run一次,嗯,这个世界终于清静了,handle count/gdi resource/ mem size都很平稳。so,总结一下,对于类似上面的、可能被反复调用的type,如GDI+ obj,可以考虑使用完毕后立刻Dispose,这样可以被GC提早回收。对于返回一个IntPtr的方法,要仔细看,是不是需要再call win32里面对应的Delete方法。对于绝大多数GDI+ obj,我们只需要DeleteObject即可,但是对于icon,我记着是另外一个函数,有兴趣的可以在msdn上查一下。...[阅读全文]

posted @ | Feedback (11) | Filed Under [ VS.NET ]

摘要:1、这本书对于初学者没有太大用处2、这本书对于眼中只有架构、自己不写程序的、鄙视代码的人没有用处3、这本书对于非微软的人用处不算太大,你不知道ms内部的数据结构,你没有private symbols。4、这本书对于微软的人用处不算太大,搞debug的就那么几号人 5、这本书对于在客户现场被骂的狗血喷头的、自己即使架了.NET IDE也不知道如何找出问题的人很有用处   如果你是第五种人,疯狂购买吧!...[阅读全文]

posted @ | Feedback (4) | Filed Under [ Solution VS.NET ]

摘要:此文是偶的偶像和哥们的,转过来,替他做一下宣传。偶会买10本,9本送人,有要的,现在报名。 (原来公司大量的COM+和.NET的case,都是熊老大做的)   Windows 高效排错《Windows 高效排错》 可以在CSDN读书频道预览了 地址在这里:http://book.csdn.net/bookfiles/555/ 读书频道的排版有些问题,看起来不是很舒服。如果想看PDF的,可以在这里下载 http://www.cnblogs.com/lixiong/archive/2006/08/16/475520.html 纸板书籍估计在11月中下旬面世  现在在China-pub, dearbook等网上书店已经有介绍,地址分别 http://www.china-pub.com/computers/common/info.asp?id=37008http://www.dearbook.com.cn/book/230727 您的看法非常重要。如果你看过这本书的PDF,了解本书的内容和潜在读者,请您在上面的链接中留下你的观点,便于其它不了解这本书的人选择。 如果您通过这本书介绍的方法解决过实际的问题,非常希望您能够分享您的经验。您可以发邮件到eparg@msn.com。如果您的分享能够帮助其它读者,我非常希望能够送书给您作为答谢。 如果您对书中所述内容有疑惑,也欢迎您写信来讨论。我会尽快回复。如果我们之间的讨论对其他读者有帮助,我也会放到网上,同时送书给您作为答谢。 === 上面是官腔了。我个人想说的话其实是: 1) 书前言里面说,"2007年年初我不再做技术支持。希望这本书能帮我记录下这一段美好的经历",其中最让我难忘的就是跟xiao,elan和leo站在水房门口讨论case的日子2) 常来我blog踩的同学们,你们要书的话把地址留给我。等我拿到了我给大家发。(估计要等一段时间。出版社就给我6本。剩下的我争取去批发)3) 跟我一起做过case的,如果你需要的话也把地址留给我4) 多卖一本书,我可以多拿3块钱不到。整本书的稿费还不够在上海买半个车牌,或者买个厕所。我一点也不关心字面上的销量。我之所以花那么多时间来做这个亏本生意,目的只有一个,就是希望跟喜欢调试的人分享其中的过程,让真正需要这方面文章的人有所参考。所以如果你身边有这本书的潜在读者,劳烦你推荐一下。...[阅读全文]

posted @ | Feedback (27) | Filed Under [ Solution VS.NET ]