|
.Net Windows Form程序开发较ASP.Net而言,是较麻烦的一件事,原因如下: ASP.Net应用是面向ISAPI与W3C编程,核心的功能,IIS与浏览器都给你做了。你的工作差不多就是读写数据库、生成对象、吐Html或javascript而已。总之,这个“舞台”很小。 而Windows Form程序则不然,它是架设在操作系统上的,“舞台”很大,牵涉的知识点很多。比如:窗体自绘、多线程管理、进程间通信、跨线程资源访问、平台调用、托管与非托管的互操作、内存占用的优化、跨平台、软件升级。下面就将我在实际项目中的收获拿出来与大家分享。 记得清理干净你的对象 在.Net中,托管资源可以交与GC释放,但非托管资源呢,比如说Bitmap,记得在用完后手动Dispose。 而static的托管资源,GC也不会释放,因为它的生命周期与进程一致。在编程时,就要注意了,如果某个窗体被某static对象引用着,即使你关掉窗体,也不会释放资源的。所以说,如果你要把一个窗体做成单例模式,记得在窗体关闭时,将单例对象的static对象置null。否则,这个窗体就一直在内存中呆着,直接进程退出。 此外,对象中的事件也可能会让对象一直被别的对象引用,这样GC也不会释放它。所以在窗体关闭或对象Dispose时,记得将把事件给“-=”了。 当对象被轮“X”时 如果对象被两个或以上的线程访问,就要考虑并发的情况。常用的方式是lock。说到lock就不得不提“最佳实践”,建议为每个需要被lock的对象建立一个private static 的object对象,然后lock该对象,据说是可以避免死锁。但究其原理,高手们从来都是神秘一笑,反正大家按“最佳实践”来就行。 谁分配,谁释放!--关于互操作时的内存分配 我的项目是用.Net写的,但其中也调用了一个C++的组件,接口签名如下: IntPtr GetCertString()。即.Net向C++获取一个指针地址。 程序在xp,2003上运行正常,但Vista下却出以下错误: System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏 MD,这个问题困扰了我们长时间,最后才发现,是C++的接口方法中分配了一块内存(用于存放一个字符串),然后没有释放(当然不能释放,因为要返回给.Net),结果在返回给.Net时报上面的错。 仔细分析,我们知道:C++是不会释放这个字符串的,.Net也不会,因为它本来就是C++分配的。所以这就存在“内存泄露”的危险。而Vista对内存安全的要求很严格,在运行时直接给你报异常了。 所以后来我把接口改成:void GetCertString(StringBuilder s),然后在调用前分配StringBuilder,将该StringBuilder传给C++,C++“搞”完后,返回给.Net,然后由GC来释放StringBuilder。这就做到了“谁分配,谁释放”。 退出程序的麻烦事 如何彻底退出你的进程? Application.Exit? Environment.Exit? Process.GetCurrentProcess().Kill? 后两种太暴力。推荐用第一种,但如果程序中有启动其他线程时要注意了,记得将设置Thread.IsBackground=true,否则,这些线程是不会随主线程退出的。 还有一个比较麻烦的问题,如果在Application.Exit的过程中,又有一个新线程启动,Application.Exit可能就无法终止它了。这个问题也折腾了我很长时间--明明已经退出了,但任务管理器里还有进程名。怎么办哩? 我的办法就是在Application.Exit后加上System.Environment.Exit(0)。文明关闭搞不死你,就再加个暴力的。就叫“先礼后兵”。 Process.Start的困惑 我的程序有这么一个逻辑,程序A.exe启动程序B.exe,程序B再启动程序C.exe。结果发现,进程C死活就启动不了。但如果直接双击B,进程C是可以启动的。 原因是这样的:B与C在同一文件夹,启动时用的是相对路径,也就是直接Process.Start("C.exe")。而A是在另一个文件夹,结果在B的进程中,Process.Start("C.exe")就到A的文件夹找C.exe了。 后来修改了A的代码,在启动B时指定domain,也就是B运行的上下文,就OK了。 当然如果改B,将Process.Start("C.exe")改成Process.Start(AppDomain.CurrentDomain.BaseDirectory +"C.exe")也可以的。 最好别用WebService 由于我们的.Net程序要运行在没有.Net Framework的平台上,所以我们借助了Salamander技术。先说说Salamander是什么。 它就是将.Net所需的组件给你提炼出来,与你的程序放在一起,然后做一个.Net虚机的引导程序VM.exe。运行VM.exe时,先加载.Net运行时与相关组件,然后再启动你自己的程序。 其实说白了,它与装了.Net Framework的原理差不多,只不过有Framework的机器是操作系统负责启动.Net Runtime。而Salamander时在运行VM.exe时启动Runtime。 而.Net WebService是依赖反射技术的(我是把WebService组件反编译后知道的),反射技术需要动态编译(我觉得是的,高手请指正),而编译就得用到csc.exe。 后面的原理我就不知道了,反正结果就是WebService在没装.Net Framework的XP机器上不好使。 后来我就回到“原始社会”了,自己用HttpWebRequest做了一个与WebService类似的类。 搞定了。 如何在新线程里创建窗体 如何在一个新线程里创建一个窗体?看下面的代码: public Form1() { Thread thread = new Thread(new ThreadStart(AAA)); thread.Start(); InitializeComponent(); } void AAA() { Form f = new Form(); f.Show(); } 结果就是,窗体闪一下就没了。改成下面的就没问题: public Form1() { Thread thread = new Thread(new ThreadStart(AAA)); thread.Start(); InitializeComponent(); } void AAA() { Form f = new Form(); Application.Run(f); } 不难发现,Application.Run里面其实就是一个死循环,以便确保该线程永远运行着。
|