Recently in .Net Category

c#中使用多线程

| 1 Comment

 

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;


using System.Threading;


namespace student
{
 /// <summary>
 /// Form1 的摘要说明。
 /// </summary>
 public class Form1 : System.Windows.Forms.Form
 {
  private System.Windows.Forms.Button button1;
  private System.Windows.Forms.Button button2;
  private System.Windows.Forms.Button button3;


  ArrayList threads = new ArrayList();


  private System.Windows.Forms.ListView listView1;
  private System.Windows.Forms.ColumnHeader columnHeader1;
  private System.Windows.Forms.ColumnHeader columnHeader2;
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private System.ComponentModel.Container components = null;


  public Form1()
  {
   //
   // Windows 窗体设计器支持所必需的
   //
   InitializeComponent();


   //
   // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   //
  }


  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }


  #region Windows 窗体设计器生成的代码
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {
   this.button1 = new System.Windows.Forms.Button();
   this.button2 = new System.Windows.Forms.Button();
   this.button3 = new System.Windows.Forms.Button();
   this.listView1 = new System.Windows.Forms.ListView();
   this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
   this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
   this.SuspendLayout();
   //
   // button1
   //
   this.button1.Location = new System.Drawing.Point(24, 216);
   this.button1.Name = "button1";
   this.button1.TabIndex = 1;
   this.button1.Text = "Add";
   this.button1.Click += new System.EventHandler(this.button1_Click);
   //
   // button2
   //
   this.button2.Location = new System.Drawing.Point(104, 216);
   this.button2.Name = "button2";
   this.button2.TabIndex = 2;
   this.button2.Text = "Del";
   this.button2.Click += new System.EventHandler(this.button2_Click);
   //
   // button3
   //
   this.button3.Location = new System.Drawing.Point(184, 216);
   this.button3.Name = "button3";
   this.button3.TabIndex = 3;
   this.button3.Text = "DelAll";
   this.button3.Click += new System.EventHandler(this.button3_Click);
   //
   // listView1
   //
   this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
                      this.columnHeader1,
                      this.columnHeader2});
   this.listView1.FullRowSelect = true;
   this.listView1.GridLines = true;
   this.listView1.Location = new System.Drawing.Point(0, 0);
   this.listView1.Name = "listView1";
   this.listView1.Size = new System.Drawing.Size(288, 208);
   this.listView1.TabIndex = 5;
   this.listView1.View = System.Windows.Forms.View.Details;
   //
   // columnHeader1
   //
   this.columnHeader1.Text = "线程编号";
   this.columnHeader1.Width = 81;
   //
   // columnHeader2
   //
   this.columnHeader2.Text = "value";
   this.columnHeader2.Width = 180;
   //
   // Form1
   //
   this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
   this.ClientSize = new System.Drawing.Size(292, 266);
   this.Controls.Add(this.listView1);
   this.Controls.Add(this.button3);
   this.Controls.Add(this.button2);
   this.Controls.Add(this.button1);
   this.Name = "Form1";
   this.Text = "Form1";
   this.Load += new System.EventHandler(this.Form1_Load);
   this.ResumeLayout(false);


  }
  #endregion


  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main()
  {
   Application.Run(new Form1());
  }


  private void Form1_Load(object sender, System.EventArgs e)
  {
  
  }


  private void button1_Click(object sender, System.EventArgs e)
  {
   Add();
  }


  private void button2_Click(object sender, System.EventArgs e)
  {
   Del();
  }


  private void button3_Click(object sender, System.EventArgs e)
  {


   while(threads.Count>0)
   {
    Thread t1 = (Thread)threads[0];
    if(t1.IsAlive)
    {
     t1.Abort();
    }
    threads.RemoveAt(0);
    lock(listView1)
    {
     listView1.Items.RemoveAt(0);
    }
   }
  }


  private void Add()
  {
   int count = threads.Count;
   if(count<10)
   {
    Thread t = new Thread(new ThreadStart(Process));
    t.Start();
    threads.Add(t);
    lock(listView1)
    {
     listView1.Items.Insert(count, new ListViewItem(new string[]{count.ToString(),"0"}));
    }
   }
  }


  private void Del()
  {
   int count = threads.Count;
   if(count>0)
   {
    Thread t1 = (Thread)threads[count-1];
    if(t1.IsAlive)
    {
     t1.Abort();
    }
    threads.RemoveAt(count-1);
    lock(listView1)
    {
     listView1.Items.RemoveAt(count-1);
    }
   }
  }



  private void Process()
  {
   int i = 1;
   while(true)
   {
    i++;
    int j = threads.IndexOf(Thread.CurrentThread);
    lock(listView1)
    {
     listView1.Items[j].SubItems[1].Text = i.ToString();
    }
    Thread.Sleep(0);
   }
  }
 }
}

一个很不错介绍session的文章

一个很不错介绍session的文章


摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。

目录:
一、术语session
二、HTTP协议与状态保持
三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用程序的session共享
八、总结
参考文档

一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义, “面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者 “一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session 里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie 可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按 Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html




这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分




浏览器在再次访问goolge的资源时自动向外发送cookie

 


使用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。




IE也可以设置在接受cookie前询问

 


这是一个询问接受cookie的对话框。

四、理解session机制
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域, cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用, Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)


1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。


 

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。


 

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>

需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。


在Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。


 

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下


 

对于这样一种结构,在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session);

应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");

值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。


参考文档:
[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt



 

最近写个spider,一开始使用webclient来取html页,发现即使开再多的线程也只有两个并发连接,找了好长时间资料也没找到相关的资料(没想到仔细找找msdn,丢人哪)。没奈何,只好自己来实现一个http,大部分时间花在调试http通信上,耗了差不多一个星期,效果也就是仅仅能用而已。


结果有一天灵光一闪,用“webclient 并发”(竟然一直没想到,猪啊)google了一下,找到一位高人的文章,一下子解决了大问题。


两个办法:


1。限制针对应用程序域,为每个线程使用不同的应用程序域:


AppDomain appDomain = AppDomain.CreateDomain("");
appDomain.ExecuteAssembly(@"TestClient.exe");
AppDomain.Unload(appDomain);


2。配置文件:


<system.net>
 <connectionManagement>
  <add address="*" maxconnection="100"/>
 </connectionManagement>
</system.net>


当时气不打一处来,这么简单的东西,ms怎么不给列出来呢?!搜一下msdn,不好意思,里面明明就有,错怪了ms了。


接下来的问题是,通常情况下url里传递汉字都是会用urlencode一下变成utf-8字符串来传,可就有个别网站直接把汉字写上去。webclient呢,又自作聪明的把querystring都utf-8一下再发出去,于是就...404。还没找到办法解决,实在不行就用自己写那一段http通信代码来取了。


keep thinking, searching, googling....



C#中事件处理的个人体会

 

事件的简单解释:(来自于MSDN)
事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。引发(触发)事件的对象叫做事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。


在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。


与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调。


C#中使用事件需要的步骤:
1.创建一个委托
2.将创建的委托与特定事件关联(.Net类库中的很多事件都是已经定制好的,所以他们也就有相应的一个委托,在编写关联事件处理程序--也就是当有事件发生时我们要执行的方法的时候我们需要和这个委托有相同的签名)
3.编写事件处理程序
4.利用编写的事件处理程序生成一个委托实例

5.把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件
C#中事件产生和实现的流程:
1.定义A为产生事件的实例,a为A产生的一个事件
2.定义B为接收事件的实例,b为处理事件的方法
3.A由于用户(程序编写者或程序使用者)或者系统产生一个a事件(例如点击一个Button,产生一个Click事件)
4.A通过事件列表中的委托对象将这个事件通知给B

5.B接到一个事件通知(实际是B.b利用委托来实现事件的接收)
6.调用B.b方法完成事件处理


下面给出《C#入门经典》的例子,并做一定的解释:
//====================Connection.cs===========
//事件定义,也就是上面提到的A
//============================================
using System;
using System.Timers;


namespace Ch12Ex02
{
 /// <summary>
 /// Connection 的摘要说明。
 /// </summary>
 ///


 public delegate void MessageHandler(string messageText);//创建一个委托---步骤1
 public class Connection
 {
  public event MessageHandler MessageArrived;//将创建的委托和特定事件关联,在这里特定的事件为MessageArrived ---步骤2*/
/*上面这语句值得注意的地方是 MessageArrived方法被关联到MessageHandler上后,以后消息的传递就通过MessageHandler这个委托来实现,所以如果要能接收这个消息,就必须能支持MessageHandler这个委托,也就是要有一个和委托一样的签名 
  private Timer pollTimer;
  public Connection()
  {
   //
   // TODO: 在此处添加构造函数逻辑
   //
   pollTimer=new Timer(100);
   pollTimer.Elapsed+=new ElapsedEventHandler(CheckForMessage);
  }


  public void Connect()
  {
   pollTimer.Start();
  }


  public void Disconnect()
  {
   pollTimer.Stop();
  }


  public void CheckForMessage(object sender,ElapsedEventArgs e)
  {
   Console.WriteLine("Check for message.");
   Random random=new Random();
   if((random.Next(9)==0)&&(MessageArrived!=null))
   {
    MessageArrived("Hello Mum!");//程序编写者自己产生一个消息,消息的内容为Hello Mum!
   }
  }
 }
}


//====================Display.cs===========
//接收事件的类,也就是上面提到的B
//=========================================
using System;


namespace Ch12Ex02
{
 /// <summary>
 /// Display 的摘要说明。
 /// </summary>
 public class Display
 {
  public Display()
  {
   //
   // TODO: 在此处添加构造函数逻辑
   //


  }


  public void DisplayMessage(string message)  //a事件的最终处理函数,即上面的B.b,在main函数中,我们会使用本函数实现一个委托实例,并且添加到A的MessageArrived事件列表中--步骤3
  {
   Console.WriteLine("Message Arrived:{0}",message);
  }

 }
}


//====================Class1.cs=================
//一个控制台可执行类,主要是使用上面两个类的实例
//==============================================
using System;


namespace Ch12Ex02
{
 /// <summary>
 /// Class1 的摘要说明。
 /// </summary>
 class Class1
 {
  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
   //
   // TODO: 在此处添加代码以启动应用程序
   //


   Connection myConnection=new Connection();
   Display myDisplay=new Display();
   myConnection.MessageArrived+=new MessageHandler(myDisplay.DisplayMessage);//把委托添加到当前A的事件列表中----步骤4和步骤5


   myConnection.Connect();
   Console.ReadLine();
  }
 }
}


注:程序代码中事件定义部分,委托定义部分的相关知识可以参看MSDN.
值得注意的代码:
public delegate void MessageHandler(string messageText);//委托定义
public event MessageHandler MessageArrived;//定义一个事件,并且关联到一个委托上
myConnection.MessageArrived+=new MessageHandler(myDisplay.DisplayMessage);//产生一个委托实例,并通过+=运算符号添加到事件列表中 +=运算符号在这里非常的有用



多线程编程学习笔记

| 6 Comments

多线程编程笔记(一)
操作线程
1、操作实例
using System;
using System.Threading;


namespace AppThreadDemo
{
 class App
 {
  static public void threadMethod()
  {
    Console.WriteLine("\n执行ThreadMethod的线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
    Thread.Sleep(1000);  //休眠1秒
  }
  
  static public void ThreadCreate()
  {
   Console.WriteLine("\n执行ThreadCreate的线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
   ThreadStart entry =  new ThreadStart(App.threadMethod);//用线程体实例化Thread代理类
   Thread thread = new Thread(entry);
   thread.Start();
   thread.Join();//等待线程结束
  }


  static public void Main()
  {
   ThreadCreate();
  }
 }
}
注意:
A、多个线程可以执行同一个线程体:
 Thread thread = new Thread(entry);
 Thread thread1 = new Thread(entry);
 thread.Start();
 thread1.Start();
 thread.Join();
 thread2.Join();
B、一个线程体执行结束后,不能调用其Start再次启动,试图通过对已终止的线程调用 Start 来重新启动已中止的线程将引发 ThreadStateException。
C、一个启动了的线程不能再次启动。试图通过对已运行的线程调用 Start 来重新启动已中止的线程将引发 ThreadStateException。
(对于B和C,《.net核心技术--原理与架构》中描述为SecurityException异常,用编译器是ThreadStateException异常。应该是笔误吧!)
2、结束线程
A、Abort
 调用此方法后,在当前的线程上引发 ThreadAbortException,该异常终止此线程的过程。调用此方法通常会终止线程。
 如果对尚未启动的线程调用 Abort,则在线程调用 Start 后再调用Abort 将该线程将中止。例如:
   ThreadStart entry =  new ThreadStart(App.threadMethod);
   Thread thread = new Thread(entry);
   Console.WriteLine("终止线程");
   thread.Abort();
   Console.WriteLine("启动线程");
   thread.Start();   
   thread.Join();
   Console.WriteLine("结束线程");
   输出为:
    终止线程
    启动线程
    结束线程
   可以看出,执行这段代码后,Thread刚启动就马上死亡。
 如果对已挂起的线程调用 Abort,则线程被重新开始后再执行Abort将该线程将中止。
 如果对被阻塞或正在休眠的线程调用 Abort,则该线程被中断然后中止。
 下面这段代码没有进行异常处理,所以异常向外传递,直到最后由公共运行库捕获。
 using System;
 using System.Threading;


 namespace AppThreadDemo
 {
  class App
  {
   static public void threadMethod()
     {
      Console.WriteLine("\n执行ThreadMethod的线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
    Thread.Sleep(1000);
    Console.WriteLine("threadMethod线程结束");
     }
     static public void ThreadCreate()
   {
    Console.WriteLine("\n执行ThreadCreate的线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
    ThreadStart entry =  new ThreadStart(App.threadMethod);
    Thread thread = new Thread(entry);
    Console.WriteLine("终止线程");
   
    Console.WriteLine("启动线程");
    thread.Start();
    Thread.Sleep(500);//主线程休眠0.5秒,等待子线程运行
    thread.Abort();
    thread.Join();
    Console.WriteLine("主线程结束");
   }
   static public void Main()
   {
    ThreadCreate();
   }
  }
 }
 输出为:
 执行ThreadCreate的线程的代码为:1
 终止线程
 启动线程


 执行ThreadMethod的线程的代码为:2
 主线程结束
 在threadMethod中,启动子线程后,主线程休眠0.5等待子线程执行,子线程执行threadMethod的第1句代码后,休眠1秒等待主线程运行。此当主线程休眠并调用Abort方法结束子线程的时候,子线程仍处于休眠状态。着时候子线程被打断,并结束。所以threadMethod的最后没机会执行。
 若将threadMethod进行异常处理,
   static public void threadMethod()
  {
   try
   {
    Console.WriteLine("\n执行ThreadMethod的线程的代码为:{0}",Thread.CurrentThread.GetHashCode().ToString());
    Thread.Sleep(1000);
   }
   catch(Exception e)
   {
    Console.WriteLine("捕获到异常:{0}",e.ToString());
   }
   finally
   {
    Console.WriteLine("threadMethod执行了finally块");
   }
   Console.WriteLine("threadMethod线程结束");  //这句在调用Abort是不会执行到的;Interrupt会执行到的
  }
  线程体就会捕获到了一个ThreadAbortException异常,而且finally块也执行了。
 如果在执行非托管代码时线程忽略 ThreadAbortException,则当线程开始执行托管代码时,系统将再次引发 ThreadAbortException。
 
B、Interrupt
 该方法打断一个处于WaitSleepJoin状态的线城。
 在当前的线程调用该方法后产生ThreadInterruptedException异常。与Abort结束线程不同,调用Abort结束线程的方法将使该线程执行完Finally语句就结束,而调用Interrupt方法则允许线程执行完整个线程体后结束。
 如果线程体不采取异常处理措施,则线程在被打断的时候立刻结束。
 如果线程不处于WaitSleepJoin状态,则线程下次处于WaitSleepJoin时被打断。
摘自《.net核心技术--原理与架构》

反射

反射通常用于以下四种任务中:

1、浏览元数据
//使用反射
 public class Tester
 {
  public static void Main()
  {
   MyMath mm=new MyMath();
   Console.WriteLine("Calling DoFunc1(7). Result:{0}",mm.DoFunc1(7));

   System.Reflection.MemberInfo inf=typeof(MyMath); //Type类是访问元数据的主要方式
   object[] attributes;
   attributes=inf.GetCustomAttributes(typeof(BugFixAttribute),true);//传入要查询的属性信息的类型
   foreach(Object attribute in attributes)
   {
    BugFixAttribute bfa=(BugFixAttribute)attribute;
    Console.WriteLine("\nBugID:{0}",bfa.BugID);
    Console.WriteLine("Programmer:{0}",bfa.Programmer);
    Console.WriteLine("Date:{0}",bfa.Date);
    Console.WriteLine("Comment:{0}",bfa.Comment);
   }
   Console.ReadLine();
  }
 }
}

2、查询类型
A.反射一个配件
namespace Programming_CSharp
{
 using System;
 using System.Reflection;


 public class Test
 {
  public static void Main()
  {
   Assembly a=Assembly.Load("Mscorlib.dll");
   Type[] types=a.GetTypes();//Type代表着类型声明:类、接口、数组、值和枚举
   foreach(Type type in types)
   {
    Console.WriteLine("Type is {0}",type);
   }
   Console.WriteLine("{0} types found",types.Length);
   Console.ReadLine();
  }
 }
}

B.反射一个类型
public class Tester
 {
  public static void Main()
  {
   Type theType=Type.GetType("System.Reflection.Assembly");//用GetType()方法提取一个类型
   Console.WriteLine("\nSingle Type is {0} \n",theType);
   Console.ReadLine();
  }
 }

C.反射一个类型的成员

 public class Tester
 {
  public static void Main()
  {
   Type theType=Type.GetType("System.Reflection.Assembly");//用GetType()方法提取一个类型
   Console.WriteLine("\nSingle Type is {0} \n",theType);


   MemberInfo[] mbrInfoArray=theType.GetMembers();//列出所有方法、字段、属性
   foreach(MemberInfo mbrInfo in mbrInfoArray)
   {
    Console.WriteLine("{0} is a {1}",mbrInfo,mbrInfo.MemberType);
   }
   Console.ReadLine();
  }
 }

D.寻找类型方法

有时我们只关注方法,不需要字段、性质等。

 public class Tester
 {
  public static void Main()
  {
   Type theType=Type.GetType("System.Reflection.Assembly");//用GetType()方法提取一个类型
   Console.WriteLine("\nSingle Type is {0} \n",theType);


   //MemberInfo[] mbrInfoArray=theType.GetMembers(BindingFlags.LookupAll);//列出所有方法、字段、属性
   MemberInfo[] mbrInfoArray=theType.GetMethods();
   foreach(MemberInfo mbrInfo in mbrInfoArray)
   {
    Console.WriteLine("{0} is a {1}",mbrInfo,mbrInfo.MemberType);
   }
   Console.ReadLine();
  }
 }
E.查找特定成员

 public class Tester
 {
  public static void Main()
  {
   Type theType=Type.GetType("System.Reflection.Assembly");
   MemberInfo[] mbrInfoArray=theType.FindMembers(MemberTypes.Method,
    BindingFlags.Public |
    BindingFlags.Static |
    BindingFlags.NonPublic |
    BindingFlags.Instance |
    BindingFlags.DeclaredOnly,
    Type.FilterName,"Get*");
   foreach(MemberInfo mbrInfo in mbrInfoArray)
   {
    Console.WriteLine("{0} is a {1}",mbrInfo,mbrInfo.MemberType);
   }
   Console.ReadLine();
  }
 }

3、 //动态调用一个方法
 public class Tester
 {
  public static void Main()
  {
   Type theMathType=Type.GetType("System.Math");
   // Since System.Math has no public constructor, this
   // would throw an exception.
   //Object theObj=Activator.CreateInstance(theMathType);


   Type[] paramType=new Type[1];
   paramType[0]=Type.GetType("System.Double");


   MethodInfo cosineInfo=theMathType.GetMethod("Cos",paramType);
   
   Object[] parameters=new object[1];
   parameters[0]=45 * (Math.PI/180);


   //Object retvalue="/oblog4/cosineInfo.Invoke(theObj,parameters);
   Object" retvalue="/oblog4/cosineInfo.Invoke(theMathType,parameters);


   Console.WriteLine(""The cosine of a 45 degree angle {0}", retValue);
   Console.ReadLine();


  }
 }
4、发送反射

//代码来自《C#程序设计》



本文引用通告地址: http://blog.csdn.net/sillywxj/services/trackbacks/253931.aspx


数码相机的照片很多,而且文件名也不直观,所以写了个程序批量改名,就是把文件名都改成相机的拍照日期,呵呵,谁知道拍照日期不是那么好取,在csdn里开了一帖,谢谢网友帮忙,终于搞定!把代码放在下面了,说不定谁会用得着。
原帖地址:http://community.csdn.net/Expert/topic/4295/4295730.xml?temp=.662182
原文地址:http://www.codeproject.com/csharp/EXIFextractor.asp


////调用
            string strFile="fffff.jpg";//文件名
            System.Drawing.Bitmap bmp = new Bitmap(strFile);
            EXIF.EXIFextractor er = new EXIF.EXIFextractor(ref bmp, "\n");
            Console.Write(strFile + "\r\n");
            Console.Write(er["DTDigitized"]);//拍照日期
            foreach (EXIF.Pair pr in er)
            {
                Console.Write(pr.First + ":" + pr.Second + "\r\n");
            }
//////////////////////////////////////////////////////////////////////////////////////
///下面的两个文件处理JPG的EXIF信息
///EXIFextractor.cs
///
using System;
using System.Text;
using System.Collections;
using System.Drawing.Imaging;
using System.Reflection;
using System.IO;
 
namespace EXIF
{
        /// <summary>
        /// EXIFextractor Class
        ///
        /// </summary>
        public class EXIFextractor : IEnumerable
        {
            /// <summary>
            /// Get the individual property value by supplying property name
            /// These are the valid property names :
            ///
            /// "Exif IFD"
            /// "Gps IFD"
            /// "New Subfile Type"
            /// "Subfile Type"
            /// "Image Width"
            /// "Image Height"
            /// "Bits Per Sample"
            /// "Compression"
            /// "Photometric Interp"
            /// "Thresh Holding"
            /// "Cell Width"
            /// "Cell Height"
            /// "Fill Order"
            /// "Document Name"
            /// "Image Description"
            /// "Equip Make"
            /// "Equip Model"
            /// "Strip Offsets"
            /// "Orientation"
            /// "Samples PerPixel"
            /// "Rows Per Strip"
            /// "Strip Bytes Count"
            /// "Min Sample Value"
            /// "Max Sample Value"
            /// "X Resolution"
            /// "Y Resolution"
            /// "Planar Config"
            /// "Page Name"
            /// "X Position"
            /// "Y Position"
            /// "Free Offset"
            /// "Free Byte Counts"
            /// "Gray Response Unit"
            /// "Gray Response Curve"
            /// "T4 Option"
            /// "T6 Option"
            /// "Resolution Unit"
            /// "Page Number"
            /// "Transfer Funcition"
            /// "Software Used"
            /// "Date Time"
            /// "Artist"
            /// "Host Computer"
            /// "Predictor"
            /// "White Point"
            /// "Primary Chromaticities"
            /// "ColorMap"
            /// "Halftone Hints"
            /// "Tile Width"
            /// "Tile Length"
            /// "Tile Offset"
            /// "Tile ByteCounts"
            /// "InkSet"
            /// "Ink Names"
            /// "Number Of Inks"
            /// "Dot Range"
            /// "Target Printer"
            /// "Extra Samples"
            /// "Sample Format"
            /// "S Min Sample Value"
            /// "S Max Sample Value"
            /// "Transfer Range"
            /// "JPEG Proc"
            /// "JPEG InterFormat"
            /// "JPEG InterLength"
            /// "JPEG RestartInterval"
            /// "JPEG LosslessPredictors"
            /// "JPEG PointTransforms"
            /// "JPEG QTables"
            /// "JPEG DCTables"
            /// "JPEG ACTables"
            /// "YCbCr Coefficients"
            /// "YCbCr Subsampling"
            /// "YCbCr Positioning"
            /// "REF Black White"
            /// "ICC Profile"
            /// "Gamma"
            /// "ICC Profile Descriptor"
            /// "SRGB RenderingIntent"
            /// "Image Title"
            /// "Copyright"
            /// "Resolution X Unit"
            /// "Resolution Y Unit"
            /// "Resolution X LengthUnit"
            /// "Resolution Y LengthUnit"
            /// "Print Flags"
            /// "Print Flags Version"
            /// "Print Flags Crop"
            /// "Print Flags Bleed Width"
            /// "Print Flags Bleed Width Scale"
            /// "Halftone LPI"
            /// "Halftone LPIUnit"
            /// "Halftone Degree"
            /// "Halftone Shape"
            /// "Halftone Misc"
            /// "Halftone Screen"
            /// "JPEG Quality"
            /// "Grid Size"
            /// "Thumbnail Format"
            /// "Thumbnail Width"
            /// "Thumbnail Height"
            /// "Thumbnail ColorDepth"
            /// "Thumbnail Planes"
            /// "Thumbnail RawBytes"
            /// "Thumbnail Size"
            /// "Thumbnail CompressedSize"
            /// "Color Transfer Function"
            /// "Thumbnail Data"
            /// "Thumbnail ImageWidth"
            /// "Thumbnail ImageHeight"
            /// "Thumbnail BitsPerSample"
            /// "Thumbnail Compression"
            /// "Thumbnail PhotometricInterp"
            /// "Thumbnail ImageDescription"
            /// "Thumbnail EquipMake"
            /// "Thumbnail EquipModel"
            /// "Thumbnail StripOffsets"
            /// "Thumbnail Orientation"
            /// "Thumbnail SamplesPerPixel"
            /// "Thumbnail RowsPerStrip"
            /// "Thumbnail StripBytesCount"
            /// "Thumbnail ResolutionX"
            /// "Thumbnail ResolutionY"
            /// "Thumbnail PlanarConfig"
            /// "Thumbnail ResolutionUnit"
            /// "Thumbnail TransferFunction"
            /// "Thumbnail SoftwareUsed"
            /// "Thumbnail DateTime"
            /// "Thumbnail Artist"
            /// "Thumbnail WhitePoint"
            /// "Thumbnail PrimaryChromaticities"
            /// "Thumbnail YCbCrCoefficients"
            /// "Thumbnail YCbCrSubsampling"
            /// "Thumbnail YCbCrPositioning"
            /// "Thumbnail RefBlackWhite"
            /// "Thumbnail CopyRight"
            /// "Luminance Table"
            /// "Chrominance Table"
            /// "Frame Delay"
            /// "Loop Count"
            /// "Pixel Unit"
            /// "Pixel PerUnit X"
            /// "Pixel PerUnit Y"
            /// "Palette Histogram"
            /// "Exposure Time"
            /// "F-Number"
            /// "Exposure Prog"
            /// "Spectral Sense"
            /// "ISO Speed"
            /// "OECF"
            /// "Ver"
            /// "DTOrig"
            /// "DTDigitized"
            /// "CompConfig"
            /// "CompBPP"
            /// "Shutter Speed"
            /// "Aperture"
            /// "Brightness"
            /// "Exposure Bias"
            /// "MaxAperture"
            /// "SubjectDist"
            /// "Metering Mode"
            /// "LightSource"
            /// "Flash"
            /// "FocalLength"
            /// "Maker Note"
            /// "User Comment"
            /// "DTSubsec"
            /// "DTOrigSS"
            /// "DTDigSS"
            /// "FPXVer"
            /// "ColorSpace"
            /// "PixXDim"
            /// "PixYDim"
            /// "RelatedWav"
            /// "Interop"
            /// "FlashEnergy"
            /// "SpatialFR"
            /// "FocalXRes"
            /// "FocalYRes"
            /// "FocalResUnit"
            /// "Subject Loc"
            /// "Exposure Index"
            /// "Sensing Method"
            /// "FileSource"
            /// "SceneType"
            /// "CfaPattern"
            /// "Gps Ver"
            /// "Gps LatitudeRef"
            /// "Gps Latitude"
            /// "Gps LongitudeRef"
            /// "Gps Longitude"
            /// "Gps AltitudeRef"
            /// "Gps Altitude"
            /// "Gps GpsTime"
            /// "Gps GpsSatellites"
            /// "Gps GpsStatus"
            /// "Gps GpsMeasureMode"
            /// "Gps GpsDop"
            /// "Gps SpeedRef"
            /// "Gps Speed"
            /// "Gps TrackRef"
            /// "Gps Track"
            /// "Gps ImgDirRef"
            /// "Gps ImgDir"
            /// "Gps MapDatum"
            /// "Gps DestLatRef"
            /// "Gps DestLat"
            /// "Gps DestLongRef"
            /// "Gps DestLong"
            /// "Gps DestBearRef"
            /// "Gps DestBear"
            /// "Gps DestDistRef"
            /// "Gps DestDist"
            /// </summary>

C# MessageBox修改

 

参考以下两篇文章 ,完成了在中文系统上显示英文的对话框。非常高兴,这个问题困扰了我3天之久。现在终于解决了。


主要使用技术,关键词包括:C#中使用钩子函数。


http://www.codeproject.com/cpp/dMsgBox.asp
How to change the MessageBox window
http://www.codeproject.com/cs/miscctrl/MessageBoxChk.asp
A "Don't show this again" checkbox for the .NET MessageBox



构造函数


public MessageBoxInternal()
  {
   //
   // TODO: Add constructor logic here
   //
   m_cbt = new LocalCbtHook();
   m_cbt.WindowCreated += new  LocalCbtHook.CbtEventHandler(WndCreated);
   m_cbt.WindowDestroyed += new LocalCbtHook.CbtEventHandler(WndDestroyed);
   m_cbt.WindowActivated += new LocalCbtHook.CbtEventHandler(WndActivated);     
  }


 private void WndCreated(object sender, CbtEventArgs e)
  {
   
   if (e.IsDialogWindow)
   {
    m_bInit = false;
    m_hwnd = e.Handle;
   }
  }


  private void WndDestroyed(object sender, CbtEventArgs e)
  {
   if (e.Handle == m_hwnd)
   {
    m_bInit = false;
    m_hwnd = IntPtr.Zero;
//    if(BST_CHECKED == (int)SendMessage(m_hwndBtn,BM_GETCHECK,IntPtr.Zero,IntPtr.Zero))
//     m_bCheck = true;
   }
  }


  private void WndActivated(object sender, CbtEventArgs e)
  {
   if (m_hwnd != e.Handle)
    return;


   // Not the first time
   if (m_bInit)
    return;
   else
    m_bInit = true;


   if ( Thread.CurrentThread.CurrentUICulture.LCID == (new System.Globalization.CultureInfo("en-US")).LCID)  
   {
    SetDlgItemText(m_hwnd, IDOK ,"&OK");
    SetDlgItemText(m_hwnd, IDCANCEL ,"&Cancel");
    SetDlgItemText(m_hwnd, IDABORT ,"&Abort");
    SetDlgItemText(m_hwnd, IDRETRY ,"&Retry");
    SetDlgItemText(m_hwnd, IDIGNORE ,"&Ignore");
    SetDlgItemText(m_hwnd, IDYES ,"&Yes");
    SetDlgItemText(m_hwnd, IDNO ,"&No");
   }
 }




运用API函数获取系统信息

 

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



namespace WindowsApplication10
{
 /// <summary>
 /// Form1 的摘要说明。
 /// </summary>
 public class Form1 : System.Windows.Forms.Form
 {
  private System.Windows.Forms.GroupBox groupBox1;
  private System.Windows.Forms.GroupBox groupBox2;
  private System.Windows.Forms.GroupBox groupBox3;
  private System.Windows.Forms.GroupBox groupBox4;
  private System.Windows.Forms.Button button1;
  private System.Windows.Forms.Label AvailVirtual;
  private System.Windows.Forms.Label TotalVirtual;
  private System.Windows.Forms.Label AvailPageFile;
  private System.Windows.Forms.Label TotalPageFile;
  private System.Windows.Forms.Label AvailPhys;
  private System.Windows.Forms.Label TotalPhys;
  private System.Windows.Forms.Label MemoryLoad;
  private System.Windows.Forms.Label PageSize;
  private System.Windows.Forms.Label OemId;
  private System.Windows.Forms.Label ProcessorLevel;
  private System.Windows.Forms.Label ProcessorType;
  private System.Windows.Forms.Label NumberOfProcessors;
  private System.Windows.Forms.Label SystemDirectory;
  private System.Windows.Forms.Label WindowsDirectory;
  private System.Windows.Forms.Label Time;
  private System.Windows.Forms.Label Date;
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private System.ComponentModel.Container components = null;
  [DllImport("kernel32")]
  public static extern void GetWindowsDirectory(StringBuilder WinDir,int count);
       
  [DllImport("kernel32")]
  public static extern void GetSystemDirectory(StringBuilder SysDir,int count);


  [DllImport("kernel32")]
  public static extern void GetSystemInfo(ref CPU_INFO cpuinfo);


  [DllImport("kernel32")]
  public static extern void GlobalMemoryStatus(ref MEMORY_INFO meminfo);


  [DllImport("kernel32")]
  public static extern void GetSystemTime(ref SYSTEMTIME_INFO stinfo);


  [StructLayout(LayoutKind.Sequential)]
   public struct CPU_INFO
  {
   public uint dwOemId;
   public uint dwPageSize;
   public uint lpMinimumApplicationAddress;
   public uint lpMaximumApplicationAddress;
   public uint dwActiveProcessorMask;
   public uint dwNumberOfProcessors;
   public uint dwProcessorType;
   public uint dwAllocationGranularity;
   public uint dwProcessorLevel;
   public uint dwProcessorRevision;
  }


  //定义内存的信息结构
  [StructLayout(LayoutKind.Sequential)]
   public struct MEMORY_INFO
  {
   public uint dwLength;
   public uint dwMemoryLoad;
   public uint dwTotalPhys;
   public uint dwAvailPhys;
   public uint dwTotalPageFile;
   public uint dwAvailPageFile;
   public uint dwTotalVirtual;
   public uint dwAvailVirtual;
  }


  //定义系统时间的信息结构
  [StructLayout(LayoutKind.Sequential)]
   public struct SYSTEMTIME_INFO
  {
   public ushort wYear;
   public ushort wMonth;
   public ushort wDayOfWeek;
   public ushort wDay;
   public ushort wHour;
   public ushort wMinute;
   public ushort wSecond;
   public ushort wMilliseconds;
  }


  public Form1()
  {
   //
   // Windows 窗体设计器支持所必需的
   //
   InitializeComponent();


   //
   // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   //
  }


  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }


  #region Windows 窗体设计器生成的代码
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {
   this.groupBox1 = new System.Windows.Forms.GroupBox();
   this.groupBox2 = new System.Windows.Forms.GroupBox();
   this.groupBox3 = new System.Windows.Forms.GroupBox();
   this.groupBox4 = new System.Windows.Forms.GroupBox();
   this.button1 = new System.Windows.Forms.Button();
   this.Time = new System.Windows.Forms.Label();
   this.Date = new System.Windows.Forms.Label();
   this.TotalPhys = new System.Windows.Forms.Label();
   this.MemoryLoad = new System.Windows.Forms.Label();
   this.AvailPageFile = new System.Windows.Forms.Label();
   this.TotalVirtual = new System.Windows.Forms.Label();
   this.AvailPhys = new System.Windows.Forms.Label();
   this.TotalPageFile = new System.Windows.Forms.Label();
   this.PageSize = new System.Windows.Forms.Label();
   this.OemId = new System.Windows.Forms.Label();
   this.AvailVirtual = new System.Windows.Forms.Label();
   this.ProcessorLevel = new System.Windows.Forms.Label();
   this.NumberOfProcessors = new System.Windows.Forms.Label();
   this.ProcessorType = new System.Windows.Forms.Label();
   this.WindowsDirectory = new System.Windows.Forms.Label();
   this.SystemDirectory = new System.Windows.Forms.Label();
   this.groupBox1.SuspendLayout();
   this.groupBox2.SuspendLayout();
   this.groupBox3.SuspendLayout();
   this.groupBox4.SuspendLayout();
   this.SuspendLayout();
   //
   // groupBox1
   //
   this.groupBox1.Controls.Add(this.Date);
   this.groupBox1.Controls.Add(this.Time);
   this.groupBox1.Location = new System.Drawing.Point(8, 16);
   this.groupBox1.Name = "groupBox1";
   this.groupBox1.Size = new System.Drawing.Size(240, 128);
   this.groupBox1.TabIndex = 0;
   this.groupBox1.TabStop = false;
   this.groupBox1.Text = "groupBox1";
   //
   // groupBox2
   //
   this.groupBox2.Controls.Add(this.TotalVirtual);
   this.groupBox2.Controls.Add(this.AvailVirtual);
   this.groupBox2.Location = new System.Drawing.Point(296, 24);
   this.groupBox2.Name = "groupBox2";
   this.groupBox2.Size = new System.Drawing.Size(240, 120);
   this.groupBox2.TabIndex = 1;
   this.groupBox2.TabStop = false;
   this.groupBox2.Text = "groupBox2";
   //
   // groupBox3
   //
   this.groupBox3.Controls.Add(this.ProcessorType);
   this.groupBox3.Controls.Add(this.NumberOfProcessors);
   this.groupBox3.Controls.Add(this.MemoryLoad);
   this.groupBox3.Controls.Add(this.PageSize);
   this.groupBox3.Controls.Add(this.OemId);
   this.groupBox3.Controls.Add(this.ProcessorLevel);
   this.groupBox3.Location = new System.Drawing.Point(296, 160);
   this.groupBox3.Name = "groupBox3";
   this.groupBox3.Size = new System.Drawing.Size(240, 256);
   this.groupBox3.TabIndex = 1;
   this.groupBox3.TabStop = false;
   this.groupBox3.Text = "groupBox2";
   //
   // groupBox4
   //
   this.groupBox4.Controls.Add(this.AvailPageFile);
   this.groupBox4.Controls.Add(this.TotalPageFile);
   this.groupBox4.Controls.Add(this.AvailPhys);
   this.groupBox4.Controls.Add(this.TotalPhys);
   this.groupBox4.Controls.Add(this.WindowsDirectory);
   this.groupBox4.Location = new System.Drawing.Point(16, 160);
   this.groupBox4.Name = "groupBox4";
   this.groupBox4.Size = new System.Drawing.Size(240, 296);
   this.groupBox4.TabIndex = 2;
   this.groupBox4.TabStop = false;
   this.groupBox4.Text = "groupBox2";
   //
   // button1
   //
   this.button1.Location = new System.Drawing.Point(248, 456);
   this.button1.Name = "button1";
   this.button1.TabIndex = 3;
   this.button1.Text = "button1";
   this.button1.Click += new System.EventHandler(this.button1_Click);
   //
   // Time
   //
   this.Time.Location = new System.Drawing.Point(32, 48);
   this.Time.Name = "Time";
   this.Time.Size = new System.Drawing.Size(160, 23);
   this.Time.TabIndex = 0;
   this.Time.Text = "label1";
   //
   // Date
   //
   this.Date.Location = new System.Drawing.Point(32, 96);
   this.Date.Name = "Date";
   this.Date.Size = new System.Drawing.Size(160, 23);
   this.Date.TabIndex = 1;
   this.Date.Text = "label2";
   //
   // TotalPhys
   //
   this.TotalPhys.Location = new System.Drawing.Point(32, 208);
   this.TotalPhys.Name = "TotalPhys";
   this.TotalPhys.Size = new System.Drawing.Size(160, 23);
   this.TotalPhys.TabIndex = 2;
   this.TotalPhys.Text = "label3";
   //
   // MemoryLoad
   //
   this.MemoryLoad.Location = new System.Drawing.Point(40, 56);
   this.MemoryLoad.Name = "MemoryLoad";
   this.MemoryLoad.Size = new System.Drawing.Size(168, 23);
   this.MemoryLoad.TabIndex = 3;
   this.MemoryLoad.Text = "label4";
   //
   // AvailPageFile
   //
   this.AvailPageFile.Location = new System.Drawing.Point(32, 64);
   this.AvailPageFile.Name = "AvailPageFile";
   this.AvailPageFile.Size = new System.Drawing.Size(160, 23);
   this.AvailPageFile.TabIndex = 4;
   this.AvailPageFile.Text = "label5";
   //
   // TotalVirtual
   //
   this.TotalVirtual.Location = new System.Drawing.Point(40, 40);
   this.TotalVirtual.Name = "TotalVirtual";
   this.TotalVirtual.Size = new System.Drawing.Size(168, 23);
   this.TotalVirtual.TabIndex = 5;
   this.TotalVirtual.Text = "label6";
   //
   // AvailPhys
   //
   this.AvailPhys.Location = new System.Drawing.Point(32, 160);
   this.AvailPhys.Name = "AvailPhys";
   this.AvailPhys.Size = new System.Drawing.Size(160, 23);
   this.AvailPhys.TabIndex = 6;
   this.AvailPhys.Text = "label7";
   //
   // TotalPageFile
   //
   this.TotalPageFile.Location = new System.Drawing.Point(32, 112);
   this.TotalPageFile.Name = "TotalPageFile";
   this.TotalPageFile.Size = new System.Drawing.Size(160, 23);
   this.TotalPageFile.TabIndex = 7;
   this.TotalPageFile.Text = "label8";
   //
   // PageSize
   //
   this.PageSize.Location = new System.Drawing.Point(40, 96);
   this.PageSize.Name = "PageSize";
   this.PageSize.Size = new System.Drawing.Size(168, 23);
   this.PageSize.TabIndex = 8;
   this.PageSize.Text = "label9";
   //
   // OemId
   //
   this.OemId.Location = new System.Drawing.Point(40, 144);
   this.OemId.Name = "OemId";
   this.OemId.Size = new System.Drawing.Size(168, 23);
   this.OemId.TabIndex = 9;
   this.OemId.Text = "label10";
   //
   // AvailVirtual
   //
   this.AvailVirtual.Location = new System.Drawing.Point(40, 80);
   this.AvailVirtual.Name = "AvailVirtual";
   this.AvailVirtual.Size = new System.Drawing.Size(168, 23);
   this.AvailVirtual.TabIndex = 10;
   this.AvailVirtual.Text = "label11";
   //
   // ProcessorLevel
   //
   this.ProcessorLevel.Location = new System.Drawing.Point(40, 184);
   this.ProcessorLevel.Name = "ProcessorLevel";
   this.ProcessorLevel.Size = new System.Drawing.Size(168, 23);
   this.ProcessorLevel.TabIndex = 11;
   this.ProcessorLevel.Text = "label12";
   //
   // NumberOfProcessors
   //
   this.NumberOfProcessors.Location = new System.Drawing.Point(40, 24);
   this.NumberOfProcessors.Name = "NumberOfProcessors";
   this.NumberOfProcessors.Size = new System.Drawing.Size(168, 23);
   this.NumberOfProcessors.TabIndex = 12;
   this.NumberOfProcessors.Text = "label1";
   //
   // ProcessorType
   //
   this.ProcessorType.Location = new System.Drawing.Point(40, 216);
   this.ProcessorType.Name = "ProcessorType";
   this.ProcessorType.Size = new System.Drawing.Size(168, 23);
   this.ProcessorType.TabIndex = 13;
   this.ProcessorType.Text = "label2";
   //
   // WindowsDirectory
   //
   this.WindowsDirectory.Location = new System.Drawing.Point(32, 24);
   this.WindowsDirectory.Name = "WindowsDirectory";
   this.WindowsDirectory.Size = new System.Drawing.Size(160, 23);
   this.WindowsDirectory.TabIndex = 11;
   this.WindowsDirectory.Text = "label1";
   //
   // SystemDirectory
   //
   this.SystemDirectory.Location = new System.Drawing.Point(32, 400);
   this.SystemDirectory.Name = "SystemDirectory";
   this.SystemDirectory.TabIndex = 2;
   this.SystemDirectory.Text = "label2";
   //
   // Form1
   //
   this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
   this.ClientSize = new System.Drawing.Size(568, 507);
   this.Controls.Add(this.button1);
   this.Controls.Add(this.groupBox1);
   this.Controls.Add(this.groupBox2);
   this.Controls.Add(this.groupBox3);
   this.Controls.Add(this.groupBox4);
   this.Controls.Add(this.SystemDirectory);
   this.Name = "Form1";
   this.Text = "Form1";
   this.groupBox1.ResumeLayout(false);
   this.groupBox2.ResumeLayout(false);
   this.groupBox3.ResumeLayout(false);
   this.groupBox4.ResumeLayout(false);
   this.ResumeLayout(false);


  }
  #endregion


  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main()
  {
   Application.Run(new Form1());
  }


  private void button1_Click(object sender, System.EventArgs e)
  {
   //调用GetWindowsDirectory和GetSystemDirectory函数分别取得Windows路径和系统路径
   const int nChars = 128;
   StringBuilder Buff = new StringBuilder(nChars);
   GetWindowsDirectory(Buff,nChars);
   WindowsDirectory.Text = "Windows路径:"+Buff.ToString();
   GetSystemDirectory(Buff,nChars);
   SystemDirectory.Text = "系统路径:"+Buff.ToString();


   //调用GetSystemInfo函数获取CPU的相关信息
   CPU_INFO CpuInfo;
   CpuInfo = new CPU_INFO();
   GetSystemInfo(ref CpuInfo);
   NumberOfProcessors.Text = "本计算机中有"+CpuInfo.dwNumberOfProcessors.ToString()+"个CPU";
   ProcessorType.Text = "CPU的类型为"+CpuInfo.dwProcessorType.ToString();
   ProcessorLevel.Text = "CPU等级为"+CpuInfo.dwProcessorLevel.ToString();
   OemId.Text = "CPU的OEM ID为"+CpuInfo.dwOemId.ToString();
   PageSize.Text = "CPU中的页面大小为"+CpuInfo.dwPageSize.ToString();


   //调用GlobalMemoryStatus函数获取内存的相关信息
   MEMORY_INFO MemInfo;
   MemInfo = new MEMORY_INFO();
   GlobalMemoryStatus(ref MemInfo);
   MemoryLoad.Text = MemInfo.dwMemoryLoad.ToString()+"%的内存正在使用";
   TotalPhys.Text = "物理内存共有"+MemInfo.dwTotalPhys.ToString()+"字节";
   AvailPhys.Text = "可使用的物理内存有"+MemInfo.dwAvailPhys.ToString()+"字节";
   TotalPageFile.Text = "交换文件总大小为"+MemInfo.dwTotalPageFile.ToString()+"字节";
   AvailPageFile.Text = "尚可交换文件大小为"+MemInfo.dwAvailPageFile.ToString()+"字节";
   TotalVirtual.Text = "总虚拟内存有"+MemInfo.dwTotalVirtual.ToString()+"字节";
   AvailVirtual.Text = "未用虚拟内存有"+MemInfo.dwAvailVirtual.ToString()+"字节";


   //调用GetSystemTime函数获取系统时间信息
   SYSTEMTIME_INFO StInfo;
   StInfo = new SYSTEMTIME_INFO();
   GetSystemTime(ref StInfo);
   Date.Text = StInfo.wYear.ToString()+"年"+StInfo.wMonth.ToString()+"月"+StInfo.wDay.ToString()+"日";
   Time.Text = (StInfo.wHour+8).ToString()+"点"+StInfo.wMinute.ToString()+"分"+StInfo.wSecond.ToString()+"秒";
  }


  }
 }




Visual C#的剪切板編程

Visual C#是微軟.Net框架中的一個重要的程序開發語言,雖然在.Net框架中還有其他的程序開發語言,但微軟似乎對Visual C#更喜愛有加。這同時也就決定了Visual C#在.Net框架中的地位,以及他以後的發展前途。由於針對剪切板方面的編程始終是程序設計的一個重點,當然也可以算是一個難點。本文將探討如何利用Visual C#進行剪切板編程。本文將結合二個具體的程序例子來介紹Visual C#剪切板編程的二個重要方面:判定剪切板中的數據類型、保存剪切板中的數據。首先來介紹Visual C#如何判定剪切板中的數據類型的。{)b}}
   bpf
剪切板中可以存放許多類型的數據,而Visual C#可以判定剪切板中的數據類型大致有21種,這其中包括位圖(Bitmap)、文本(Text)、超文本(Html)等,由於類型比較將多,本文將只介紹比較典型的數據類型,對於其他的數據類型,操作方法基本類似。2oN
   }}2sT
一. 本文程序設計和運行的環境AlgsBE
   _F
(1).微軟公司視窗2000服務器版^QM_A
  (2)..Net FrameWork SDK Beta 2Kc+4
   f[fMEY
二. 介紹程序中使用到的類和接口^5"^U
   Qb
Visual C#判斷剪切板中的數據類型,要使用到.Net FrameWork SDK中二個類(Class)和一個接口(Interface),具體的就是:Clipboard類、Dataformats類和IdataObject接口。這三者的作用是:Clipboard類是一個不可以繼承的類,他的主要作用是通過自身的方法來實現在計算機系統的剪切板中存放和獲得數據。Dataformats類主要是提供了一個預先設定好的剪切板數據類型,並可以被IdataObject接口調用來判定剪切板數據類型。IdataObject接口提供了一個自由格式的裝置來傳輸數據。下面就結合這些類和接口來具體看一下,具體的使用方法。cc|@=
   eU@>M
三. Visual C#判定剪切板數據類型的解決思路以及重要步驟的解決辦法J!
   &M
(1).首先要讀取剪切板中的數據內容:0zO0g,
   wP;P
Clipboard類中有一個方法叫GetDataObject ( ),此方法的作用就是讀取當前剪切板中的數據內容。要把讀取了當前剪切板中數據存放起來,這就要用到IdataObject接口,這個接口可以使用在Clipboard類和Drag and Drop的操作中。下面這些代碼就是讀取把當前剪切板中的數據內容並存放起來。.
   fUac
IDataObject d = Clipboard.GetDataObject ( ) ;\(OQ
   )6u}
(2).判定當前剪切板中的數據類型:&@
   }
在IdataObject接口中有一個方法名稱是GetDataPresent ( ),這個方法的作用是檢測存放在接口的數據是否是指定的數據類型或者可以轉換成指定的數據類型。他的返回值是布爾型,True表示為指定的數據類型或者可以轉換成指定的數據類型。False於上面意思相反。下面語句實現的功能是判定當前剪切板的數據內容是否為位圖、文本和超文本的,具體如下:;:R2
   -
if ( d.GetDataPresent ( Dataformats.Bitmap ) ) {*
  Console.WriteLine ( "當前剪切板中的數據類型是位圖!" ) ;D*
  } else if ( d.GetDataPresent ( Dataformats.Text ) ) {o"=8P
  Console.WriteLine ( "當前剪切板中的數據類型是文本!" ) ;%3b
  } else if ( d.GetDataPresent ( Dataformats.Html ) ) {.en cA
  Console.WriteLine ( "當前剪切板中的數據類型是超文本!" ) ;\<
  } else {Pr
  Console.WriteLine ( "當前剪切板中的數據類型是其他類型數據!" ) ;+_N
  }   j
   L6E
四. 程序源代碼6Zt
   ro9B*
通過上面的介紹,我們不難理解下面這些程序代碼,這些程序代碼的功能就是判定當前剪切板中的數據是什麼類型。具體如下:~QNU
   u
using System ;Tl
  using System.Windows.forms ;ARn
  public class MyClipboard_`3O
  {   yR`8``
  public static void Main ( string [ ] args )!I
  {   $qcPj7
  IDataObject d = Clipboard.GetDataObject ( ) ;|ECJ
   yNhq
if ( d.GetDataPresent ( Dataformats.Bitmap ) ) {=?H
  Console.WriteLine ( "當前剪切板中的數據類型是位圖!" ) ;|Lg8&
  } else if ( d.GetDataPresent ( Dataformats.Text ) ) {ogC](<
  Console.WriteLine ( "當前剪切板中的數據類型是文本!" ) ; xzNR
  } else if ( d.GetDataPresent ( Dataformats.Html ) ) {YO{^
  Console.WriteLine ( "當前剪切板中的數據類型是超文本!" ) ;Mj
  } else {\y_
  Console.WriteLine ( "當前剪切板中的數據類型是其他類型數據!" ) ;Pmj>
  }   Scf5
  }   #;c.>"
  }   o^jQyB
五. Visual C#保存剪切板數據的解決思路以及重要步驟的解決辦法Ow%O
   |
用Visual C#保存剪切板中的數據要使用的類和接口和判定剪切板中的數據類型基本一樣。但由於要讀取剪切板數據,就需要用到了IdataObject接口的方法GetData ( ),這個方法的作用就是獲得指定類型的數據內容。他的語法為:;.Ba
   !
object GetData ( Type ) ;4yD
   xfwTf
我們注意到這個方法返回的類型是一個Object類型,這是一個參考類型,他的變量就是一個參考類型變量,而我們讀取的內容應該是一個實值類型變量,這就又涉及到裝箱和出箱的問題了。由於在前面的文章中已經比較詳細的說明了這二者的關係(見《用Visual C#動態生成組件》),所以在本文就不介紹了。在本文的程序中是通過下列二行語句來實現這種由參考類型變量轉換成實值類型變量的過程:)>/Q+
   3.rmmR
Bitmap b = ( Bitmap ) d.GetData ( Dataformats.Bitmap ) ;//位圖g
String c = ( String ) d.GetData ( Dataformats.Text ) ;//文本n9od
   2NR)D_
實現保存剪切板中數據的程序的設計思路是,首先判定當前剪切板中的數據類型,如果數據類型為位圖,則保存到C盤的"my.bmp"文件中,如果數據類型是文本,則用一個RichText組件顯示出來。否則則提示是其他類型數據。?V4
   3FC~
如何判定剪切板數據類型在上面已經介紹過了,程序的另外一個難點就是,假定已經判定剪切板中的數據類型為位圖,如何保存數據。在程序中是通過名稱空間System.Windows.forms中的Bitmap類的Save ( )方法來實現的。下列語句就是具體的實現方法。K
   <uDW
Bitmap b = ( Bitmap ) d.GetData ( Dataformats.Bitmap ) ;e
  b.Save ( @"c:\my.bmp" ) ;Qqz's
  MessageBox.Show ( "當前剪切板內容是位圖,已經保存到"MY.BMP"文件中!" ) ;X]
   +(
六. 用Visual C#讀取當前剪切板中數據內容並保存的程序源代碼rXT@>f
   >pua
通過上面的介紹,可以得到實現上述功能的源程序代碼,如下:!hmP
   \
using System ;>HDP<Q
  using System.Drawing ;=y5
  using System.Collections ;u@lzX
  using System.ComponentModel ;}wPq{C
  using System.Windows.forms ;]*}~:D
  using System.Data;c
  public class form1 : formR4O34
  {   TbpHa=
  private RichTextBox richTextBox1 ;";0
  private Button button1 ;IA
  private System.ComponentModel.Container components = null ;Ec-d
  public form1()\^49j
  {   r
  //初始化窗體中的各個組件d%Xnd
  InitializeComponent ( ) ;VEM
  }   0#&-g
  //清除程序中使用過的資源juF1~I
  protected override void Dispose ( bool disposing )c`4Ees
  {   @
  if ( disposing )T
  {   kZ.
  if ( components != null ) *O
  {   hD+:
  components.Dispose ( ) ;sh$'
  }   &N]^?
  }   G2
  base.Dispose ( disposing );'#
  }   [
  private void InitializeComponent ( )_6
  {   YC+
  this.richTextBox1 = new RichTextBox ( ) ;qI[
  this.button1 = new Button ( ) ;P
  this.SuspendLayout ( ) ;tH
   a.UZv
this.richTextBox1.Location = new System.Drawing.Point ( 40 , 16 ) ;*
  this.richTextBox1.Name = "richTextBox1" ;HR
  this.richTextBox1.Size = new System.Drawing.Size ( 336 , 264 ) ;.k`/%
  this.richTextBox1.TabIndex = 0 ;jbKg
  this.richTextBox1.Text = "" ;2:+
   <<
this.button1.Location = new System.Drawing.Point ( 128 , 304 ) ;T-JsKw
  this.button1.Name = "button1" ;#dnK&
  this.button1.Size = new System.Drawing.Size ( 128 , 24 ) ;_+R:]U
  this.button1.TabIndex = 1 ;d^D-
  this.button1.Text = "獲得剪切板中的數據" ;M
  this.button1.Click += new System.EventHandler ( this.button1_Click ) ;5BEhF
   S7p
this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ;=j>
  this.ClientSize = new System.Drawing.Size ( 408 , 357 ) ;SZU2D
  this.Controls.Add ( button1 );-Jr
  this.Controls.Add ( richTextBox1 );vzws?
  this.Name = "form1";vUd2J
  this.Text = "用Visual C#來保存剪切板中的數據!";YvQg
  this.ResumeLayout(false);0f
   &IZz[
}   y5
  static void Main ( ) Tq_b
  {   s&/4M
  Application.Run ( new form1 ( ) ) ;)SFIN
  }   ;ZP
  private void button1_Click ( object sender , System.EventArgs e )y
  { //定義一個IDataObject接口\*bnP
  IDataObject d = Clipboard.GetDataObject ( ) ;.3qw
  //如果剪切板中數據是位圖,則另存為C盤的my.bmp文件ZJo_hO
  if ( d.GetDataPresent ( Dataformats.Bitmap ) ) Nq
  {   X|/
  //出箱-
  Bitmap b = ( Bitmap ) d.GetData ( Dataformats.Bitmap ) ;`6
  b.Save ( @"c:\my.bmp" ) ;MM
  MessageBox.Show ( "當前剪切板內容是位圖,已經保存到"MY.BMP"文件中!" ) ;obb
  } //如果是文本,則用窗體中的RichText組件顯示文本內容。2
  else if ( d.GetDataPresent ( Dataformats.Text ) ) kzhk
  {   1<4F6@
  //出箱C,x
  String c = ( String ) d.GetData ( Dataformats.Text ) ; WC<C
  richTextBox1.Text = c ;lu`%iZ
  }   6L6l'w
  else 7:
  {   ^8)
  MessageBox.Show ( "剪切板中是其他類型的數據!" ) ;}=
  }   H
  }   h(Kx
  }   G&`
   N2EV
七. 總結bK
   in4Q9
本文介紹了在用Visual C#進行剪切板編程的二個重要方面的內容,即:判定剪切板中的數據和保存剪切板中的數據。其實針對剪切板的編程用途是比較廣的,譬如有了上面的知識做鋪墊,我想如果要你用Visual C#開發一個抓圖程序,你一定不會說很難吧! n9Z



About this Archive

This page is a archive of recent entries in the .Net category.

Java is the next category.

Find recent content on the main index or look in the archives to find all content.