您的位置:首页 > 其它

.Net Windows Form开发心得,与大家分享

2008-12-05 17:13 309 查看
  .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里面其实就是一个死循环,以便确保该线程永远运行着。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: