﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-玄铁剑-文章分类-windows</title><link>http://www.cnitblog.com/MartinYao/category/4622.html</link><description>成功的途径：抄，创造，研究，发明...</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 13:53:02 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 13:53:02 GMT</pubDate><ttl>60</ttl><item><title>Linq VS Entity framework</title><link>http://www.cnitblog.com/MartinYao/articles/67203.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 06 Jul 2010 13:27:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67203.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67203.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67203.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67203.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67203.html</trackback:ping><description><![CDATA[<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mL-FLm34vQ3Kt0XXTC_6jWaq6D16Ae3nyIf7n2WwJdPSz4fpn6uzEiAVFt52t2hmKdTvuseMxkNrqQ7DPJkJPsEUZMdxUSk7C2mefNKPX8k7Ib1IWd7Rfp6CvlbrgpX3pR_cfT2D7lJM/image_thumb[30].png" width=553 height=349> </p>
<div align=center>
<table>
    第二次读写10条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mOaGxJJmIclM7iP0nrlOkoeKBnl5cxxlEYrYuk-tEGy2PLtpQuKNnF7wxv4CYSYyfHdCuVCw4qeBZkD4z8wk8uW1kW5yy3CCA_cNmieYaCtZkXVILuJCDOi_QA6ehVeemkw8iVV9PWHU/image_thumb[34].png" width=555 height=357> </p>
<div align=center>
<table>
    操作100条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mZeEY04gWG2ouk8GKlXjWaMS7ubj_KdZDg819afWokBsZamj_r65UhRRCnxK_L32SMrEQYclupVMyVZ5DxojPvYGssqsGMv5D0iORmD64HHHPqqWZ0s_-NSZN3KtANkaV5FEVySDh0z0/image_thumb38.png" width=558 height=347> </p>
<div align=center>
<table>
    操作1000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1mNU8Y5RO0cdDkVi9QwQI3zFQgWKR7Lvp1XKLN83zOlVDjpiEpEwvmVNPNcG5wbJtNM-NSvDmFX-fFRQ58X2xaGII-xYAsIs2b3sUG1Kr3WZPYIjYtncBfyhzlA43wxi-PW4vlYMxbOec/image_thumb42.png" width=558 height=356> </p>
<div align=center>
<table>
    操作10000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1m_n5bRSj-pCTH8tRlXN34d16sjMcH5giyrtt6eX7MToLX0ZcTk2vX1Y7egGLykZIPTxoDZDjbItZ1bv5ZCP6aYgHq9pYeER08tf9sDx9Vz3F9w9mh-Eth2SqGcG6mxeB3sTSTHlmzNOQ/image_thumb46.png" width=553 height=349> </p>
<div align=center>
<table>
    操作100000条数据
    <tbody>
    </tbody>
</table>
</div>
<p><img title="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" border=0 alt="ADO.net，Linq to SQL和Entity Framework性能实测分析 — Windows Live - longniezhang - 疯狂计算机技术" src="https://wr61eq.bay.livefilestore.com/y1md5mMqxFYiM4Qpru3CPnn1K47S69f5cBaPKNZBxcwR0r0hE7A5s-BjB5gtFWtaK6bUSYbdMvy3AEo2ooJniXgqVIGkgnBRpChqlDdH0xvnn5FzI5Qt-K_tifxTCC-xBvGY6eSGhzDTzI/image_thumb51.png" width=555 height=355>&nbsp;<br></p>
<p>关于ado.net entity framework 性能比较网上也有很多，这里我只是初步的介绍下ado.net entity framework使用不同的方法查询数据的不同性能</p>
<p>第一部分：重复查询单个实体</p>
<p>第一种：Linq To Entitiess</p>
<p>代码如下：</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　(from　c　in　context.Customers　where　c.CustomerID　==　"ALFKI"　select　c).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间为6.2秒左右</p>
<p>第二种：使用Entity SQL</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context.Customers.Where("it.CustomerID=@Id",　new　System.Data.Objects.ObjectParameter("Id",　"ALFKI")).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}<br>查询使用时间为6.2秒左右</p>
<p>第三种：使用EntityKey 来查询</p>
<p>　static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　for　(int　i　=　0;　i　&lt;　1000;　i++)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context.GetObjectByKey(new　System.Data.EntityKey("NorthwindEntities.Customers",　"CustomerID",　"ALFKI"))　as　Customers;<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2-time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间为1秒，没错是一秒</p>
<p>总结：</p>
<p>前两种方法查询所使用的时间都差不多为6.2秒，时间比较长，但是使用第三种方法查询数据仅仅使用了1秒，为什么会相差那么多，区别在于前两种方法每次查询都要从数据库中查找，1000次查询每次的查询时间都一样，但是第三种方法只有第一次是从数据库中查数据，后面的999次都是在context通过主键直接取数据，速度当然会快很多啦，EF4.0之前的实体当含有外键引用时是自动生成对象引用的，而EF4.0现在多了一个外键ID，这样我们可以很方便的通过外键来查询数据</p>
<p>第二部分：查询不同是实体<br>第一种：Linq To Entitiess</p>
<p>　static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.Customers.Where(c　=&gt;　c.CustomerID　==　key).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间0.68秒左右</p>
<p>第二种：使用Entity SQL</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.Customers.Where("it.CustomerID=@Id",　new　System.Data.Objects.ObjectParameter("Id",　key)).FirstOrDefault();<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询使用时间0.5秒左右</p>
<p>第三种：使用EntityKey 来查询</p>
<p>static　void　Main(string[]　args)<br>　　　　　　　　{<br>　　　　　　　　　　　　DateTime　time1;<br>　　　　　　　　　　　　DateTime　time2;<br>　　　　　　　　　　　　NorthwindEntities　context　=　new　NorthwindEntities();<br>　　　　　　　　　　　　//先将Customers所有的主键查询出来<br>　　　　　　　　　　　　List&lt;string&gt;　keys　=　context.Customers.Select(c　=&gt;　c.CustomerID).Distinct().ToList();<br>　　　　　　　　　　　　//和上面的context以作区别<br>　　　　　　　　　　　　NorthwindEntities　　context_　=　new　NorthwindEntities();<br>　　　　　　　　　　　　time1　=　DateTime.Now;<br>　　　　　　　　　　　　foreach　(var　key　in　keys)<br>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　　　var　data　=　context_.GetObjectByKey(new　System.Data.EntityKey("NorthwindEntities.Customers",　"CustomerID",　key))　as　Customers;<br>　　　　　　　　　　　　　　　　string　addr　=　data.Address;<br>　　　　　　　　　　　　}<br>　　　　　　　　　　　　time2　=　DateTime.Now;<br>　　　　　　　　　　　　Console.WriteLine((time2　-　time1).ToString());<br>　　　　　　　　}</p>
<p>查询时间为0.18秒左右</p>
<p>总结：</p>
<p>通过比较第三种方法仍然比前两种来的快，查询效率更高</p>
<p>使用EntityKey来查询数据比其他两种方法来的更快，而且不是快一点点，而是相差有好几倍，这里的比较并没有使用存储过程来比较查询，但是从这里我们可以看出，在平常的应用中如果知道实体的主键尽量用主键来查询</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67203.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-07-06 21:27 <a href="http://www.cnitblog.com/MartinYao/articles/67203.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>图片流比较</title><link>http://www.cnitblog.com/MartinYao/articles/67201.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 06 Jul 2010 12:23:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/67201.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/67201.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/67201.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/67201.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/67201.html</trackback:ping><description><![CDATA[<p>关于桌面截屏图片分割为16*8块大小一样的图片，然后把截屏的图和原来的图片进行比较，一样就不发送，不同则发送然后覆盖以前的图片。</p>
<p>/*http://home.cnblogs.com/yangleiWPF/*/<br>using System;<br>using System.Collections.Generic;<br>using System.Linq;<br>using System.Text;<br>using System.Windows;<br>using System.Windows.Controls;<br>using System.Windows.Data;<br>using System.Windows.Documents;<br>using System.Windows.Input;<br>using System.Windows.Media;<br>using System.Windows.Media.Imaging;<br>using System.Windows.Navigation;<br>using System.Windows.Shapes;<br>using System.IO;<br>using System.Drawing;<br>using System.Runtime.InteropServices;<br>using System.Threading;<br>using System.Drawing.Imaging;</p>
<p>namespace photoText<br>{<br>&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp; /// Window1.xaml 的交互逻辑<br>&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp; public partial class Window1 : Window<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private delegate void UpImageCall(ImageSource Isource);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private UpImageCall upimage;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap ALL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int x = 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int y = 8;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int ScreenX;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int ScreenY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int SpX;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static int SpY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //保存所以小张图片流&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List&lt;Bitmap&gt; LBitmap = new List&lt;Bitmap&gt;();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Window1()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitializeComponent();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; upimage = Isource =&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image1.Source = Isource;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获取桌面大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ScreenX = GetScreenPixel()[0];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ScreenY = GetScreenPixel()[1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获取小张图片的长宽<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SpX = ScreenX / x;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SpY = ScreenY / y;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化图片数组<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ALL = CaptureDesktopALL();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaptureDesktopT();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread aa = new Thread(new ThreadStart(() =&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (true)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每隔300毫秒截一张图<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.Sleep(300);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaptureDesktop();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aa.Start();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aa.IsBackground = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 获取屏幕分辨率<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int[] GetScreenPixel()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Windows.Forms.Screen scr = System.Windows.Forms.Screen.PrimaryScreen;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Drawing.Rectangle rc = scr.Bounds;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int[] sp = new int[2] { rc.Width, rc.Height };<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return sp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 截屏<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [DllImport("gdi32.dll")]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, System.Drawing.CopyPixelOperation dwRop);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void CaptureDesktop()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; x; j++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int k = 0; k &lt; y; k++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap screenBitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Graphics screenGraphic = Graphics.FromHwnd(IntPtr.Zero))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //根据屏幕大小建立位图，这就是最后截取到的*屏幕图像*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenBitmap = new Bitmap(SpX, SpY, screenGraphic);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图相关Graphics<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics bitmapGraphics = Graphics.FromImage(screenBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立屏幕上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcScreen = screenGraphic.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcBitmap = bitmapGraphics.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将屏幕捕获保存在位图中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BitBlt(hdcBitmap, 0, 0, SpX, SpY, hdcScreen, j * SpX, k * SpY, CopyPixelOperation.CaptureBlt | CopyPixelOperation.SourceCopy);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭位图句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.ReleaseHdc(hdcBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭屏幕句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.ReleaseHdc(hdcScreen);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放位图对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放屏幕对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //比较之前的图片和现在的图片是否相同，不同则替换<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ImageCompareString(LBitmap[j * 8 + k], screenBitmap))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //保存最新的图片<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LBitmap[j * 8 + k] = screenBitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics g = Graphics.FromImage(ALL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //覆盖原来的图片区域<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.DrawImageUnscaled(LBitmap[j * 8 + k], j * SpX, k * SpY);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream stream = new MemoryStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ALL.Save(stream, System.Drawing.Imaging.ImageFormat.Png);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // MStream(ALL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageSourceConverter imageSourceConverter = new ImageSourceConverter();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //显示整张图片<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image1.Dispatcher.Invoke(upimage, (ImageSource)imageSourceConverter.ConvertFrom(stream));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获得压缩文件流 减少像素<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private MemoryStream MStream(Bitmap myBitmap)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageCodecInfo myImageCodecInfo;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Drawing.Imaging.Encoder myEncoder;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EncoderParameter myEncoderParameter;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EncoderParameters myEncoderParameters;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream ms = new MemoryStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置Mime类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myImageCodecInfo = GetEncoderInfo("image/jpeg");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myEncoder = System.Drawing.Imaging.Encoder.Quality;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myEncoderParameters = new EncoderParameters(1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置图片像素100L为最大<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myEncoderParameter = new EncoderParameter(myEncoder, 50L);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myEncoderParameters.Param[0] = myEncoderParameter;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myBitmap.Save(ms, myImageCodecInfo, myEncoderParameters);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ms;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static ImageCodecInfo GetEncoderInfo(String mimeType)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int j;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageCodecInfo[] encoders;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; encoders = ImageCodecInfo.GetImageEncoders();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (j = 0; j &lt; encoders.Length; ++j)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (encoders[j].MimeType == mimeType)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return encoders[j];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 抓取有鼠标的桌面,并且能抓到透明窗体<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void CaptureDesktopT()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; x; j++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int k = 0; k &lt; y; k++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap screenBitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Graphics screenGraphic = Graphics.FromHwnd(IntPtr.Zero))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //根据屏幕大小建立位图，这就是最后截取到的*屏幕图像*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //screenBitmap = new Bitmap(1280, 800, screenGraphic);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenBitmap = new Bitmap(SpX, SpY, screenGraphic);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图相关Graphics<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics bitmapGraphics = Graphics.FromImage(screenBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立屏幕上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcScreen = screenGraphic.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcBitmap = bitmapGraphics.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将屏幕捕获保存在位图中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BitBlt(hdcBitmap, 0, 0, SpX, SpY, hdcScreen, j * SpX, k * SpY, CopyPixelOperation.CaptureBlt | CopyPixelOperation.SourceCopy);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭位图句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.ReleaseHdc(hdcBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭屏幕句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.ReleaseHdc(hdcScreen);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放位图对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放屏幕对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化16*8张图片<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LBitmap.Add(screenBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Graphics g = Graphics.FromImage(ALL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //g.DrawImageUnscaled(LBitmap[2], 200, 200);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //MemoryStream stream = new MemoryStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ALL.Save(stream, System.Drawing.Imaging.ImageFormat.Png);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ImageSourceConverter imageSourceConverter = new ImageSourceConverter();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //image1.Source = (ImageSource)imageSourceConverter.ConvertFrom(stream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Bitmap CaptureDesktopALL()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap screenBitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (Graphics screenGraphic = Graphics.FromHwnd(IntPtr.Zero))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //根据屏幕大小建立位图，这就是最后截取到的*屏幕图像*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //screenBitmap = new Bitmap(1280, 800, screenGraphic);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenBitmap = new Bitmap(GetScreenPixel()[0], GetScreenPixel()[1], screenGraphic);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图相关Graphics<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics bitmapGraphics = Graphics.FromImage(screenBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立屏幕上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcScreen = screenGraphic.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立位图上下文<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IntPtr hdcBitmap = bitmapGraphics.GetHdc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将屏幕捕获保存在位图中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BitBlt(hdcBitmap, 0, 0, GetScreenPixel()[0], GetScreenPixel()[1], hdcScreen, 0, 0, CopyPixelOperation.CaptureBlt | CopyPixelOperation.SourceCopy);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭位图句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.ReleaseHdc(hdcBitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭屏幕句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.ReleaseHdc(hdcScreen);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放位图对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmapGraphics.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //释放屏幕对像<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenGraphic.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream stream = new MemoryStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; screenBitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageSourceConverter imageSourceConverter = new ImageSourceConverter();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image1.Source = (ImageSource)imageSourceConverter.ConvertFrom(stream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return screenBitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //图片比较类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static bool ImageCompareString(Bitmap firstImage, Bitmap secondImage)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream ms = new MemoryStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String firstBitmap = Convert.ToBase64String(ms.ToArray());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms.Position = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String secondBitmap = Convert.ToBase64String(ms.ToArray());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (firstBitmap.Equals(secondBitmap))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p><br>原文来自：雨枫技术教程网 <a href="http://www.fengfly.com/">http://www.fengfly.com</a><br>原文网址：<a href="http://www.fengfly.com/plus/view-173177-1.html">http://www.fengfly.com/plus/view-173177-1.html</a></p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/67201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2010-07-06 20:23 <a href="http://www.cnitblog.com/MartinYao/articles/67201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在服务器端生成 Word 2007 文档</title><link>http://www.cnitblog.com/MartinYao/articles/59059.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Thu, 04 Jun 2009 13:01:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/59059.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/59059.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/59059.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/59059.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/59059.html</trackback:ping><description><![CDATA[<span class=CodeDownloadText><strong><font face="Segoe UI" color=#003399>代码下载位置: </font></strong></span><a href="http://download.microsoft.com/download/f/2/7/f279e71e-efb0-4155-873d-5554a0608523/BasicInstincts2006_11.exe"><font color=#0033cc>BasicInstincts2006_11.exe</font></a> <strong><font color=#003399><font face="Segoe UI"><span class=CodeDownloadText>(170 KB) </span><br></font></font></strong><span><a id=ctl00_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_mainContentContainer_ctl00|ctl00_mainContentContainer_ctl01',this);" href="http://msdn.microsoft.com/zh-cn/magazine/cc164538.aspx"><font color=#0033cc>Browse the Code Online</font></a> </span>
<div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271597>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271597_h onclick="applyClick('ctl00_mainContentContainer_cpe271597');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271597_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;目录 </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271597_c style="DISPLAY: block"><a href="http://msdn.microsoft.com/zh-cn/magazine/cc163526.aspx#S1"><font color=#800080>Word 2007 文档内部结构</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/magazine/cc163526.aspx#S2"><font color=#800080>生成您的第一个 DOCX 文件</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/magazine/cc163526.aspx#S3"><font color=#800080>在服务器上生成 DOCX 文件</font></a> <br></div>
</div>
<br></div>
<div class=ArticleNormalPara>直到现在，编写和部署服务器端的能够读取、修改和生成 Microsoft<span class=superscript><font size=1>&#174;</font></span> Office 应用程序所使用的文档的应用程序仍然是个挑战。Microsoft Word、Excel<span class=superscript><font size=1>&#174;</font></span> 和 PowerPoint<span class=superscript><font size=1>&#174;</font></span> 所用的较早的二进制格式是在 1997 年开始使用的，直到在 Office 2003 版中仍然将其作为默认文件格式。然而，这种二进制的文件格式已被证明因过于棘手而不便使用。大多数读写 Office 文档的生产应用程序都通过承载 Office 应用程序的对象模型来达到此目的。</div>
<div class=ArticleNormalPara>使用应用程序（如 Word 或 Excel）的对象模型的应用程序和组件，在桌面机上的运行状况远远好于在服务器端环境中。任何曾花费时间编写额外的基础代码来让 Office 桌面应用程序在服务器上可靠运行的人都会告诉您，那绝对不是理想的解决方案。这是因为，Word 和 Excel 之类的 Office 桌面应用程序的设计初衷从来就不是使其在服务器上运行，每当遇到需要人工干预的频繁的对话时，就需要用一个自定义实用程序来终止并重新启动它们。</div>
<div class=ArticleNormalPara>对于服务器端而言，无需采用承载 Office 应用程序的对象模型的方法即可读写 Office 文档的能力已变为极需要的趋势。Office 2000 和 Office 2003 都引入了一些模型化的功能，可使用 XML 来创建 Excel 工作簿和 Word 文档。这一先进特性使得用 XML 库来编写 Office 文档的某些部分成为可能，比如使用通过 System.Xml 命名空间提供的 Microsoft .NET Framework 中包含的 XML 库。</div>
<div class=ArticleNormalPara>在 2007 Microsoft Office 系统中，Microsoft 进一步贯彻了这一理念，将 Office Open XML 文件格式用于 Microsoft Office Word、Excel 和 PowerPoint 2007 的文档。Office Open XML 文件格式受到了 ASP.NET 和 SharePoint<span class=superscript><font size=1>&#174;</font></span> 开发人员的热烈欢迎，因为它们能够做到不需要系统在服务器上运行 Office 桌面应用程序，就能在服务器上读取、编写和生成 Word 文档、Excel 工作簿或 PowerPoint 演示文稿。</div>
<div class=ArticleNormalPara>Office Open XML 文件格式正逐渐成为欧洲计算机制造商协会 (ECMA) 发布的标准。您可以在 <a href="http://openxmldeveloper.com/"><font color=#0033cc>openxmldeveloper.com</font></a> 了解到不断发展的标准化过程的详细信息，下载 Office Open XML 文件格式规范的最新草案和查找其他重要的在线资源。本月专栏的目标在于介绍可使得从 ASP.NET 应用程序中创建简单的 Word 文档并将文档发回给用户及能够让用户使用 Word 2007 直接打开这些文档所需的编程技术。 </div>
<br>
<div class=ArticleTypeTitle id=S1>Word 2007 文档内部结构</div>
<div class=ArticleNormalPara>首先先观察基于 Office Open XML 文件格式的简单 Word 文档的结构。如您所见，Office Open XML 文件格式基于标准 ZIP 文件技术。每个顶层文件都保存为 ZIP 存档。这意味着您能像对其他任何 ZIP 文件那样，使用 Windows<span class=superscript><font size=1>&#174;</font></span> 资源管理器中内置的 ZIP 文件支持功能打开 Word 文档并查看其内容。</div>
<div class=ArticleNormalPara>请注意，Word 和 Excel 等 2007 Microsoft Office 系统应用程序已经引入了采用新格式的文件的扩展名。例如，.docx 扩展名用于以 Office Open XML 文件格式存储的 Word 文档，而更熟悉的 .doc 扩展名继续用于以二进制格式存储的 Word 文档。</div>
<div class=ArticleNormalPara>为理解我所讲的内容，请在 Word 2007 中创建一个新文档，然后添加&#8220;Hello Word&#8221;作为正文。使用默认的格式将该文档保存为新文档并命名为 Hello.docx ，然后关闭 Word。接着，在 Windows 资源管理器中找到 Hello.docx，将其更名为 Hello.zip。打开 Hello.zip 并查看 Word 在内部创建的文件夹和文件的结构（参见<strong>图 1</strong>）。</div>
<div class=ArticleImageSpacer><img onmouseover="this.style.cursor='pointer';" style="CURSOR: pointer" onclick="var large='http://i.msdn.microsoft.com/cc163526.fig01_L(zh-cn).gif'; var small='http://i.msdn.microsoft.com/cc163526.fig01(zh-cn).gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" alt="" src="http://i.msdn.microsoft.com/cc163526.fig01(zh-cn).gif">
<div class=ArticleImageCaptionText>图 1<strong>&nbsp;DOCX 文档是一个 ZIP 存档&nbsp;</strong>(单击该图像获得较大视图)</div>
</div>
<div class=ArticleNormalPara>顶层文件 (Hello.docx) 被称为一个包。由于包是以标准 ZIP 存档实现的，所以它能自动实现压缩，并能在 Windows 平台和其他类似平台上用现有的许多实用程序和 API 即时使用其内容。</div>
<div class=ArticleNormalPara>每个包内部有两种内部组件：部件和项。一般来讲，部件包含内容，项包含描述部件的元数据。项可以进一步再分为关系项和内容类型的项。一个部件就是一个内部组件，它包含包内始终存在的内容。大多数部件都是简单的文本文件，它们被序列化为具有相关 XML 架构的 XML。不过，如果需要，例如当 Word 文档包含图形图像或媒体文件时，也可以将部件序列化为二进制数据。</div>
<div class=ArticleNormalPara>部件使用统一资源标识符 (URI) 来命名，URI 包含该部件在包文件内的相对路径，以及部件文件名。例如，一个 Word 文档包内的主要部件是 /word/document.xml。以下是一些在简单 Word 文档包内能找到的几个比较典型的部件名称：
<ul>
    <li>/[Content_Types].xml
    <li>/_rels/.rels
    <li>/docProps/app.xml
    <li>/docProps/core.xml
    <li>/word/_rels/document.xml.rels
    <li>/word/document.xml
    <li>/word/fontTable.xml
    <li>/word/settings.xml
    <li>/word/styles.xml
    <li>/word/theme/theme1.xml </li>
</ul>
</div>
<div class=ArticleNormalPara>Office Open XML 文件格式使用关系来定义源和目标部件之间的关联。一个顶层包和一个部件之间的关联构成了一个包关系。一个父部件和一个子部件之间的关联构成了一个部件关系。由于关系能够充分体现这些关联而无需检查特定部件中的内容，因而关系很重要。这意味着关系与特定内容的架构无关，因此能够更快地进行解析。此外还有一个优点，即在两个部件之间建立关系时，不需要修改其中任何一个部件。</div>
<div class=ArticleNormalPara>关系是在称为关系项的内部组件中被定义的。虽然关系项并不被真正视为一个部件，但仍然与部件一样存储在包内。为保持一致，关系项始终创建在名为 _rels 的文件夹中。</div>
<div class=ArticleNormalPara>例如，一个包中仅包含一个名为 /_rels/.rels 的包关系项。包关系项包含用于定义包的关系的 XML 元素，如，定义一个 .docx 文件的顶层包和内部部件 /word/document.xml 之间的关系，如下所示：
<div class="" id=ctl00_mainContentContainer_ctl05_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl05');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl05 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;Relationships xmlns="../package/2006/relationships "&gt;
&lt;Relationship Id="rId1"
Type="../officeDocument/2006/relationships/officeDocument "
Target="word/document.xml"/&gt;
&lt;/Relationships&gt;
</pre>
</div>
</div>
如您所见，Relationship（关系）元素用于定义名称、类型和目标部件。您还会注意到，关系的类型名称是用创建 XML 命名空间所采用的规范来定义的。</div>
<div class=ArticleNormalPara>除单个的包关系项外，包中还可以包含一个或多个部件关系项。例如，您可以在位于 URI /word/_rels/document.xml.rels 的包关系项中为 /word/document.xml 和子部件定义关系。注意，部件关系项中的关系的 Target 属性是一个相对于父部件而不是相对于顶层包的 URI。</div>
<div class=ArticleNormalPara>包内的每个部件都按照特定的内容类型进行定义。内容类型是定义部件的媒体名称、子类型和一组可选参数的元数据。包内使用的任何内容类型都必须在称为内容类型项的组件内显式定义。每个包内都仅有一个名为 /[Content_Types].xml 的内容类型项。在 /[Content_Types].xml 内定义的内容类型定义的示例如<strong>图 2</strong> 所示。您会发现，在该图中，关系项都是相似的部件，这是因为它们也是用一个关联的内容类型定义的。</div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271598>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271598_h onclick="applyClick('ctl00_mainContentContainer_cpe271598');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271598_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;Figure&nbsp;2&nbsp;Content Types Define Package </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271598_c style="DISPLAY: block">
<div class="" id=ctl00_mainContentContainer_ctl09_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl09');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl09 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;Types xmlns=
"http://schemas.openxmlformats.org/package/2006/content-types"&gt;
&lt;Default Extension="rels" ContentType=
"application/vnd.openxmlformats-package.relationships+xml"/&gt;
&lt;Default Extension="xml" ContentType="application/xml"/&gt;
&lt;Override PartName="/word/document.xml"
ContentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.document.main+xml"/&gt;
&lt;/Types&gt;
</pre>
</div>
</div>
</div>
</div>
<div class=ArticleNormalPara>内容类型供包的使用者用来解释如何读取和转换包的部件内的内容。正如您在<strong>图 2</strong> 中所看到的，默认设定的内容类型通常关联着一个文件扩展名，如 .rels 或 .xml。当按照与文件扩展名关联的默认内容类型不同的内容类型定义特定部件时，需要重写内容类型。例如，<strong>图 2</strong> 显示了 /word/document.xml 如何与扩展名为 .xml 的文件所使用的默认内容类型之外的内容类型相关联。</div>
<br>
<div class=ArticleTypeTitle id=S2>生成您的第一个 DOCX 文件</div>
<div class=ArticleNormalPara>由于打包的 API 与 Office Open XML 文件格式兼容，所以，尽管您可以使用现有的一些库来读写 ZIP 文件，但您应尽量使用新的打包的 API，它属于 .NET Framework 3.0 附带的 WindowsBase.dll 程序集的一部分。例如，有便利的方法可轻松地将关系元素添加到关系项，也可将内容类型元素添加到内容类型项。由于无需直接操作这些元素和项，所以使用打包的 API 能够简化操作。</div>
<div class=ArticleNormalPara>这个月的代码使用了随 WinFX<span class=superscript><font size=1>&#174;</font></span> Runtime Components 二月社区技术预览 (CTP) 一起安装的 WindowsBase.dll 版本。请注意，在六月发布的 CTP 中，WinFX Runtime Components 已更名为 .NET Framework 3.0。您可以使用这两个版本中的任何一个，也可以使用更新的 .NET Framework 3.0 版本。为了测试我们将在本专栏中所写的代码，您还需要安装 Word 2007 Beta 2 或更新版本。（变更为 Beta 2 之后的 Office 版本可能要求对这里所写的代码进行一些修改。）</div>
<div class=ArticleNormalPara>.NET Framework 3.0 包含 WindowsBase.dll，当您安装了 .NET Framework 3.0 后，只要添加一个引用（如<strong>图 3</strong> 所示），您就可以开始在 Visual Studio<span class=superscript><font size=1>&#174;</font></span> 2005 项目中对打包的 API 编程。</div>
<div class=ArticleImageSpacer><img onmouseover="this.style.cursor='pointer';" style="CURSOR: pointer" onclick="var large='http://i.msdn.microsoft.com/cc163526.fig03_L(zh-cn).gif'; var small='http://i.msdn.microsoft.com/cc163526.fig03(zh-cn).gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" alt="" src="http://i.msdn.microsoft.com/cc163526.fig03(zh-cn).gif">
<div class=ArticleImageCaptionText>图 3<strong>&nbsp;添加引用至 WindowsBase.dll&nbsp;</strong>(单击该图像获得较大视图)</div>
</div>
<div class=ArticleNormalPara>组成打包的 API 的类包含在命名空间 System.IO.Package 中。当使用包时，您还可以经常利用 System.IO 命名空间和 System.Xml 命名空间中较早和熟悉（希望如此）的类编写程序。观察<strong>图 4</strong> 中的代码，这些代码显示了创建新包的框架。</div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271599>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271599_h onclick="applyClick('ctl00_mainContentContainer_cpe271599');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271599_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;Figure&nbsp;4&nbsp;Creating a New Package </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271599_c style="DISPLAY: block">
<div class="" id=ctl00_mainContentContainer_ctl14_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl14');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl14 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">Imports System
Imports System.IO
Imports System.IO.Packaging
Imports System.Xml
Class GenerateDOCX
Shared Sub Main()
&#8216;*** Create a new package
Dim pack As Package = Package.Open("c:\Data\Hello.docx", _
FileMode.Create, _
FileAccess.ReadWrite)
&#8216;*** Write code here to create parts and add content
&#8216;*** Close package
pack.Close()
End Sub
End Class
</pre>
</div>
</div>
</div>
</div>
<div class=ArticleNormalPara>System.IO.Packaging 命名空间包含 Package 类，它提供了一个名为 Open 的共享方法，可用于创建新包和打开现有包。与其他需要处理文件 I/O 的类一样，调用 Open 后始终都要调用 Close。</div>
<div class=ArticleNormalPara>创建了新包后，接下来就要创建一个或多个部件并将内容序列化到其中。在本月专栏的这个示例中，我们将遵循&#8220;Hello World&#8221;应用程序的指导原则，按要求创建一个名为 \word\document.xml 的部件。创建部件的方法为，调用打开的 Package 对象的 CreatePart 方法，然后传递一个 URI 参数和一个基于字符串的内容类型：
<div class="" id=ctl00_mainContentContainer_ctl15_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl15');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl15 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">'*** Create main document part (document.xml) ...
Dim uri As Uri = New Uri("/word/document.xml", UriKind.Relative)
Dim partContentType As String = "application/vnd.openxmlformats" &amp; _
"-officedocument.wordprocessingml.document.main+xml"
Dim part As PackagePart = pack.CreatePart(uri, partContentType)
'*** Get stream for document.xml
Dim streamPart As New StreamWriter(part.GetStream(FileMode.Create, _
FileAccess.Write))
</pre>
</div>
</div>
</div>
<div class=ArticleNormalPara>调用 CreatePart 将传递一个基于 path /word/document.xml 的 URI 和内容类型，该类型是 Office Open XML 文件格式所需的，用作文字处理文档中包含主要内容的那部分。创建了部件后，我们必须使用标准的基于流的程序设计技术来将内容序列化到其中。您刚才看到的代码会在部件上打开一个流，方法是调用 GetStream 方法，然后使用得到的 Stream 对象来初始化 StreamWriter 对象。</div>
<div class=ArticleNormalPara>StreamWriter 对象将被用来将&#8220;Hello World&#8221;XML 文档序列化到 document.xml 中。请看下面的 XML；它表示可序列化到 document.xml 中的最简单的 XML 文档：
<div class="" id=ctl00_mainContentContainer_ctl16_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl16');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl16 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;w:document xmlns:w=
"http://schemas.openxmlformats.org/wordprocessingml/2006/3/main"&gt;
&lt;w:body&gt;
&lt;w:p&gt;
&lt;w:r&gt;
&lt;w:t&gt;Hello Word 2007&lt;/w:t&gt;
&lt;/w:r&gt;
&lt;/w:p&gt;
&lt;/w:body&gt;
&lt;/w:document&gt;
</pre>
</div>
</div>
</div>
<div class=ArticleNormalPara>请注意，这个 XML 文档中的所有元素都是在 Office Open XML 文件格式所要求的 schemas.openxmlformats.org/wordprocessingml/2006/3/main 命名空间中定义的。这个 XML 文档包含一个高级文档元素。在这个文档元素中，有一个包含主要内容的主体元素。</div>
<div class=ArticleNormalPara>在这个主体元素内，每个段落会有一个 &lt;p&gt; 元素。&lt;p&gt; 元素内有一个 &lt;r&gt;，它定义了一个运行区。运行区就是具有同样的一组特征的元素的区域。在运行区内有一个 &lt;t&gt; 元素，用于定义一段文本，在本例中为&#8220;Hello Word 2007&#8221;。</div>
<div class=ArticleNormalPara>现在，我们要通过一段代码，使用 System.Xml 命名空间中的 XmlDocument 类来生成这个 XML 文档。如果您注意观察一下<strong>图 5</strong>，您就会发现这些代码如何在正确的结构内使用相应的命名空间来创建这些元素。您还会注意到，这些代码如何使用 StreamWriter 对象将 XML 文档写入流，然后关闭流，再将这些序列化内容注入到 Package 对象中。</div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271600>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271600_h onclick="applyClick('ctl00_mainContentContainer_cpe271600');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271600_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;Figure&nbsp;5&nbsp;Generating the XML Document </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271600_c style="DISPLAY: block">
<div class="" id=ctl00_mainContentContainer_ctl20_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl20');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl20 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">&#8216;*** Define string variable for Open XML namespace for nsWP:
Dim nsWP As String = &amp;quot;http://schemas.openxmlformats.org&amp;quot; &amp;amp; _
&amp;quot;/wordprocessingml/2006/3/main&amp;quot;
&#8216;***  Create the start part, set up the nested structure ...
Dim xmlPart As XmlDocument = New XmlDocument()
Dim tagDocument As XmlElement
tagDocument = xmlPart.CreateElement(&amp;quot;w:document&amp;quot;, nsWP)
xmlPart.AppendChild(tagDocument)
Dim tagBody As XmlElement
tagBody = xmlPart.CreateElement(&amp;quot;w:body&amp;quot;, nsWP)
tagDocument.AppendChild(tagBody)
Dim tagParagraph As XmlElement
tagParagraph = xmlPart.CreateElement(&amp;quot;w:p&amp;quot;, nsWP)
tagBody.AppendChild(tagParagraph)
Dim tagRun As XmlElement
tagRun = xmlPart.CreateElement(&amp;quot;w:r&amp;quot;, nsWP)
tagParagraph.AppendChild(tagRun)
Dim tagText As XmlElement
tagText = xmlPart.CreateElement(&amp;quot;w:t&amp;quot;, nsWP)
tagRun.AppendChild(tagText)
&#8216;*** Insert text into part as a Text node
Dim nodeText As XmlNode
nodeText = xmlPart.CreateNode(XmlNodeType.Text, &amp;quot;w:t&amp;quot;, nsWP)
nodeText.Value = &amp;quot;Hello Word 2007&amp;quot;
tagText.AppendChild(nodeText)
&#8216;*** Write XML to part and close stream
xmlPart.Save(streamPart)
&#8216;*** Close stream and flush XML content into package
streamPart.Close()
pack.Flush()
</pre>
</div>
</div>
</div>
</div>
<div class=ArticleNormalPara>现在我们就要完成将 XML 内容写入 document.xml 的操作了。最后一步是通过调用 Package 对象的 CreateRelationship 方法在包和 document.xml 之间建立关系。只要您知道该关系类型的确切的字符串值，就可以很容易地完成该操作。您可以为要创建的关系指定唯一的名称（如 rId1）：
<div class="" id=ctl00_mainContentContainer_ctl21_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl21');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl21 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">'*** Create the relationship part
Dim relationshipType As String = _
"http://schemas.openxmlformats.org" &amp; _
"/officeDocument/2006/relationships/officeDocument"
pack.CreateRelationship(uri, TargetMode.Internal, _
relationshipType, "rId1")
pack.Flush()
'*** Close package
pack.Close()
</pre>
</div>
</div>
</div>
<div class=ArticleNormalPara>调用 CreateRelationship 后，您应按规定接着调用 Flush。这样将强制打包的 API 用正确的 Relationship 元素更新包关系项。最后调用 Package 对象的 Close 方法完成包的序列化并释放 Hello.docx 的文件句柄。</div>
<div class=ArticleNormalPara>至此，您已经了解了利用 Visual Basic<span class=superscript><font size=1>&#174;</font></span> 2005 编写的控制台应用程序来生成一个简单 .docx 文件的所有步骤，所有这些内容都可从<a href="http://msdn.microsoft.com/msdnmag/code06.aspx"><font color=#0033cc>此处</font></a>获得。现在让我们编写代码，以便通过 ASP.NET 应用程序在服务器端生成 .docx 文件。</div>
<br>
<div class=ArticleTypeTitle id=S3>在服务器上生成 DOCX 文件</div>
<div class=ArticleNormalPara>要修改控制台应用程序的代码以使其在 Web 服务器上运行，首先要考虑的是，您可能希望避免必须将包保存为宿主计算机文件系统中的物理文件。要知道，仅仅在 ASP.NET 工作进程的内存流中创建包的速度会更快。然后，您可以使用 ASP.NET Response 对象里的 OutputStream 对象将其写回到客户端。</div>
<div class=ArticleNormalPara>首先我们将代码更改为使用 MemoryStream 对象而不是使用实质的文件。观察<strong>图 6</strong> 中的代码，它们已经被添加到 ASP.NET 2.0 页面中的服务器端事件处理程序中。请注意，Package.Open 的第一个参数已由基于字符串的文件路径更改为 MemoryStream 对象。这种方法允许您再次使用同样的代码，像我们前面的操作那样创建包及其部件。不过，您无需担心如何创建和命名 OS 层文件。在高流量环境下，这种方法还具有更快的响应时间和更出色的吞吐量。</div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271601>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271601_h onclick="applyClick('ctl00_mainContentContainer_cpe271601');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271601_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;Figure&nbsp;6&nbsp;Using a MemoryStream Object </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271601_c style="DISPLAY: block">
<div class="" id=ctl00_mainContentContainer_ctl25_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl25');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl25 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">&#8216;*** Create in-memory stream as buffer
Dim bufferStream As New MemoryStream()
&#8216;*** Create new package in memory stream
Dim pack As Package = Package.Open( _
bufferStream, FileMode.Create, FileAccess.ReadWrite)
&#8216;*** This calls same code shown in Hello World example
WriteContentToPackage(pack)
&#8216;*** Save/close package object leaving DOCX file in MemoryStream
pack.Close()
&#8216;*** (1) SET UP HTTP HEADERS FOR RESPONSE
&#8216;*** (2) WRITE PACKAGE CONTENT INTO RESPONSE BODY
</pre>
</div>
</div>
</div>
</div>
<div class=ArticleNormalPara>您刚才看到的代码将创建一个 MemoryStream 对象，然后将一个 .docx 文件序列化到其中，操作与将 .docx 文件序列化到物理文件中一样。自定义的 WriteContentToPackage 方法中的代码直接从上文中的控制台应用程序代码中获得。但是，它此时要创建一个包并将其序列化到内存中的一个缓冲区而不是一个物理文件中。</div>
<div class=ArticleNormalPara>当您将包写入 MemoryStream 对象并对包对象调用 Close 后，即已完成对打包的 API 的操作。剩下要做的事情就是设置正确的 HTTP 标头，然后将包内容写入将发回到客户端的响应的正文中。</div>
<div class=ArticleNormalPara>首先我们来设置 HTTP 标头。您应对 ASP.NET 响应对象调用各种方法来清除现有的任何标头，然后添加一个内容放置标头来指定该响应包含一个文件名为 Hello.docx 的附件：
<div class="" id=ctl00_mainContentContainer_ctl26_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl26');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl26 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">Response.ClearHeaders()
Response.AddHeader("content-disposition", _
"attachment; filename=Hello.docx")
</pre>
</div>
</div>
</div>
<div class=ArticleNormalPara>接下来，您必须设置 HTTP 响应的编码和 MIME 内容类型，然后将包的二进制内容写入 HTTP 响应的正文中。使用<strong>图 7</strong> 中的代码即可实现此目的。请注意，最后必须调用 Response.Flush 和 Response.Close 才能确保整个包被完整写入 HTTP 响应。</div>
<div class=MTPS_CollapsibleRegion id=ctl00_mainContentContainer_cpe271602>
<div class=CollapseRegionLink id=ctl00_mainContentContainer_cpe271602_h onclick="applyClick('ctl00_mainContentContainer_cpe271602');"><img class=LibC_o id=ctl00_mainContentContainer_cpe271602_i style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; VERTICAL-ALIGN: middle; BORDER-RIGHT-WIDTH: 0px" src="http://i.msdn.microsoft.com/Global/Images/clear.gif">&nbsp;Figure&nbsp;7&nbsp;Response.ClearContent </div>
<div class=MTPS_CollapsibleSection id=ctl00_mainContentContainer_cpe271602_c style="DISPLAY: block">
<div class="" id=ctl00_mainContentContainer_ctl30_>
<div class=CodeSnippetTitleBar>
<div class=CodeDisplayLanguage></div>
<div class=CopyCodeButton><a class=copyCode title=复制代码 href="javascript:CopyCode('ctl00_mainContentContainer_ctl30');">
<div class=LibC_copy_off align=middle border="0" height="9px" width="auto"></div>
复制代码</a></div>
</div>
<div dir=ltr>
<pre class=libCScode id=ctl00_mainContentContainer_ctl30 style="WORD-BREAK: break-all; WORD-WRAP: break-word" space="preserve">Response.ClearContent()
Response.ContentEncoding = System.Text.Encoding.UTF8
Response.ContentType = &amp;quot;application/vnd.ms-word.document.12&amp;quot;
&#8216;*** Write package to response stream
bufferStream.Position = 0
Dim writer As New BinaryWriter(Response.OutputStream)
Dim reader As New Bina NO RESPONSE YET | ryReader(bufferStream)
writer.Write(reader.ReadBytes(bufferStream.Length))
reader.Close()
writer.Close()
bufferStream.Close()
&#8216;*** flush and close the response object
Response.Flush()
Response.Close()
</pre>
</div>
</div>
</div>
</div>
<div class=ArticleNormalPara>只要客户机经过配置，能够理解 .docx 文件关联的 MIME 内容类型，系统就会为用户提供以下常规选项：在 Word 中打开文档或将其保存到本地硬盘。</div>
<div class=ArticleNormalPara>如果用户单击&#8220;打开&#8221;按钮，则该 .docx 文件将自动在 Word 中打开，如<strong>图 8</strong> 所示。请注意，直到此时，一直都未将这个包保存为文件系统的物理文件，这一点很有趣。而只是将它存储在了 Web 服务器和客户端桌面机的内存中。如果用户没有保存就关闭文档，文档即会消失，仿佛从来就没有存在过。鉴于这个特点，该方法成为了为信函、备忘录、客户列表以及任何您能够想到的文档类型创建模板的理想之选。</div>
<div class=ArticleImageSpacer><img onmouseover="this.style.cursor='pointer';" style="CURSOR: pointer" onclick="var large='http://i.msdn.microsoft.com/cc163526.fig08_L(zh-cn).gif'; var small='http://i.msdn.microsoft.com/cc163526.fig08(zh-cn).gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" alt="" src="http://i.msdn.microsoft.com/cc163526.fig08(zh-cn).gif">
<div class=ArticleImageCaptionText>图 8<strong>&nbsp;文档于 Word 2007 里开启&nbsp;</strong>(单击该图像获得较大视图)</div>
</div>
<a href="http://www.cnblogs.com/Dragonpro/archive/2005/09/01/228144.html">http://www.cnblogs.com/Dragonpro/archive/2005/09/01/228144.html</a>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/59059.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2009-06-04 21:01 <a href="http://www.cnitblog.com/MartinYao/articles/59059.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>以编程方式更改屏幕方向 </title><link>http://www.cnitblog.com/MartinYao/articles/52414.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 13:38:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52414.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52414.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52414.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52414.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52414.html</trackback:ping><description><![CDATA[<p><strong>摘要：</strong> 本文描述如何以编程方式更改显示设备的方向、分辨率及其他方面。相应示例采用 C# 编写。</p>
<p>单击<a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" href="http://download.microsoft.com/download/B/F/3/BF321EC9-F3A7-473F-BA1B-7F9EA8949D27/DisplaySettingsCodeSample.EXE"><font color=#0033cc>此处</font></a>可下载本文的代码示例。</p>
<h5>本页内容</h5>
<p><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#ENAA"><img alt=简介 src="http://i.msdn.microsoft.com/ms812499.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#ENAA"><font color=#0033cc>简介</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EKAA"><img alt=概述 src="http://i.msdn.microsoft.com/ms812499.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EKAA"><font color=#0033cc>概述</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EHAA"><img alt=使用示例 src="http://i.msdn.microsoft.com/ms812499.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EHAA"><font color=#0033cc>使用示例</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EEAA"><img alt=在托管代码中获取并更改显示设置 src="http://i.msdn.microsoft.com/ms812499.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EEAA"><font color=#0033cc>在托管代码中获取并更改显示设置</font></a> <br><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EBAA"><img alt=小结 src="http://i.msdn.microsoft.com/ms812499.arrow_px_down(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#EBAA"><font color=#0033cc>小结</font></a> <br></p>
<h3 id=ENAA>简介</h3>
<p>有些情况下，应用程序需要更改屏幕方向，因为有的功能被设计为在特定模式下运行得最好。其中一个例子就是 Microsoft Office PowerPoint 中的&#8220;幻灯片放映&#8221;：PowerPoint 以横向模式运行。即使正在纵向模式下使用 Tablet PC，当开始幻灯片放映时，应用程序也会切换到横向方向。当用户结束幻灯片放映时，PowerPoint 会切换回原来的设置。</p>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms812499.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EKAA>概述</h3>
<p>更改显示设置可以通过使用两个 Win32 API 来完成，这两个 API 都具有指向 <a id=ctl00_rs1_mainContentContainer_ctl02 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" href="http://msdn.microsoft.com/library/en-us/gdi/prntspol_8nle.asp"><font color=#0033cc>DEVMODE</font></a> 结构的指针，它们分别包含与显示设置有关的所有信息： </p>
<ul>
    <li>
    <p>使用 <a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" href="http://msdn.microsoft.com/library/en-us/gdi/devcons_84oj.asp"><font color=#0033cc>EnumDisplaySettings</font></a> 读取当前显示设置，并枚举所有受支持的显示设置。 </p>
    <li>
    <p>使用 <a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" href="http://msdn.microsoft.com/library/en-us/gdi/devcons_7gz7.asp"><font color=#0033cc>ChangeDisplaySettings</font></a> 切换到新的显示设置。 </p>
    </li>
</ul>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms812499.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EHAA>使用示例</h3>
<p>要编译示例源代码，计算机中必须安装 Microsoft Visual Studio .NET 2003。该示例应用程序有一个用户界面，它具有以下功能： </p>
<ul>
    <li>
    <p>查看当前显示设备所支持的所有显示设置。 </p>
    <li>
    <p>查看当前显示设置的参数。 </p>
    <li>
    <p>切换至任何受支持的显示设置。 </p>
    <li>
    <p>顺时针和逆时针旋转屏幕方向。 </p>
    </li>
</ul>
<p><img alt="" src="http://i.msdn.microsoft.com/ms812499.tbconchgscrn_image01(zh-cn,MSDN.10).gif"> </p>
<p><strong>获取当前显示设置</strong> </p>
<p>要获取当前显示设置，请将 <em>iModeNum</em> 参数中的 ENUM_CURRENT_SETTINGS 常量传递给 <a id=ctl00_rs1_mainContentContainer_ctl05 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" href="http://msdn.microsoft.com/library/en-us/gdi/devcons_84oj.asp"><font color=#0033cc>EnumDisplaySettings</font></a> API，如以下 C++ 代码所示。</p>
<pre>   DEVMODE dm;
// initialize the DEVMODE structure
ZeroMemory(&amp;dm, sizeof(dm));
dm.dmSize = sizeof(dm);
if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &amp;dm))
{
// inspect the DEVMODE structure to obtain details
// about the display settings such as
//  - Orientation
//  - Width and Height
//  - Frequency
//  - etc.
}
</pre>
<p><strong>枚举所有受支持的显示设置</strong> </p>
<p>要枚举当前显示设备支持的所有显示设置，请将 <em>iModeNum</em> 参数中的 0 传递给 <strong>EnumDisplaySettings</strong> API，然后继续以递增的 <em>iModeNum</em> 值调用它，直到该函数返回零，如以下 C++ 代码所示。</p>
<pre>   int index = 0;
DEVMODE dm;
// initialize the DEVMODE structure
ZeroMemory(&amp;dm, sizeof(dm));
dm.dmSize = sizeof(dm);
while (0 != EnumDisplaySettings(NULL, index++, &amp;dm))
{
// inspect the DEVMODE structure to obtain details
// about the display settings such as
//  - Orientation
//  - Width and Height
//  - Frequency
//  - etc.
}
</pre>
<p><strong>更改显示设置</strong> </p>
<p>要更改显示设置，请将指向有效 <strong>DEVMODE</strong> 结构的指针传递给 <strong>ChangeDisplaySettings</strong> API。以下 C++ 代码演示如何使屏幕方向顺时针旋转 90 度。请注意，这段代码只对支持相应显示设置的设备起作用。遵守 <strong>ChangeDisplaySettings</strong> API 的返回值十分重要，因为有些操作为了在图形模式下工作，需要计算机重启。</p>
<pre>   DEVMODE dm;
// initialize the DEVMODE structure
ZeroMemory(&amp;dm, sizeof(dm));
dm.dmSize = sizeof(dm);
if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &amp;dm))
{
// swap height and width
DWORD dwTemp = dm.dmPelsHeight;
dm.dmPelsHeight= dm.dmPelsWidth;
dm.dmPelsWidth = dwTemp;
// determine new orientaion
switch (dm.dmDisplayOrientation)
{
case DMDO_DEFAULT:
dm.dmDisplayOrientation = DMDO_270;
break;
case DMDO_270:
dm.dmDisplayOrientation = DMDO_180;
break;
case DMDO_180:
dm.dmDisplayOrientation = DMDO_90;
break;
case DMDO_90:
dm.dmDisplayOrientation = DMDO_DEFAULT;
break;
default:
// unknown orientation value
// add exception handling here
break;
}
long lRet = ChangeDisplaySettings(&amp;dm, 0);
if (DISP_CHANGE_SUCCESSFUL != lRet)
{
// add exception handling here
}
}
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms812499.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EEAA>在托管代码中获取并更改显示设置</h3>
<p><strong>映射</strong> <strong>API</strong> </p>
<p>为了在托管代码中更改显示设置，必须使用<a id=ctl00_rs1_mainContentContainer_ctl06 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);" href="http://msdn.microsoft.com/library/en-us/vcmxspec/html/vcmg_PlatformInvocationServices.asp"><font color=#0033cc>平台调用服务</font></a> (PInvoke) 调用 <strong>EnumDisplaySettings</strong> 和 <strong>ChangeDisplaySettings</strong> API。对此，一个好的做法是创建一个名为 <strong>NativeMethods</strong> 的类，该类可以将封装这些 API 的公共静态方法公开。该类应该包含 API 所对应的所有必需的常量定义。以下代码示例演示了这种做法。该类的完整实现可在 NativeMethods.cs 文件中找到，该文件是示例应用程序的一部分。</p>
<pre>using System.Runtime.InteropServices;
...
public class NativeMethods
{
// PInvoke declaration for EnumDisplaySettings Win32 API
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
public static extern int EnumDisplaySettings(
string lpszDeviceName,
int iModeNum,
ref DEVMODE lpDevMode);
// PInvoke declaration for ChangeDisplaySettings Win32 API
[DllImport("user32.dll, CharSet=CharSet.Ansi")]
public static extern int ChangeDisplaySettings(
ref DEVMODE lpDevMode,
int dwFlags);
// add more functions as needed ??
// constants
public const int ENUM_CURRENT_SETTINGS = -1;
public const int DMDO_DEFAULT = 0;
public const int DMDO_90 = 1;
public const int DMDO_180 = 2;
public const int DMDO_270 = 3;
// add more constants as needed ??
}
</pre>
<p><strong>映射</strong> <strong>DEVMODE </strong><strong>结构</strong> </p>
<p>在将 <strong>DEVMODE</strong> 结构映射到托管结构时，应该注意以下几个问题： </p>
<ul>
    <li>
    <p>因为 <strong>DEVMODE</strong> 结构包含联合 (union)，所以必须挑选出我们所需要的那些成员。 </p>
    <li>
    <p>在 .NET Framework 中映射到字符串的数组必须作为相同大小的字符串封送。 </p>
    <li>
    <p>为简单起见，可以将嵌套结构平面化（例如，将 <strong>POINTL</strong> 结构替换为两个托管的 <strong>int</strong> 类型。） </p>
    </li>
</ul>
<pre>[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]
public string dmDeviceName;
public short  dmSpecVersion;
public short  dmDriverVersion;
public short  dmSize;
public short  dmDriverExtra;
public int    dmFields;
public int    dmPositionX;
public int    dmPositionY;
public int    dmDisplayOrientation;
public int    dmDisplayFixedOutput;
public short  dmColor;
public short  dmDuplex;
public short  dmYResolution;
public short  dmTTOption;
public short  dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public short  dmLogPixels;
public short  dmBitsPerPel;
public int    dmPelsWidth;
public int    dmPelsHeight;
public int    dmDisplayFlags;
public int    dmDisplayFrequency;
public int    dmICMMethod;
public int    dmICMIntent;
public int    dmMediaType;
public int    dmDitherType;
public int    dmReserved1;
public int    dmReserved2;
public int    dmPanningWidth;
public int    dmPanningHeight;
};
</pre>
<p>在 .NET Framework 中初始化 <strong>DEVMODE</strong> 结构的新实例时，请确保 <em>dmDeviceName</em>、<em>dmFormName</em> 和 <em>dmSize</em> 值设置恰当。为此，我在示例应用程序的 <strong>NativeMethods</strong> 类中添加了以下方法：</p>
<pre>public static DEVMODE CreateDevmode()
{
DEVMODE dm = new DEVMODE();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf(dm);
return dm;
}
</pre>
<p><strong>在</strong> <strong>C# </strong><strong>中旋转屏幕</strong> </p>
<p>以下 C# 代码将前面讨论的技术结合起来，并展示了如何在托管代码中顺时针旋转屏幕。请注意，这段代码只对支持相应显示设置的设备起作用。</p>
<pre>// initialize the DEVMODE structure
DEVMODE dm = new DEVMODE();
dm.dmDeviceName = new string(new char[32]);
dm.dmFormName = new string(new char[32]);
dm.dmSize = Marshal.SizeOf(dm);
if (0 != NativeMethods.EnumDisplaySettings(
null,
NativeMethods.ENUM_CURRENT_SETTINGS,
ref dm))
{
// swap width and height
int temp = dm.dmPelsHeight;
dm.dmPelsHeight = dm.dmPelsWidth;
dm.dmPelsWidth = temp;
// determine new orientation
switch(dm.dmDisplayOrientation)
{
case NativeMethods.DMDO_DEFAULT:
dm.dmDisplayOrientation = NativeMethods.DMDO_270;
break;
case NativeMethods.DMDO_270:
dm.dmDisplayOrientation = NativeMethods.DMDO_180;
break;
case NativeMethods.DMDO_180:
dm.dmDisplayOrientation = NativeMethods.DMDO_90;
break;
case NativeMethods.DMDO_90:
dm.dmDisplayOrientation = NativeMethods.DMDO_DEFAULT;
break;
default:
// unknown orientation value
// add exception handling here
break;
}
int iRet = NativeMethods.ChangeDisplaySettings(ref dm, 0);
if (NativeMethods.DISP_CHANGE_SUCCESSFUL != iRet)
{
// add exception handling here
}
}
</pre>
<div><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><img alt="" src="http://i.msdn.microsoft.com/ms812499.arrow_px_up(zh-cn,MSDN.10).gif" border=0><font color=#0033cc> </font></a><a href="http://msdn.microsoft.com/zh-cn/library/ms812499.aspx#mainSection"><font color=#0033cc>返回页首</font></a> <br></div>
<h3 id=EBAA>小结</h3>
<ul>
    <li>
    <p>使用 <strong>EnumDisplaySettings</strong> API 来获取当前显示设置的相关信息。 </p>
    <li>
    <p>使用 <strong>EnumDisplaySettings</strong> API 来枚举所有受支持的显示设置。 </p>
    <li>
    <p><strong>DEVMODE</strong> 结构包含关于给定的显示模式的所有信息。 </p>
    <li>
    <p>使用 <strong>ChangeDisplaySettings</strong> 来切换到由有效的 <strong>DEVMODE</strong> 结构指定的新显示模式。 </p>
    <li>
    <p>使用平台调用服务以从托管代码实现。</p>
    </li>
</ul>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 21:38 <a href="http://www.cnitblog.com/MartinYao/articles/52414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用反射操作窗体</title><link>http://www.cnitblog.com/MartinYao/articles/52401.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 09 Dec 2008 12:31:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/52401.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/52401.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/52401.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/52401.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/52401.html</trackback:ping><description><![CDATA[<div class=overview>
<div id="">
<p>John Dyer<br>Dallas Theological Seminary</p>
</div>
<div id="">
<p>适用于：<br>Microsoft Visual Studio 2005 及早期版本<br>ASP.NET 1.1<br>C# 编程语言<br>Visual Basic 编程语言</p>
</div>
<div id="">
<p><strong>摘要：</strong>使用反射以单行代码将业务对象绑定到 ASP.NET Web 窗体，从而降低复杂性并减少错误。（本文包含一些指向英文站点的链接。请注意，在示例文件中，程序员的注释使用的是英文，本文中将其译为中文是为了便于读者理解。）</p>
</div>
<div id="">
<p><a href="http://download.microsoft.com/download/0/6/7/067D02CE-35CE-4A57-9203-394A2A569C38/MSDNFormBinding.msi" target=_blank><u><font color=#0000ff>下载 MSDFormBinding.msi 示例文件</font></u></a>。</p>
</div>
</div>
<center><img height=6 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119867_1.gif" width=30 border=0></center>
<div style="HEIGHT: 18px"></div>
<h5 style="PADDING-TOP: 2px">本页内容</h5>
<table style="MARGIN-TOP: 7px; MARGIN-BOTTOM: 12px" cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#E1AA"><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#E1AA"><font color=#0000ff><u>引言</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EXAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EXAA"><font color=#0000ff><u>简化和缩短窗体代码</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EUAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EUAA"><font color=#0000ff><u>开始：从反射中检索属性列表</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ERAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ERAA"><font color=#0000ff><u>将对象属性值绑定到控件</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EOAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EOAA"><font color=#0000ff><u>用已知属性设置未知控件的值</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ELAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#ELAA"><font color=#0000ff><u>反转过程：BindControlsToObject</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EIAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EIAA"><font color=#0000ff><u>性能和 FormBinding 方案的扩展</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EFAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EFAA"><font color=#0000ff><u>结论</u></font></a></td>
        </tr>
        <tr vAlign=top>
            <td><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EEAA"><font color=#0000ff><u><img height=9 hspace=4 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119865_2.gif" width=7 vspace=2 border=0></u></font></a></td>
            <td class=onThisPage><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#EEAA"><u><font color=#0000ff>参考资料</font></u></a></td>
        </tr>
    </tbody>
</table>
<div id=""><a name=E1AA></a>
<h2>引言</h2>
<div id="">
<p>在 Web 开发人员的最常见任务之中，有一项任务是他们要反复执行的：建立更新数据库表的简单窗体。我们将创建一个列表页面和一个窗体页面，列表页面中以表格形式显示记录，窗体页面中带有用于各个数据库字段的适当的窗体控件。许多开发人员还使用表示数据库表的业务对象将代码组织到分为多层的设计中。如果以业务对象 (<strong>Document</strong>) 来表示数据库表 (<strong>Documents</strong>)，许多窗体的代码看上去将如下所示：</p>
</div>
<pre class=codeSample>&lt;script runat="server"&gt;
protected void Page_Load(Object Src, EventArgs E) {
if (!IsPostBack) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
Title.Text = document.Title;
Active.Checked = document.Active;
CreatedDate.Text = document.CreatedDate.ToString();
AuthorID.FindByValue(document.AuthorID.ToString()).Selected =
true;
// ... 等等
HtmlBody.Text = document.HtmlBody;
}
}
protected void SaveButton_Click(Object Src, EventArgs E) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
document.Title = Title.Text;
document.Active = Active.Checked;
document.CreatedDate = Convert.ToDateTime(CreatedDate.Text);
document.AuthorID = Convert.ToInt32(AuthorID.SelectedItem.Value);
// ... 等等
document.HtmlBody = HtmlBody.Text;
Documents.Update(document);
}
&lt;/script&gt;
</pre>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EXAA></a>
<h2>简化和缩短窗体代码</h2>
<div id="">
<p>在以上代码中，对每个控件进行显式转换，并将其设置为窗体控件的正确属性。根据属性和窗体控件的数量，这部分代码可能会变长并难以管理。代码还应包含类型转换的错误更正和 <strong>ListControl</strong>，这将进一步增加复杂性。即使窗体是由代码生成工具（例如 Eric J. Smith 的优秀的 <a href="http://www.ericjsmith.net/codesmith/" target=_blank><u><font color=#0000ff>CodeSmith</font></u></a>）生成的，当需要任何自定义逻辑关系时，很容易引入错误。</p>
</div>
<div id="">
<p>使用反射，可以仅使用单行代码便将业务对象的所有属性绑定到相应的窗体控件，从而减少代码的行数并增强可读性。完成反射系统的建立后，以上代码将简化为：</p>
</div>
<pre class=codeSample>protected void Page_Load(Object Src, EventArgs E) {
if (!IsPostBack) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
FormBinding.BindObjectToControls(document);
}
}
protected void Save_Click(Object Src, EventArgs E) {
Document document =
Documents.GetDocument(Request.QueryString["DocumentID"]);
FormBinding.BindControlsToObject(document);
Documents.Update(document);
}
</pre>
<div id="">
<p>此代码可用于所有标准的 ASP.NET 控件（<strong>TextBox</strong>、<strong>DropDownList</strong>、<strong>CheckBox</strong> 等）和许多第三方控件（例如 <a href="http://www.freetextbox.com/" target=_blank><u><font color=#0000ff>Free TextBox</font></u></a> 和 <a href="http://www.eworldui.net/" target=_blank><u><font color=#0000ff>Calendar Popup</font></u></a>）。无论有多少业务对象属性和窗体控件，这一行代码都能提供所需的全部功能，只要窗体控件的 ID 与业务对象属性名相匹配。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EUAA></a>
<h2>开始：从反射中检索属性列表</h2>
<div id="">
<p>首先，我们需要检查业务对象的属性，并查找与业务对象属性名具有相同 ID 的 ASP.NET 控件。以下代码构成了绑定查找的基础：</p>
</div>
<pre class=codeSample>public class FormBinding {
public static void BindObjectToControls(object obj,
Control container) {
if (obj == null) return;
Type objType = obj.GetType();
PropertyInfo[] objPropertiesArray =
objType.GetProperties();
foreach (PropertyInfo objProperty in objPropertiesArray) {
Control control =
container.FindControl(objProperty.Name);
if (control != null) {
// 处理控件 ...
}
}
}
}
</pre>
<div id="">
<p>在以上代码中，方法 <strong>BindObjectsToControls</strong> 接受了业务对象 obj 和一个容器控件。容器控件通常是当前 Web 窗体的 <strong>Page</strong> 对象。如果所用版本是会在运行时更改控件嵌套顺序的 ASP.NET 1.x MasterPages，您将需要指定窗体控件所在的 <strong>Content</strong> 控件。这是在 ASP.NET 1.x 中，<strong>FindControl</strong> 方法对嵌套控件和命名容器的处理方式导致的。</p>
</div>
<div id="">
<p>在以上代码中，我们获取了业务对象的 <strong>Type</strong>，然后使用该 <strong>Type</strong> 来获取 <strong>PropertyInfo</strong> 对象的数组。每个 <strong>PropertyInfo</strong> 对象都包含关于业务对象属性以及从业务对象获取和设置值的能力的信息。我们使用 foreach 循环检查具有与业务对象属性名 (<strong>PropertyInfo.Name</strong>) 对应的 ID 属性的 ASP.NET 控件的容器。如果找到控件，则尝试将属性值绑定到该控件。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=ERAA></a>
<h2>将对象属性值绑定到控件</h2>
<div id="">
<p>过程中的大部分操作是在此阶段执行的。我们需要用对象的属性值来填充找到的控件。一种实现方法是为每种控件类型创建一个 if ... else 语句。派生自 <strong>ListControl</strong>（<strong>DropDownList</strong>、<strong>RadioButtonList</strong>、<strong>CheckBoxList</strong> 和 <strong>ListBox</strong>）的所有控件都具有可以统一访问的公用接口，所以可以将它们编组在一起。如果找到的控件是 <strong>ListControl</strong>，我们可以将其作为 <strong>ListControl</strong> 进行转换，然后设置选定项：</p>
</div>
<pre class=codeSample>Control control = container.FindControl(objProperty.Name);
if (control != null) {
if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty.GetValue(obj,
null).ToString();
ListItem listItem =
listControl.Items.FindByValue(propertyValue);
if (listItem != null) listItem.Selected = true;
} else {
// 处理其他控件类型
}
}
</pre>
<div id="">
<p>不幸的是，其他控件类型并不从父类中派生。以下几个公用控件都具有 <strong>.Text</strong> 字符串属性：<strong>TextBox</strong>、<strong>Literal</strong> 和 <strong>Label</strong>。但该属性不是从公用父类中派生出来的，所以需要分别转换每种控件类型。我们还需要转换其他控件类型，例如 <strong>Calendar</strong> 控件，以便使用适当的属性（在 <strong>Calendar</strong> 的例子中，是 <strong>SelectedDate</strong> 属性）。要包含所有标准的 ASP.NET 窗体控件，并访问窗体控件的正确属性并不需要太多的代码行。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty.GetValue(obj,
null).ToString();
ListItem listItem = listControl.Items.FindByValue(propertyValue);
if (listItem != null) listItem.Selected = true;
} else if (control is CheckBox) {
if (objProperty.PropertyType == typeof(bool))
((CheckBox) control).Checked = (bool)
objProperty.GetValue(obj, null);
} else if (control is Calendar) {
if (objProperty.PropertyType == typeof(DateTime))
((Calendar) control).SelectedDate = (DateTime)
objProperty.GetValue(obj, null);
} else if (control is TextBox) {
((TextBox) control).Text = objProperty.GetValue(obj,
null).ToString();
} else if (control is Literal)(
//... 等等。还可用于标签等属性。
}
</pre>
<div id="">
<p>此方法完整地涵盖了标准的 ASP.NET 1.x 控件。从这个角度来看，我们拥有了功能齐全的 <strong>BindObjectToControls</strong> 方法。但在起作用的同时，此方法的应用范围会受到限制，因为它仅考虑内置的 ASP.NET 1.x 控件。如果要支持新的 ASP.NET 2.0 控件，或者要使用任何第三方控件，我们必须在 <strong>FormBinding</strong> 项目中引用控件的程序集，并将控件类型添加到 if ... else 列表。 </p>
</div>
<div id="">
<p>此问题的解决方案是第二次使用反射，以查看各个控件的属性，并找出控件是否具有与业务对象的属性类型对应的属性类型。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EOAA></a>
<h2>用已知属性设置未知控件的值</h2>
<div id="">
<p>如上所述，有些控件共享字符串属性 <strong>.Text</strong>，大多数窗体控件以实质相同的方式使用此属性。该属性用于获取和设置用户输入的数据。有大量控件还使用了其他一些公用属性和属性类型。以下是这些属性中的一些：称为 <strong>.SelectedDate</strong> 的 <strong>DateTime</strong> 属性，它在许多日历和日期选取器控件中使用；称为 <strong>.Checked</strong> 的布尔属性，它在布尔型控件中使用；称为 <strong>.Value</strong> 的字符串属性，它常见于隐藏控件。这四个属性（<strong>string Text</strong>、<strong>string Value</strong>、<strong>bool Checked</strong> 和 <strong>DateTime SelectedDate</strong>）是最常见的控件属性。如果可以将系统设计成无论何种控件类型，都绑定到这些属性，那么我们的绑定方法将适用于使用那四个属性的任何控件。 </p>
</div>
<div id="">
<p>在以下代码中，我们将第二次使用反射（这一次是对窗体控件使用，而不是对业务对象使用），以确定它是否具有任何常用属性。如果有，则尝试将业务对象的属性值设置为控件的属性。作为示例，我们将对整个 PropertyInfo 数组进行迭代，并查找称为 <strong>.Text</strong> 的字符串属性。如果控件具有该属性，则将数据从业务对象发送到该控件的属性。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
// ...
} else {
// 获取控件的类型和属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
// 查找 .Text 属性
//
foreach (PropertyInfo controlProperty
in controlPropertiesArray) {
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 设置控件的 .Text 属性
//
controlProperty.SetValue(control,
(String) objProperty.GetValue(obj, null), null);
}
}
}
</pre>
<div id="">
<p>如果找到 <strong>.Text</strong>，则使用 <strong>PropertyInfo</strong> 类的 <strong>GetValue</strong> 方法从业务对象的属性中检索值。然后，使用控件的 <strong>.Text</strong> 属性的 <strong>SetValue</strong> 方法。在此，我们还使用 Type 命令将控件的属性设置为 typeof(String)，并使用 (String) 符号显式转换来自属性的值。 </p>
</div>
<div id="">
<p>为了使 <strong>BindObjectToControls</strong> 方法完整，我们还需要处理其他公用属性，即 <strong>.Checked</strong>、<strong>.SelectedDate</strong> 和 <strong>.Value</strong>。在以下代码中，我们将控件属性搜索打包到称为 <strong>FindAndSetControlProperty</strong> 的辅助方法中，以简化代码。</p>
</div>
<pre class=codeSample>if (control is ListControl) {
// ...
} else {
// 获取控件的属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
bool success = false;
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Checked", typeof(bool) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"SelectedDate", typeof(DateTime) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Value", typeof(String) );
if (!success)
success = FindAndSetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Text", typeof(String) );
}
private static void FindAndSetControlProperty(object obj,
PropertyInfo objProperty, Control control,
PropertyInfo[] controlPropertiesArray, string propertyName,
Type type) {
// 在整个控件属性中进行迭代
foreach (PropertyInfo controlProperty in
controlPropertiesArray) {
// 检查匹配的名称和类型
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 将控件的属性设置为
// 业务对象属性值
controlProperty.SetValue(control,
Convert.ChangeType(
objProperty.GetValue(obj, null), type) , null);
return true;
}
}
return false;
}
</pre>
<div id="">
<p>以上属性检查的顺序很重要，因为有些控件具有以上属性中的多个，但我们只想设置一个。例如，<strong>CheckBox</strong> 控件既有 <strong>.Text</strong> 属性也有 <strong>.Checked</strong> 属性。在此示例中，我们希望使用 <strong>.Checked </strong>属性而不是 <strong>.Text</strong> 属性，所以将 <strong>.Checked</strong> 放在属性搜索顺序的首位。任何情况下，如果找到具有正确名称和类型的控件属性，则尝试将控件的属性设置为业务对象属性的值。</p>
</div>
<div id="">
<p>从这个角度来看，我们拥有了功能齐全的 <strong>BindObjectToControls</strong> 方法。利用该方法，我们可以在 ASPX 窗体上的任何地方，使用任何类和控件的任意组合进行调用，而这确实有效。现在，我们需要创建在提交窗体时进行反转的方法。我们需要从表示用户输入的控件中检索新值，而不是将控件属性的值设置为业务对象的值。 </p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=ELAA></a>
<h2>反转过程：BindControlsToObject</h2>
<div id="">
<p>在 <strong>BindControlsToObject</strong> 方法中，我们将以同样的方式开始，即从业务对象中检索属性的列表，然后使用 <strong>FindControl</strong> 方法找到具有与对象属性相匹配的 ID 的控件。如果找到控件，则检索值并将该值返回给业务对象。此部分还将包含 <strong>ListControl</strong> 的单独代码，因为这些控件具有公用接口。我们将使用另一种辅助方法来搜索并检索控件中的值，然后将该值返回给业务对象。</p>
</div>
<pre class=codeSample>public static void BindControlsToObject(object obj,
Control container) {
Type objType = obj.GetType();
PropertyInfo[] objPropertiesArray = objType.GetProperties();
foreach (PropertyInfo objProperty in objPropertiesArray) {
if (control is ListControl) {
ListControl listControl = (ListControl) control;
if (listControl.SelectedItem != null)
objProperty.SetValue(obj,
Convert.ChangeType(list.SelectedItem.Value,
objProperty.PropertyType), null);
} else {
// 获取控件的属性
//
Type controlType = control.GetType();
PropertyInfo[] controlPropertiesArray =
controlType.GetProperties();
bool success = false;
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Checked", typeof(bool) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"SelectedDate", typeof(DateTime) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Value", typeof(String) );
if (!success)
success = FindAndGetControlProperty(obj,
objProperty, control, controlPropertiesArray,
"Text", typeof(String) );
}
}
}
private static void FindAndGetControlProperty(object obj,
PropertyInfo objProperty, Control control, PropertyInfo[]
controlPropertiesArray, string propertyName, Type type) {
// 在整个控件属性中进行迭代
foreach (PropertyInfo controlProperty in
controlPropertiesArray) {
// 检查匹配的名称和类型
if (controlPropertiesArray.Name == "Text" &amp;&amp;
controlPropertiesArray.PropertyType == typeof(String)) {
// 将控件的属性设置为
// 业务对象属性值
try {
objProperty.SetValue(obj,
Convert.ChangeType(
controlProperty.GetValue(control, null),
objProperty.PropertyType) , null);
return true;
} catch {
// 无法将来自窗体控件
// 的数据转换为
// objProperty.PropertyType
return false;
}
}
}
return true;
}
</pre>
<div id="">
<p>完成这两种方法后，我们的窗体语法将得到简化，如以上<a href="http://msdn.microsoft.com/library/en-us/dnaspp/html/aspformbinding.asp?frame=true#aspformbinding_topic2" target=_blank><u><font color=#0000ff>简化和缩短窗体代码</font></u></a>中所述。每个属性和控件的类型转换与错误更正都是自动进行的。这两种方法（<strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong>）为开发人员创建窗体提供了很大的灵活性。它们还可以用于处理以下这些常见方案： </p>
</div>
<table cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>如果将新属性添加到业务对象，并且需要在窗体上访问该新属性，那么开发人员只需将控件添加到页面，并将控件的 ID 设置为新属性的名称，<strong>FormBinding</strong> 方法将处理剩下的一切。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>如果开发人员需要更改用于特定属性的控件的类型，例如从 <strong>TextBox</strong> 更改为第三方的 HTML 编辑器控件，他/她仅需要确保新控件具有以上属性之一（例如 <strong>.Text</strong> ），窗体将以与之前完全一致的方式进行工作。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>全部使用 <strong>TextBox</strong> 控件也可以快速生成窗体，但输入仍将转换为适用于业务对象属性的正确类型。例如，可以用 <strong>TextBox</strong> 控件来代替 <strong>Calendar</strong> 控件或第三方的日期选取器控件。只要用户输入 DateTime 字符串作为值，便会将 <strong>TextBox</strong> 的 <strong>.Text</strong> 属性中的值转换为 DateTime，就如同它是日历控件上的 <strong>SelectedDate</strong> 属性一样。如果以后将 <strong>TextBox</strong> 更改为日期选取器控件，逻辑关系将保持不变。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>通过将所有控件更改为 <strong>Literal</strong> 控件，开发人员还可以快速创建&#8220;视图&#8221;页面。<strong>Literal</strong> 的 <strong>.Text</strong> 属性将被设置为业务对象属性的值，就如同它是 <strong>TextBox</strong> 一样。 </p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p>在实际方案中，窗体还包含其他数据类型和自定义配置。用于处理这些特定操作的代码可以放置在对 <strong>BindObjectToControls </strong>和 <strong>BindControlsToObject</strong> 的调用之后。 </p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EIAA></a>
<h2>性能和 FormBinding 方案的扩展</h2>
<div id="">
<p>有些开发人员可能想知道，使用反射引起的性能下降是否值得。在我的测试中，使用了具有七种属性（int DocumentID、bool Active、DateTime Created、int CategoryID、String Title、string Author 和 String htmlText）的对象，<strong>BindObjectToControls</strong> 用时约 1/3 毫秒，<strong>BindControlsToObject</strong> 用时大约 1 毫秒。这些值是通过循环运行 1000 次 <strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong> 方法得到的。对于常见的&#8220;添加&#8221;和&#8220;编辑&#8221;窗体方案，这样的性能应不会引起任何重大的问题，而且确实能够提高开发速度和灵活性。 </p>
</div>
<div id="">
<p>尽管此方法几乎适用于每种窗体，但有时可能需要修改以上代码。在某些方案中，开发人员要使用的控件可能并不使用以上属性之一作为其主要接口。在此情形中，需要更新 <strong>FormBinding</strong> 方法，以包括该属性和类型。 </p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EFAA></a>
<h2>结论</h2>
<div id="">
<p>这两种 <strong>FormBinding</strong> 方法（<strong>BindObjectToControls</strong> 和 <strong>BindControlsToObject</strong>）可用于极大地简化窗体代码，并为 ASP.NET 窗体的开发提供了最大的灵活性。对它们的使用使我获益良多，希望您的团队同样能够从中受益。</p>
</div>
</div>
<div id="">
<div style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><a href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><img height=9 src="http://www.chinaitpower.com/A-A-A/2005/07/17/20050717090746119860_11.gif" width=7 border=0></a><a class=topOfPage href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/aspformbinding.mspx#top"><u><font color=#0000ff>返回页首</font></u></a></div>
<a name=EEAA></a>
<h2>参考资料</h2>
<table cellSpacing=0 cellPadding=0 border=0>
    <tbody>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p><a href="http://shopping.msn.com/search/detail.aspx?pcId=12237&amp;prodId=734690&amp;ptnrid=141&amp;ptnrdata=0" target=_blank><u><font color=#0000ff>ASP.NET Unleashed</font></u></a></p>
            </div>
            </td>
        </tr>
        <tr>
            <td class=listBullet vAlign=top>&#8226;</td>
            <td class=listItem>
            <div id="">
            <p><a href="http://shopping.msn.com/search/detail.aspx?pcId=4650&amp;prodId=1627167&amp;ptnrid=141&amp;ptnrdata=0" target=_blank><u><font color=#0000ff>Programming Microsoft ASP.NET</font></u></a></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div id="">
<p><strong>作者简介</strong></p>
</div>
<div id="">
<p>John Dyer 是 <a href="http://www.dts.edu/" target=_blank><u><font color=#0000ff>Dallas Theological Seminary</font></u></a> 的顶级 Web 开发人员，负责指导他们富有盛誉的联机培训计划，该计划建立在 <a href="http://www.telligentsystems.com/" target=_blank><u><font color=#0000ff>Telligent System</font></u></a> 的 Community Server 之上。他还是广泛使用的 ASP.NET HTML 编辑器 <a href="http://www.freetextbox.com/" target=_blank><u><font color=#0000ff>FreeTextBox</font></u></a> 的作者，该控件为除他之外的很多人带来了经济收益。在业余时间，他在 Dallas 神学院攻读神学硕士，并在准备 2005 年 1 月 1 日的婚礼。</p>
</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/52401.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2008-12-09 20:31 <a href="http://www.cnitblog.com/MartinYao/articles/52401.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Process cmd</title><link>http://www.cnitblog.com/MartinYao/articles/28621.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:18:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28621.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28621.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28621.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28621.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28621.html</trackback:ping><description><![CDATA[<p>using System;<br>using System.Diagnostics;<br>using System.ComponentModel;</p>
<p>namespace MyProcessSample<br>{<br>&nbsp;/// &lt;summary&gt;<br>&nbsp;/// Shell for the sample.<br>&nbsp;/// &lt;/summary&gt;<br>&nbsp;class MyProcess<br>&nbsp;{<br>&nbsp;&nbsp;// These are the Win32 error code for file not found or access denied.<br>&nbsp;&nbsp;const int ERROR_FILE_NOT_FOUND =2;<br>&nbsp;&nbsp;const int ERROR_ACCESS_DENIED = 5;</p>
<p>&nbsp;&nbsp;/// &lt;summary&gt;<br>&nbsp;&nbsp;/// Prints a file with a .doc extension.<br>&nbsp;&nbsp;/// &lt;/summary&gt;<br>&nbsp;&nbsp;void PrintDoc()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;Process myProcess = new Process();<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;try<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;// Get the path that stores user documents.<br>&nbsp;&nbsp;&nbsp;&nbsp;string myDocumentsPath = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Environment.GetFolderPath(Environment.SpecialFolder.Personal);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;myProcess.StartInfo.FileName = myDocumentsPath + "<a href="file://MyFile.doc/">\\MyFile.doc</a>"; <br>&nbsp;&nbsp;&nbsp;&nbsp;myProcess.StartInfo.Verb = "Print";<br>&nbsp;&nbsp;&nbsp;&nbsp;myProcess.StartInfo.CreateNoWindow = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;myProcess.Start();<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;catch (Win32Exception e)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;if(e.NativeErrorCode == ERROR_FILE_NOT_FOUND)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(e.Message + ". Check the path.");<br>&nbsp;&nbsp;&nbsp;&nbsp;} </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;else if (e.NativeErrorCode == ERROR_ACCESS_DENIED)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Note that if your word processor might generate exceptions<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// such as this, which are handled first.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(e.Message + <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;". You do not have permission to print this file.");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}</p>
<p><br>&nbsp;&nbsp;public static void Main()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;MyProcess myProcess = new MyProcess();<br>&nbsp;&nbsp;&nbsp;myProcess.PrintDoc();<br>&nbsp;&nbsp;}<br>&nbsp;}<br>}</p>
<p>&nbsp;<br>C++&nbsp; Copy Code <br>#using &lt;System.dll&gt;</p>
<p>using namespace System;<br>using namespace System::Diagnostics;<br>using namespace System::ComponentModel;</p>
<p>// These are the Win32 error code for file not found or access denied.<br>#define ERROR_FILE_NOT_FOUND 2<br>#define ERROR_ACCESS_DENIED&nbsp; 5<br>int main()<br>{<br>&nbsp;&nbsp; Process^ myProcess = gcnew Process;<br>&nbsp;&nbsp; try<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get the path that stores user documents.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String^ myDocumentsPath = Environment::GetFolderPath( Environment::SpecialFolder::Personal );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess-&gt;StartInfo-&gt;FileName = String::Concat( myDocumentsPath, "<a href="file://MyFile.doc/">\\MyFile.doc</a>" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess-&gt;StartInfo-&gt;Verb = "Print";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess-&gt;StartInfo-&gt;CreateNoWindow = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess-&gt;Start();<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; catch ( Win32Exception^ e ) <br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( e-&gt;NativeErrorCode == ERROR_FILE_NOT_FOUND )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console::WriteLine( "{0}. Check the path.", e-&gt;Message );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( e-&gt;NativeErrorCode == ERROR_ACCESS_DENIED )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Note that if your word processor might generate exceptions<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // such as this, which are handled first.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console::WriteLine( "{0}. You do not have permission to print this file.", e-&gt;Message );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; }</p>
<p>}</p>
<p><br>&nbsp;</p>
<p>The following example uses the Process class itself and a static Start method to start a process.</p>
<p>Visual Basic&nbsp; Copy Code <br>Imports System<br>Imports System.Diagnostics<br>Imports System.ComponentModel</p>
<p><br>Namespace MyProcessSample<br>&nbsp;&nbsp;&nbsp; _<br>&nbsp;&nbsp; '/ &lt;summary&gt;<br>&nbsp;&nbsp; '/ Shell for the sample.<br>&nbsp;&nbsp; '/ &lt;/summary&gt;<br>&nbsp;&nbsp; Class MyProcess <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ Opens the Internet Explorer application.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Public Sub OpenApplication(myFavoritesPath As String)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Start Internet Explorer. Defaults to the home page.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start("IExplore.exe")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Display the contents of the favorites folder in the browser.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start(myFavoritesPath)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Sub 'OpenApplication<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ Opens urls and .html documents using Internet Explorer.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sub OpenWithArguments()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' url's are not considered documents. They can only be opened<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' by passing them as arguments.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start("IExplore.exe", "<a href="http://www.northwindtraders.com/">www.northwindtraders.com</a>")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Start a Web page using a browser associated with .html and .asp files.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start("IExplore.exe", "C:\myPath\myFile.htm")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start("IExplore.exe", "C:\myPath\myFile.asp")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Sub 'OpenWithArguments<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ Uses the ProcessStartInfo class to start new processes, both in a minimized <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ mode.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '/ &lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sub OpenWithStartInfo()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim startInfo As New ProcessStartInfo("IExplore.exe")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startInfo.WindowStyle = ProcessWindowStyle.Minimized<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start(startInfo)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startInfo.Arguments = "<a href="http://www.northwindtraders.com/">www.northwindtraders.com</a>"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start(startInfo)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Sub 'OpenWithStartInfo<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Shared Sub Main()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ' Get the path that stores favorite links.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim myFavoritesPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Favorites)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim myProcess As New MyProcess()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess.OpenApplication(myFavoritesPath)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess.OpenWithArguments()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myProcess.OpenWithStartInfo()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End Sub 'Main <br>&nbsp;&nbsp; End Class 'MyProcess<br>End Namespace 'MyProcessSample</p>
<p>&nbsp;<br>C#&nbsp; Copy Code <br>using System;<br>using System.Diagnostics;<br>using System.ComponentModel;</p>
<p>namespace MyProcessSample<br>{<br>&nbsp;/// &lt;summary&gt;<br>&nbsp;/// Shell for the sample.<br>&nbsp;/// &lt;/summary&gt;<br>&nbsp;class MyProcess<br>&nbsp;{<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;/// &lt;summary&gt;<br>&nbsp;&nbsp;/// Opens the Internet Explorer application.<br>&nbsp;&nbsp;/// &lt;/summary&gt;<br>&nbsp;&nbsp;void OpenApplication(string myFavoritesPath)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;// Start Internet Explorer. Defaults to the home page.<br>&nbsp;&nbsp;&nbsp;Process.Start("IExplore.exe");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Display the contents of the favorites folder in the browser.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process.Start(myFavoritesPath);<br>&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;/// &lt;summary&gt;<br>&nbsp;&nbsp;/// Opens urls and .html documents using Internet Explorer.<br>&nbsp;&nbsp;/// &lt;/summary&gt;<br>&nbsp;&nbsp;void OpenWithArguments()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;// url's are not considered documents. They can only be opened<br>&nbsp;&nbsp;&nbsp;// by passing them as arguments.<br>&nbsp;&nbsp;&nbsp;Process.Start("IExplore.exe", "<a href="http://www.northwindtraders.com/">www.northwindtraders.com</a>");<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;// Start a Web page using a browser associated with .html and .asp files.<br>&nbsp;&nbsp;&nbsp;Process.Start("IExplore.exe", "C:\\myPath\\myFile.htm");<br>&nbsp;&nbsp;&nbsp;Process.Start("IExplore.exe", "C:\\myPath\\myFile.asp");<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;/// &lt;summary&gt;<br>&nbsp;&nbsp;/// Uses the ProcessStartInfo class to start new processes, both in a minimized <br>&nbsp;&nbsp;/// mode.<br>&nbsp;&nbsp;/// &lt;/summary&gt;<br>&nbsp;&nbsp;void OpenWithStartInfo()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");<br>&nbsp;&nbsp;&nbsp;startInfo.WindowStyle = ProcessWindowStyle.Minimized;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;Process.Start(startInfo);<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;startInfo.Arguments = "<a href="http://www.northwindtraders.com/">www.northwindtraders.com</a>";<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;Process.Start(startInfo);<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;static void Main()<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;// Get the path that stores favorite links.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;string myFavoritesPath = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;Environment.GetFolderPath(Environment.SpecialFolder.Favorites);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;MyProcess myProcess = new MyProcess();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;myProcess.OpenApplication(myFavoritesPath);<br>&nbsp;&nbsp;&nbsp;myProcess.OpenWithArguments();<br>&nbsp;&nbsp;&nbsp;myProcess.OpenWithStartInfo();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;}&nbsp;<br>&nbsp;}<br>}</p>
<p>&nbsp;<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:18 <a href="http://www.cnitblog.com/MartinYao/articles/28621.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自動更新(Application Block/ClickOnce)</title><link>http://www.cnitblog.com/MartinYao/articles/28620.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:15:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28620.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28620.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28620.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28620.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28620.html</trackback:ping><description><![CDATA[<p>　　应用程序更新的方法一般有两种：一是通知用户（比如发E-mail）,让用户到指定的网站地址下载更新的程序；二是将更新的职责从用户那边转移到应用程序自身，由应用程序自身取代用户获取并安装一个软件的更新，客户端应用程序自身负责从一个已知服务器下载并安装更新，用户唯一需要进行干预的是决定是否愿意现在或以后安装新的更新。显然，后者比前者更友好。你现在可以看到类似后一种方法的实际产品，比如Windows XP和Microsoft Money。本文所介绍的.NET应用程序更新组件就可以提供类似的功能。</p>
<p>　　一、.NET应用程序更新组件介绍</p>
<p>　　.NET 应用程序更新组件AppUpdater 是使用.NET框架开发的。尽管AppUpdater 不是微软的产品，但是只要你在VS.NET工具栏中添加了该组件，就可以象使用其它组件一样通过拖拽的方式将该组件从工具栏中拖放到你的应用程序中，并设置一些属性（比如获取更新的位置、频率等）之后就可以使得你的客户端应用程序具备自动更新的功能。</p>
<p>　　二、工作原理</p>
<p>　　要深入理解.NET客户端应用程序更新组件的工作原理，需要仔细研究一下实现客户端应用程序更新有那些必须要做的事情。第一步需要做的是检查是否有更新；当发现有更新时，开始第二步工作——下载更新；当更新下载完成时，进行是最后一步的工作——实现更新。</p>
<p>　　(一) 为更新做检查</p>
<p>　　作为开发者，首先你得告诉应用程序到什么地方去做更新检查，否则它岂不是要大海捞针？其次，确定何时做更新检查。不可能用户每次一运行客户端程序，而它就在后台不停的进行更新检查，那多浪费资源！最后还有一项重要的事情需要解决，那就是如何进行更新检查。.NET应用程序更新组件使用HTTP进行通讯，这就允许客户端应用程序穿透防火墙来进行更新。并且进行更新检查所需要的地址就成了已知的Web服务器的一个URL地址，第一个问题顺利解决。</p>
<p>　　.NET应用程序更新组件在组件生成的基础上产生一个线程，该线程负责进行更新检查。该线程在大多数时间处于休眠状态，但会在设置好的间隔苏醒并实现一次更新检查。应用程序为新的更新所做的检查的频率依赖于各应用自身。进行更新检查的间隔常用值的范围一般是从一个小时到几天。这种轮询的基本方法并不适合于所有情况。比如Microsoft Money只是在用户让它去进行更新检查时它才去检查。在这种情况下，更新轮询线程可被禁用。</p>
<p>　　通过用命令调用更新组件的CheckForUpdate()方法来实现更新检查。</p>
<p>　　关于如何进行更新检查的方法有这样几种：</p>
<p>　　方法一：直接文件检查——使用HTTP来比较服务器和客户端应用程序的最后的修改日期/时间戳是否一致。如果服务器上有更新的文件，客户端就知道可以更新自己了。对于Web浏览器来讲，也是同样的道理，它知道是否需要重新下载一个html页面或图片或是否可以<br>重复使用先前已下载的。当应用程序有一个新的版本可用时，管理员简单地拷贝一个更新的版本来覆盖Web服务器上的旧版本。这种方法的问题在于更新不是自动进行，由此会出现潜在的失败可能。比如，如果管理员正在更新Web服务器上的应用程序版本，同时有个客户正在下载更新之前的版本，那么这个客户的计算机上就会既存在更新之前的一些文件，也存在更新之后新版本的一些文件。基于上述原因，对于重要的应用程序不提倡使用直接文件检查来更新。</p>
<p>　　方法二：显式检查——在服务器上使用一个显式的配置文件。一个可和.NET应用程序更新组件使用的有效服务器显式文件大致是这个样子：.. </p>
<p>＜VersionConfig＞<br>＜AvailableVersion＞1.0.0.0＜/AvailableVersion＞<br>＜ApplicationUrl＞<a href="http://localhost/demos/selfupdate/V1/">http://localhost/demos/selfupdate/V1/</a>＜/<br>ApplicationUrl＞<br>＜/VersionConfig＞ </p>
<p>　　AvailableVersion指定最新的可用程序集的版本号。ApplicationURL属性指定该版本应用程序所在的URL地址。当管理员想要更新客户端应用程序时，他们就会将应用程序的新版本拷贝到Web服务器上并且适当地修改服务器显式文件。客户端自身会探测到服务器显式文件已被修改，然后下载显式文件。客户端随后比较显式文件中指定的程序集版本号与应用程序EXE文件的版本号。如果服务器显式文件中的可用版本号较新，应用程序就知道需要更新了。这种方法对大多数应用程序而言是推荐使用的方法。</p>
<p>　　方法三：XML Web Service 检查——XML WebServices提供一种更高级的更新检查方式。比如，假定你希望在进行更新你的其他用户之前先对一系列早期用户进行更新，如果客户端应用程序调用一个XML WebService来检查一项更新是否可用，那个XML Web Service还可以在数据库中查询该用户并判断该用户是否是早期用户。如果他们是早期用户，XML Web Service就返回一个值表示更新可用。如果不是，Web Service就返回一个值表示更新不可用。但本文介绍的.NET应用程序更新组件并不提供直接的XML Web Service支持。</p>
<p>　　要采用XML Web Service来进行更新检查，首先建立 XML Web Service并挂钩一个OnCheckForUpdate事件。这样就允许你自己的自定义检查代替轮询者线程更新检查。OnCheckForUpdate事件有一个返回值，该值表示更新是否被检测到。</p>
<p>　　(二) 下载更新</p>
<p>　　当.NET应用程序更新组件检测到一项新的更新可用时它会自动启动另一个线程并开始异步后台下载更新。</p>
<p>　　下载使用HTTP-DAV来完成。DAV 是一种扩展的HTTP，.. 它提供诸如目录和文件枚举这样的功能。一项完整的下载过程始于指定一个URL。采用URL来下载依赖于完成更新检查所使用的方式。比如，如果使用服务器显式文件，下载更新所用的URL通过在服务器显式文件 中的ApplicationURL属性指定。</p>
<p>　　更新下载显然需要一定的健壮性。在下载和更新之后让客户端应用程序处于任何不稳定的状态是不可接受的。下载过程中任何问题可能都会出现：更新文件所属的Web 服务器可能会宕机，客户端机器可能会崩溃，或者因为某种原因用户只是简单的关闭了应用程序。由于<br>应用程序正在进行下载，如果它被关闭的话，下载将会停止。一种可选的设计方案是使用单独的系统服务来进行应用程序的下载和更新。使用系统服务，即便应用程序自身没有在运行，更新下载也会继续。实际上，Windows XP就有一种称之为BITS的内建的下载服务，其目的即在此。BITS是Windows XP用来对Windows自身进行下载更新的。想查看更多关于BITS的信息，请参阅<a href="http://msdn.microsoft.com/library/en-us/dnwxp/html/WinXP_BITS.asp">http://msdn.microsoft.com/library/en-us/dnwxp/html/WinXP_BITS.asp</a>。在.NET应用程序更新组件中没有使用这种服务，因此它可以在不支持系统服务的Windows 9x中使用。</p>
<p>　　(三) 实现更新</p>
<p>　　.NET应用程序更新组件通过将下载和更新过程分为单独的两个阶段来获得健壮性。当每一阶段都完成时，在位于客户端应用程序目录下的一个更新显式文件（manifest file）中记载下来。如果下载或更新任何一个阶段中的过程被打断，它就会在下一次应用程序启动时从上一次完成的断点继续原来的工作。每一阶段都可以重新运行，因此如果在一个阶段中间出现失败，重新运行该阶段就可以成功。如果有错误发生，比如在下载过程中与服务器的链接丢失，.NET更新组件将会在稍后重试。如果报告了非常多的错误（例如web服务器再也没有回到在线状态），下载和更新将会被放弃并且报告出错误。</p>
<p>　　我们的第一个方法是简单地启动一个单独的进程来实现更新。这个单独的进程将会首先关闭应用程序进程，实现更新（因为这时候已被解锁），重启应用程序进程并且在完成之后关闭自己。因此，这种设计存在三个基本的问题：</p>
<p>　　. 在某些情况下它不起作用。在更新应用程序时，更新进程关闭原始的应用程序进程，更新进程自身也要被关闭，因此也就不会实现更新。</p>
<p>　　. 我们希望能够自动更新所有要实现更新的代码。我们希望自动安装修补的能力不仅仅发生在应用程序上，而且.NET应用程序更新组件自身也可以。使用这种模式，我们不能更新实现更新的进程。</p>
<p>　　. 强制用户关闭应用程序并在使用过程中等待，这是很不礼貌的。</p>
<p>　　用来实现应用程序更新的最后一种方法是使用.NET框架并行程序集模式。作为试图更新应用程序自身的替代方案，生成一个比目前存在版本新的应用程序版本。</p>
<p>　　新版本可以通过合并目前现存的应用程序目录与下载的更新版本来生成。当新版本完成时，用户在下次重新打开应用程序时会自动使用新版本。原始应用程序的拷贝就可以被移除了。棘手问题是弄清在某个指定时刻哪个版本该被载入。我们介绍一个名称为Appstart的应用程<br>序。Appstart是进入你应用程序的入口点，使用这种模式，你的应用程序目录看起来是这个样子：.. </p>
<p>--＞ Program Files </p>
<p>--＞ MyApp </p>
<p>--＞ Appstart.exe </p>
<p>--＞ Appstart.config </p>
<p>--＞ V1 Folder </p>
<p>--＞ MyApp.exe </p>
<p>--＞ V1.1 Folder </p>
<p>--＞ MyApp.exe </p>
<p>　　要运行你的应用程序，你通常是启动Appstart.exe。如果你想在桌面上有个快捷键，那个快捷键必须应该指向Appstart而不是直接指向应用程序（注意，你可以重命名AppStart.exe 为任何你想要的名字，例如YourApp.exe）Appstart.exe是个非常简单的程序，它读取Appstart.config文件并且载入指定的应用程序。一个有效Appstart.config文件如下所示：</p>
<p>＜Config＞ <br>　＜AppFolderName＞V1 Folder＜/AppFolderName＞ <br>　＜AppExeName＞MyApp.exe＜/AppExeName＞ <br>　＜AppLaunchMode＞appdomain＜/AppLaunchMode＞ <br>＜/Config＞ </p>
<p><br>　　AppFolderName指定包含当前要运行的应用程序版本的子文件夹。AppExeName包含在那个文件夹下要载入的exe文件名。当一个应用程序更新完成时，最后一步就是修改AppFolderName的值为指向应用程序的新版本。这样，下次用户运行应用程序时，就会运行新的应用程序更新后的版本。AppLaunchMode指定如何加载应用程序。有两种方式加载应用程序：第一种方式是使用AppDomains。AppDomains是.NET框架公<br>用语言运行时的特性，也是独立的逻辑单元和管理对象。公用语言运行时允许每个进程中存在多个应用程序域。这样Appstart.exe就能够在单独的AppDomain中同时却是相同的AppStart.exe进程中加载你的应用程序。尽管事实是两个不同的exe 程序在运行（即Appstart.exe和MyApp.exe），但只有一个进程在使用。对于大多数应用程序AppDomains会工作得很好，当然，在一个单独的AppDomain中运行和在一个单独<br>的进程中运行还是有些细微区别的。在这种情况下，AppLaunchMode可以设置为&#8220;process&#8221;，这样就会使应用程序在单独进程中加载。</p>
<p>　　一旦Appstart启动应用程序，它就会进入休眠状态等待应用程序终止。一旦应用程序终止，Appstart也会关闭。</p>
<p>　　三、实例演练</p>
<p>　　前面我们讨论了.NET应用程序更新是如何工作的，现在我们来将它应用在实例中。</p>
<p>　　第一步：建立应用程序来进行更新</p>
<p>　　1. 使用VS.NET生成一个新的Windows应用项目，命名为"SampleApp"。</p>
<p>　　2. 给窗体一个你选择的有趣的背景色。我们将使用背景色来与后面更新的版本区别。</p>
<p>　　3. 现在让我们给这个应用程序增加一个细微的功能，首先给你的窗体增加一个按钮。压缩文件中包含一个拥有简单Windows窗体的程序集。给压缩文件中Samples\SampleApp\SimpleForm 程序集增加一个引用。然后在你的按钮事件句柄中添加两行代码：</p>
<p>.. <br>SimpleForm.Form1 F = new SimpleForm.Form1();<br>F.Show(); </p>
<p>　　4. 将你的build标志从debug转换为RELEASE。这将允许我们避免稍后当我们生成一个应用程序的新版本而同时原始拷贝正在运行产生的pdb文件锁定问题。生成并测试你的应用程序。</p>
<p>　　第二步：添加.NET应用程序更新组件</p>
<p>　　1. 在VS.NET工具栏的组件标签上，右击选择&#8220;自定义工具栏&#8221;。选择&#8220;.NET框架组件&#8221;标签。点&#8220;浏览&#8221;并选择位于压缩文件中AppUpdater 项目下的AppUpdater.dll，单击OK。</p>
<p>　　2. 一个AppUpdater图标现在应该出现在工具栏的组件列表的底部。将AppUpdater 组件拖放到SampleApp窗体上。一个名为appUpdater1的.NET应用程序更新组件的实例会出现在窗体的底部。</p>
<p>　　第三步：设置.NET应用程序更新组件</p>
<p>　　在这一步我们将设置.NET应用程序更新组件。注意这个示例你只需改变最开始的四个属性，其它的采用默认值。</p>
<p>　　AppUpdater属性：这是.NET Application应用程序更新的核心，对于本程序需要做以下设置：</p>
<p>　　（1)AutoFileLoad：这个控制后面要描述的命令下载特征，现在将它设置为true。</p>
<p>　　（2）ChangeDetectionMode：该枚举决定如何为更新进行检查。在该例中，我们将使用一个服务器显式检查，因此将这个值设置为&#8220;ServerManifestCheck &#8221;。</p>
<p>　　（3）ShowDefaultUI： .NET 应用程序更新组件具有一系列用户界面来通知用户一些事件，比如有一个新的更新可以使用了或者在更新期间发生错误等。这种用户界面可以通过设置默认的用户界面为无效而用自定义应用程序指定的用户界面来代替它，挂钩适当的事件（比如<br>OnUpdateComplete）并弹出自定义用户界面。对于该例我们将使用默认的用户界面，因此将这个值设置为true 。</p>
<p>　　（4）UpdateUrl ：UpdateUrl 是决定更新程序到何处去寻找更新的。在该例中我们使用一个服务器显式文件来检查更新，因此这个属性应当设置为服务器显式文件的URL 。</p>
<p>　　在该例中将它设置为：<a href="http://yourwebserver/SampleApp_ServerSetup/UpdateVersion.xml">http://yourWebserver/SampleApp_ServerSetup/UpdateVersion.xml</a>。请用你的Web 服务器名<br>称来代替&#8220;yourWebserver &#8221;。</p>
<p>　　Downloader 属性:AppUpdater 组件有两个子组件。第一个称之为Downloader，它控制组件的下载和Poller属性：AppUpdater的第二个子组件是Poller,Poller控制更新检查。</p>
<p>　　(1)AutoStart:布尔值，在应用程序启动时控制Poller 是否应当开始轮询或它是否应当等待直到有计划的更新查询开始。</p>
<p>　　(2)DownloadOnDetection:布尔值，控制Poller 在一个新的更新发现时是否立即开始下载更新，或者是否通过调用DownloadUdpate() 方法开始显式下载。</p>
<p>　　(3)InitialPollInterval:应用程序启动后在第一次执行更新检查前等待的秒数。</p>
<p>　　(4)PollInterval:第一次更新检查之后，PollInterval 控制后续每次更新检查之间间隔的秒数，注意：默认为每30 秒进<br>行一次检查；显然，你会希望你的应用程序减少更新检查的频率。</p>
<p>　　所有这一切完成之后，你的属性表格看起来应当是下面这个样子：</p>
<p><br>Samples\SampleApp\SampleApp_Complete目录包含应用程序正确安装的一个版本。 </p>
<p>　　安装：</p>
<p>　　(1)DownloadRetryAttempts:在下载期间如果有错误发生（比如Web 服务器宕机）downloader 会稍后重试。这个属性控制downloader 认为是彻底的应用程序更新错误之前重试网络请求的次数。</p>
<p>　　(2)SecondsBeteweenDownloadRety:重试网络请求之前等待的秒数。</p>
<p>　　(3)UpdateRetryAttempts: 在更新期间如果发生严重错误，（例如downloader 超过了试图重试的次数），就会产生一个应用程序更新错误。默认的，将会停止试图更新。但是在应用程序下一次启动时会试图恢复（比如更新Web 服务器可能会宕机好几天）。这个属性控制试图更新的次数。如果超过这个值，updater 就会取消更新，重置它的状态并返回更新检查。</p>
<p>　　(4)ValidateAssemblies: 这个属性控制下载程序集有效完成的级别。更多信息参见这篇文章的安全一节。</p>
<p>　　第四步：生成并在客户端部署应用程序V1版本。</p>
<p>　　在SampleApp项目中，打开AssemblyInfo.cs文件。将AssemblyVersion的值从&#8220;1.0&#8221;修改为&#8220;1.0.0.0&#8221;。这会引起在生成程序集时获得值为&#8220;1.0.0.0&#8221;.. 的标记，该标记代替VS.NET通常指定为递增的值。 </p>
<p>　　1. 生成应用程序。</p>
<p>　　2.从压缩文件中将Samples\SampleApp\SampleApp_ClientSetup目录拷贝到你的本地机器上。要注意此目录已经包含了AppStart.exe。AppStart.config已经设置为指向1.0.0.0目录并且启动SampleApp.exe。</p>
<p>　　从SampleApp的release目录下拷贝SampleApp（Appupdater.dll，SimpleForm.dll和SampleApp.exe）</p>
<p>　　到客户端SampleApp_ClientSetup\1.0.0.0目录下。至此，一个功能完整的应用程序版本已被&#8220;安装&#8221;到了客户端，可以通过运行AppStart.exe来执行。</p>
<p>　　第五步：安装Web服务器</p>
<p>　　在这一步我们将安装Web服务器以提供更新轮询功能。.NET应用程序更新组件使用HTTP-DAV来下载应用程序更新，因此需要一个支持HTTP-DAV的Web服务器。Windows 2000上的IIS5.0和更新的操作系统都支持HTTP-DAV。</p>
<p>　　1.将Samples/SampleApp_ServerSetup目录拷贝到你的Web服务器上的wwwroot目录下。</p>
<p>　　2. 将SampleApp的V1版本拷贝到Web服务器的1.0.0.0文件夹。</p>
<p>　　3. 在Web服务器上为SampleApp_ServerSetup目录启用IIS的&#8220;目录浏览&#8221;权限。</p>
<p>　　第六步：自动更新应用程序</p>
<p>　　OK，.. 现在是时间来通过自动安装一个新版本来看看以上这些艰苦工作的结果了。</p>
<p>　　1. 如果你部署在客户端的SampleApp版本没有运行，加载它让它运行，记得使用AppStart.exe。</p>
<p>　　2. 回到VS.NET并在SampleApp窗体中做一些可以被注意到的修改（比如修改背景色）。</p>
<p>　　3. 将AssemblyInfo.cs的版本信息改为2.0.0.0。</p>
<p>　　4. 重新生成。</p>
<p>　　5. 回到Web服务器并生成一个和1.0.0.0目录同等的目录2.0.0.0。从release生成目录下将新版本应用程序拷贝到Web服务器上新建的2.0.0.0目录下。</p>
<p>　　6.打开UpdateVersion.xml 并修改AvailableVersion为2.0.0.0。修改ApplicationURL为指向新的2.0.0.0路径。</p>
<p>　　7. 保存对UpdateVersion.xml所做的修改。</p>
<p>　　一旦你保存了新的UpdateVersion.xml，在30秒之内，运行中的SampleApp拷贝将会探测到新的可用版本。</p>
<p>　　四、按需安装、安全、可扩展性及调试</p>
<p>　　(一)按需求安装</p>
<p>　　所谓按需求安装，指只有主可执行程序被显式安装到客户机上。应用程序剩下的部分可以根据基本需要自动下载和安装。</p>
<p>　　通过.NET应用程序更新组件的AutoFileLoad属性来启动按需求安装。你必须仔细考虑在你的应用程序中程序集边界所处的位置以及什么动作会引起程序集被下载。由于程序集的下载涉及到网络输入输出，因此下载所花费的时间是可变的。在程序集下载期间，应用程序会被冻结等待程序集下载完成。</p>
<p>　　(二)部署安全</p>
<p>　　自动安装应用程序更新的能力具备很多好处，但它也伴随着一些潜在的危险。当你使得安装更新变得简单时，如果不小心，你也可能使得安装恶意代码变得简单。有两种危险，第一种危险是有人会用自己的Web服务器欺骗用来部署更新的Web服务器。他们可能会利用那台Web服务器在你的应用程序路径安装一个病毒程序。阻止欺骗或其它通过网络进行的不正当干预的最简单的方法是使用HTTPS。要和.NET应用程序更新组件一起使用HTTPS，可以简单地用HTTPS URLs来代替HTTP URLs。当然，HTTPS 不是银弹。使用HTTPS有两个问题，第一是可伸缩性。使用HTTPS需要服务器加密所有从Web服务器上下载的文件。如果一个应用程序的更新文件很大，加密更新文件的代价会使服务器的负担过重。使用HTTPS的另一个问题是它对第二种安全危险毫无益处。第二种危险是黑客既可能从内部也可能从外部来攻击你的服务器。一旦攻击成功，则有可能意味着成百上千的客户端也通过自动更新遭受连累，这种情况将是灾难性的。</p>
<p>　　为解决这个问题，.NET应用程序更新组件使用给.NET程序集添加强名称的特性来验证所下载的程序集。如果.NET应用程序更新组件检测到下载期间一个程序集不是使用你的密钥签名的，下载就会取消。这意味着只有拥有你的应用程序私钥的人才能够建立可自动部署的更新文件。</p>
<p>　　要验证程序集有效，.NET应用程序更新组件验证你当前安装的应用程序可执行程序的公钥和下载的更新的公钥是否匹配。如果两个程序集以相同且保密的私钥签名，那么嵌入的公钥也就相同。因为被CLR加载的程序集为了验证它的公钥，CLR计算它正常的哈希值检查来保证程序集实际上就是真正的程序集而不是被做了手脚的程序集。为了能够在下载时验证，可以给你所有的应用程序集添加强名称并将.NET应用程序更新组件的ValidateAssemblies属性设置为true。</p>
<p>　　在下载时进行程序集验证会起很大的作用，但实际上，应用程序会经常有不同私钥签名的组件。比如，你的应用程序可能有两个文件：使用你的私钥签名的可执行程序集和另一个包含你购买的应用在你的应用程序中的第三方图表控件的dll程序集。第三方程序集可能使<br>用第三方而不是你自己的私钥来签名。使情况变得更为复杂的是，在你的应用程序中用来签名程序集的有效私钥的设置随着版本号的改变可能会发生变化。你该如何自动更新那些应用的类型？为解决这个问题，你可以在你的应用程序中生成一个包含有效公钥的列表的程序集。将该程序集使用应用程序的主私钥（应用程序的exe文件签名的密钥）签名并把该程序集放到Web服务器上和应用程序更新文件一起的目录下。在更新下载过程开始之前，.NET应用程序更新组件将会检查Web服务器上应用程序更新目录下一个名为"AppUpdaterKeys.dll"的的程序集。如果存在，该程序集就会被下载。该程序集会被拿来和主应用程序的公钥对比验证。如果签名有效，密钥列表会被提取出来。从此之后，任何处于该列表中的密钥会被认为是更新文件的有效签名。</p>
<p>　　有关安全方面推荐的方法是使用HTTPS URLs来实现更新检查。这会提供第一级别的欺骗保护。对于更新下载，最好不要使用HTTPS RLs以避免使你Web服务器的负荷过重。而是给你的应用程序的程序集添加强名称并使用程序集验证特性。</p>
<p>　　(三)可扩展性</p>
<p>　　在这篇文章前面讲过的示例中我们简单地通过拖放一个组件到应用程序中并设置一些属性来实现自动部署。</p>
<p>　　虽然这在许多应用程序中工作的很好，但在一些应用程序中会需要高级别的控制，这只能通过写代码来获得。我们可以编写自己的代码来替换掉.NET应用程序更新组件标准过程，使用重写的CheckForUpdate()和ApplyUpdate()方法来定制检查和更新行为。</p>
<p>　　(四)调试</p>
<p>　　这一节将指出一些首选的调试选项，以及描述使用该组件的用户大多数常见的问题。</p>
<p>　　.NET应用程序更新器在和AppStart.exe相同的目录下生成一个名为AppUpdate.log的隐藏日志文件。</p>
<p>　　所有的更新成功和失败信息都记录在该日志中。当有一个特殊的客户端不能成功更新时日志文件会特别有用。</p>
<p>　　你可以使用日志来判断在什么时间以及是如何更新失败的。另外，.NET应用程序更新组件使用.NET框架的Debug类来输出大量有用的信息。如果你在调试器中运行你的应用程序，你会在输出窗口中看到这些信息。你可以循着.NET应用程序更新器的记录重点观察并找到出<br>问题的地方。</p>
<p>　　如果由于某种原因，你无法使得.NET应用程序更新器工作，在你深入调试之前请确定以下几点，你遇到的问题很可能就是如下之一：.. </p>
<p>　　. 你是否将IIS目录浏览给打开了？如果没有，更新器将不会下载安装任何文件。</p>
<p>　　. 你是否正确的部署了一切并正确设置了URL？</p>
<p>　　. 如果你的应用程序安装在program files目录下，确定你是该机的超级管理员或超级用户吗？如果不是，你将不会有写权限来更新应用程序。</p>
<p>　　. 你是在应用程序的主用户界面线程中生成AppUpdater对象的吗？如果不是，更新器将不能显示用户界面并且在激发事件回到用户界面时失败。</p>
<p>　　. 是否更新成功，但应用程序使用新的更新自动重启时失败？.NET 应用程序更新组件试图通过调用Application.Exit方法来退出应用程序。然而，该方法并不能保证关闭一个应用程序。如果你生成并遗留了单独的线程在运行，该方法就无法关闭进程。保证所有线程终止的解决的方案是通过调用Application.OnExit事件，或者挂钩.NET应用程序更新器的OnUpdateComplete事件并自己处理关闭。</p>
<p>　　五、总结</p>
<p>　　客户端应用程序部署方便是.NET框架第一个版本的重要的目标。用.NET框架建立解决部署问题的客户端应用程序是一种很好的技术。部署方便仍然是未来.NET框架新版本的一个重要目标。就方案而言，这里描述的.NET应用程序更新组件代表了我们的一些想法，在未来版本的.NET框架中我们将可以直接使用。然而，在那个时候到来之前的这段期间，.NET应用程序更新组件不失为开始建立自动更新应用程序的一种重要的方法。<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28620.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:15 <a href="http://www.cnitblog.com/MartinYao/articles/28620.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用命名管道在程序間通信</title><link>http://www.cnitblog.com/MartinYao/articles/28619.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:14:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28619.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28619.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28619.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28619.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28619.html</trackback:ping><description><![CDATA[<p>　　你曾经需要在同一台机器的两个.NET应用程序间进行数据交换吗？例如，一个Web站点和一个Windows服务？.NET框架提供了几种好的选择来完成进程间通信（IPC）：Web Service，Remoting。最快的是Remoting，因为它使用TCP通道和二进制格式。 </p>
<p>　　然而，如果需要频繁地从一个应用程序调用另外一个应用程序，并且你主要关心的是性能，Remoting还是显得慢了一点。让Remoting变慢的，不是协议，而是序列化。</p>
<p>　　通常来说，Remoting是很不错的，但如果仅限于本地机器的两个进程间相互通信，其处理机制增加了不必要的开销。所以要考虑一些别的选择，比较好的是命名管道（Named Pipes），不会进行二进制序列化，所以提供了更快的IPC。</p>
<p>　　要记住，这个解决方案最有效的使用是在一个应用程序需要和另一个应用程序进行非常频繁的、短文本的消息通信的情况下，并且是在同一台机器或在同一局域网内部。对于结构化的数据交换，这些文本消息也可以是XML文档或序列化的.NET对象。通信时没有安全层，因为命名管道最多只能在局域网中运行，所以假定安全问题由别的层进行处理。</p>
<p>　　一、实现命名管道</p>
<p>　　以下是.NET命名管道解决方案中几个主要的类。</p>
<p>　　. NamedPipeNative：这个类和kernal32.dll联系实现命名管道的通信，其中包含一些常用方法和常量。<br>　<br>　　. NamedPipeWrapper ：这个类是NamedPipeNative的一个包装。</p>
<p>　　. ApipeConnection：这是一个抽象类，定义了命名管道连接、读、写数据的方法。这个类是从ClientPipeConnection 和ServerPipeConnection 继承的，分别在客户端和服务器端应用程序中使用。</p>
<p>　　. ClientPipeConnection：被客户端应用程序使用，使用命名管道和服务器通信。</p>
<p>　　. ServerPipeConnection：允许命名管道服务器创建连接，和客户端进行通信。</p>
<p>　　. PipeHandle：保存操作系统的本地句柄，以及管道连接的当前状态。</p>
<p>　　了解上述的类之后，需要了解一下命名管道的操作。</p>
<p>　　二、创建一个服务器端命名管道</p>
<p>　　服务器端管道名的语法是：<a href="file://./pipe/PipeName">\\.\pipe\PipeName</a>。&#8220;PipeName&#8221;.. 部分是管道的具体名字。要连接管道，客户端应用程序需要创建一个同样名称的客户端命名管道。如果客户端在不同的机器上，服务器端管道的名称应该是<a href="file://SERVER/pipe/PipeName">\\SERVER\pipe\PipeName</a>。下面的代码是NamedPipeWrapper的一个静态方法，被用来实例化一个服务器端命名管道。</p>
<p>public static PipeHandle Create(string name,uintoutBuffer, uintinBuffer){<br>　name = @"\.\pipe\" + name;<br>　PipeHandle handle = new PipeHandle();</p>
<p>　for(inti=1;i&lt;=ATTEMPTS;i++){ <br>　　handle.State=InterProcessConnectionState.Creating; <br>　　handle.Handle = NamedPipeNative.CreateNamedPipe( name, <br>　　　NamedPipeNative.PIPE_ACCESS_DUPLEX, <br>　　　NamedPipeNative.PIPE_TYPE_MESSAGE | <br>　　　NamedPipeNative.PIPE_READMODE_MESSAGE | <br>　　　NamedPipeNative.PIPE_WAIT, <br>　　　NamedPipeNative.PIPE_UNLIMITED_INSTANCES, <br>　　　outBuffer, <br>　　　inBuffer, <br>　　　NamedPipeNative.NMPWAIT_WAIT_FOREVER,<br>　　　IntPtr.Zero);<br>　　if(handle.Handle.ToInt32()!=NamedPipeNative.INVALID_HANDLE_VALUE){ <br>　　　handle.State=InterProcessConnectionState.Created; <br>　　　break; <br>　} </p>
<p>　if (i &gt;= ATTEMPTS) { <br>　　handle.State = InterProcessConnectionState.Error; <br>　　throw new NamedPipeIOException("Error creating named <br>pipe"+name+".Internalerror:"+NamedPipeNative.GetLastError().ToString(),NamedPipeNative.GetLastError()); <br>　} <br>} <br>returnhandle; <br>}&nbsp; </p>
<p>　　通过调用NamedPipeNative.CreateNamedPipe方法，上面的方法创建了一个双方互通的命名管道，并且指定管道可以有无限制的实例。常量的名称都是英语，不难看懂，就不一一解释了。</p>
<p>　　假定服务器端命名管道创建成功，它就可以开始监听客户端连接了。</p>
<p>　　三、连接到客户端管道</p>
<p>　　命名管道服务器需要设置成监听状态，以使客户端管道能够连接它。这可以由调用NamedPipeNative.ConnectNamedPipe方法完成。</p>
<p>　　调用NamedPipeNative.CreateFile方法，就可以创建一个命名管道客户端，并且连接到一个监听的服务器管道。下面的代码是NamedPipeWrapper.ConnectToPipe的一部分，可以阐释这一点。</p>
<p>public static PipeHandle ConnectToPipe(string pipeName, string serverName) { <br>　PipeHandle handle = new PipeHandle(); <br>　//Buildthename ofthe pipe. <br>　string name = @"\" + serverName + @"\pipe\" + pipeName; <br>　for(inti=1;i&lt;=ATTEMPTS;i++){ <br>　　handle.State = InterProcessConnectionState.ConnectingToServer; <br>　　// Try to connect to the server <br>　　handle.Handle = NamedPipeNative.CreateFile(name, NamedPipeNative.GENERIC_READ | NamedPipeNative. <br>GENERIC_WRITE, 0,null,NamedPipeNative.OPEN_EXISTING,0,0); </p>
<p>　　在创建一个PipeHandle对象并建立管道名称后，我们调用NamedPipeNative.CreateFile方法来创建一个客户端命名管道，并连接到指定的服务器端管道。在我们的例子中，客户端管道被配置为可读可写的。</p>
<p>　　如果客户端管道被成功创建，NamedPipeNative.CreateFile方法返回其对应的本地句柄，这在以后的操作中会用到。如果由于某种原因创建失败，方法会返回1， 并把NamedPipeNative设为INVALID_HANDLE_VALUE常量。</p>
<p>　　在客户端命名管道可以用来读和写之前，还要做一件事情。我们需要把handle 设为PIPE_READMODE_MESSAGE。可以调用NamedPipeNative.SetNamed-PipeHandleState 实现。</p>
<p>if (handle.Handle.ToInt32() != NamedPipeNative.INVALID_HANDLE_VALUE){ <br>　// The client managed to connect to the server pipe <br>　handle.State = InterProcessConnectionState. </p>
<p>　ConnectedToServer;<br>　// Set the read mode of the pipe channel<br>　uint mode = NamedPipeNative.PIPE_READMODE_MESSAGE;</p>
<p>　if(NamedPipeNative.SetNamedPipeHandleState(handle.Handle,refmode,IntPtr.Zero,IntPtr.Zero)){ <br>　　break; <br>　}&nbsp; </p>
<p>　　每个客户端管道和一个服务器管道的实例通信。若服务器端的实例达到最大数目，创建客户端管道会失败。</p>
<p>　　四、读写数据</p>
<p>　　从命名管道读数据时我们不能提前知道消息的长度。我们的解决方案不需要处理很长的消息，所以使用System.Int32变量来指定消息的长度。</p>
<p>　　NamedPipeWrapper.WriteBytes 方法可以将消息写到一个命名管道，消息按UTF8编码，然后按字节数组传递。</p>
<p>public static void WriteBytes(PipeHandle handle, byte[]bytes) {<br>　byte[] numReadWritten = new byte[4];<br>　uint len;</p>
<p>　if(bytes==null){<br>　　bytes=newbyte[0];<br>　}<br>　if (bytes.Length == 0) {<br>　　bytes = new byte[1]; <br>　　bytes = System.Text.Encoding.UTF8.GetBytes(" "); <br>　}<br>　// 获取消息的长度：<br>　len= (uint)bytes.Length;</p>
<p>　handle.State = InterProcessConnectionState.Writing;<br>　// 获取消息长度的字节表示，先写这四字节<br>　if(NamedPipeNative.WriteFile(handle.Handle,BitConverter.GetBytes(len),4,numReadWritten,0)){<br>　　// 写余下的消息<br>　　if(!NamedPipeNative.WriteFile(handle.Handle,bytes,len,numReadWritten,0)){ <br>　　　handle.State=InterProcessConnectionState.Error; <br>　　　thrownewNamedPipeIOException("Errorwritingtopipe. Internalerror:"+NamedPipeNative.GetLastError().ToString(), NamedPipeNative.GetLastError()); <br>　　} <br>　} <br>　else{<br>　　handle.State=InterProcessConnectionState.Error;<br>　　thrownewNamedPipeIOException("Errorwritingtopipe.Internalerror:"+NamedPipeNative.GetLastError().ToString(), <br>NamedPipeNative.GetLastError());<br>　}</p>
<p>　handle.State =InterProcessConnectionState.Flushing; <br>　// 激活管道，保证任何缓存数据都被写入管道，不会丢失：<br>　Flush(handle); <br>　handle.State = InterProcessConnectionState.FlushedData; <br>}&nbsp; </p>
<p>　　要从一个命名管道读数据，先要把前四个字节转化为整数以确定消息的长度。接着，就可以读余下的数据了，请看下面的NamedPipeWrapper.ReadBytes方法。</p>
<p>public static byte[] ReadBytes(PipeHandle handle, int maxBytes) {<br>　byte[]numReadWritten=newbyte[4];<br>　byte[]intBytes=newbyte[4];<br>　byte[]msgBytes=null;<br>　intlen;</p>
<p>　handle.State=InterProcessConnectionState.Reading;<br>　handle.State=InterProcessConnectionState.Flushing;<br>　// 读前四个字节并转化为整数：<br>　if(NamedPipeNative.ReadFile(handle.Handle, intBytes,4, numReadWritten, 0)) { <br>　　len=BitConverter.ToInt32(intBytes,0); <br>　　msgBytes=newbyte[len]; <br>　　handle.State=InterProcessConnectionState.Flushing; <br>　　// 读余下的数据或抛出异常：<br>　　if(!NamedPipeNative.ReadFile(handle.Handle,msgBytes,(uint) len,numReadWritten,0)){ <br>　　　handle.State=InterProcessConnectionState.Error; <br>　　　thrownewNamedPipeIOException("Error readingfrompipe. Internalerror:"+NamedPipeNative.GetLastError().ToString(), NamedPipeNative.GetLastError()); <br>　　}<br>　}<br>　else {<br>　　handle.State=InterProcessConnectionState.Error; <br>　　thrownewNamedPipeIOException("Errorreadingfrompipe. Internalerror:"+NamedPipeNative.GetLastError().ToString(), NamedPipeNative.GetLastError()); <br>　}<br>　handle.State=InterProcessConnectionState.ReadData;<br>　if(len&gt;maxBytes){<br>　　returnnull; }<br>　returnmsgBytes;<br>} </p>
<p>　　以上就是命名管道的实现和一些主要的方法，下面介绍如何创建进行文本消息通信的命名管道服务器和客户端应用程序。</p>
<p>　　五、创建命名管道服务器</p>
<p>　　命名管道服务器是一个多线程的引擎，用来为并发的请求服务，创建新的线程和管道连接。</p>
<p>　　AppModule.NamedPipes assembly包含了一个基类ApipeConnection，是对普通命名管道操作的封装，例如创建管道、读写数据等等，这是一个抽象类。</p>
<p>　　另外，有两个从ApipeConnection继承的管道连接类ClientPipeConnection 和 ServerPipeConnection。它们重载了一些方法（例如连接和关闭）并为服务器和客户端命名管道分别提供实现。ClientPipeConnection 和ServerPipeConnection都有调用Dispose方法的析构器，<br>清除非管控的资源。</p>
<p>　　命名管道服务器负责创建命名管道，处理客户端连接。有两个主要的类提供了服务功能： ServerNamedPipe和PipeManager。</p>
<p>　　（1）ServerNamedPipe类</p>
<p>　　其构造器如下：.. </p>
<p>internal ServerNamedPipe(stringname, uint outBuffer,uintinBuffer,intmaxReadBytes){ <br>　PipeConnection=newServerPipeConnection(name,outBuffer,inBuffer,maxReadBytes);<br>　PipeThread=newThread(newThreadStart(PipeListener));<br>　PipeThread.IsBackground=true;</p>
<p>　PipeThread.Name ="PipeThread "+this.PipeConnection.NativeHandle.ToString();<br>　LastAction=DateTime.Now;<br>} </p>
<p>　　构造器创建了一个新的ServerPipeConnection实例，并调用PipeListener方法。随后的主要部分是循环监听客户端连接，以及读写数据。</p>
<p>private void PipeListener() {<br>　CheckIfDisposed();</p>
<p>　try{<br>　　Listen=Form1.PipeManager.Listen;<br>　　Form1.ActivityRef.AppendText("Pipe"+this.PipeConnection.NativeHandle.ToString() + ": new pipe started" + Environment.NewLine); <br>　　while(Listen){ <br>　　　LastAction=DateTime.Now; <br>　　　// 从客户端管道读取数据： <br>　　　stringrequest=PipeConnection.Read(); <br>　　　LastAction=DateTime.Now; <br>　　　if(request.Trim()!=""){ <br>　　　　//PipeManager.HandleRequest 方法接受客户端请求处理之，<br>　　　　// 然后进行响应,这个响应接着就被写入管道。<br>　　　　PipeConnection.Write(Form1.PipeManager.HandleRequest(request)); <br>　　　　Form1.ActivityRef.AppendText("Pipe"+this.PipeConnection.NativeHandle.ToString()+ ":requesthandled"+Environment.NewLine); <br>　　　} <br>　　　else{ PipeConnection.Write("Error:badrequest");} <br>　　　LastAction=DateTime.Now; <br>　　　// 从客户端管道断开连接<br>　　　PipeConnection.Disconnect(); <br>　　　if(Listen){ <br>　　　　Form1.ActivityRef.AppendText("Pipe"+this.PipeConnection. NativeHandle.ToString()+":listening"+Environment.NewLine); </p>
<p>　　　　// 开始监听一个新的连接： </p>
<p>　　　　Connect(); }<br>　　　Form1.PipeManager.WakeUp();<br>　　}<br>　}<br>　catch(System.Threading.ThreadAbortExceptionex){}<br>　catch(System.Threading.ThreadStateExceptionex){}<br>　catch(Exceptionex){<br>　　//Logexception<br>　}<br>　finally{<br>　　this.Close();}<br>　} </p>
<p>　　请注意不要关闭服务器管道，因为创建一个服务器管道是一个相对昂贵的操作，会引起比较昂贵的开销。</p>
<p>　　（2）PipeManager 类</p>
<p>　　PipeManager 类负责在必要的时候创建服务器管道，管理线程，并生成客户端请求的响应。下面代码中Initialize方法调用Start方法创建一个新的线程：</p>
<p>public void Initialize() {<br>　Pipes=Hashtable.Synchronized(_pipes);</p>
<p>　Mre =newManualResetEvent(false);<br>　MainThread =newThread(newThreadStart(Start));<br>　MainThread.IsBackground=true;<br>　MainThread.Name = "MainPipeThread";</p>
<p>　MainThread.Start();<br>　Thread.Sleep(1000);<br>} </p>
<p>　　PipeManager类只在获得请求的时候才创建新的管道连接和线程。这意味着ServerPipeConnection对象只在没有连接存在或所有连接都忙于响应请求的时候才被创建。通常2-3个命名管道实例就能处理很大负载的并发客户端请求，但这个主要取决于处理客户端请求和生<br>成响应的时间。</p>
<p>　　创建ServerPipeConnection对象的引用被保存在管道哈希表中。</p>
<p>private void Start() {<br>　try{</p>
<p>　　while(_listen){<br>　　　int[]keys=newint[Pipes.Keys.Count];<br>　　　Pipes.Keys.CopyTo(keys,0);</p>
<p>　　　// 循环检验ServerPipeConnection 对象是否还是可用： <br>　　　foreach(intkeyinkeys){ <br>　　　　ServerNamedPipeserverPipe=(ServerNamedPipe)Pipes[key]; <br>　　　　if(serverPipe!=null&amp;&amp; DateTime.Now.Subtract(serverPipe.LastAction).Milliseconds&gt; <br>PIPE_MAX_STUFFED_TIME &amp;&amp; serverPipe.PipeConnection.GetState()!=InterProcessConnectionState.WaitingForClient){<br>　　　　　serverPipe.Listen=false;<br>　　　　　serverPipe.PipeThread.Abort();<br>　　　　　RemoveServerChannel(serverPipe.PipeConnection.NativeHandle); <br>　　　　} <br>　　　} <br>　　　//NumberPipes 字段包含了可以在服务器上拥有的命名管道最大数目<br>　　　if(numChannels&lt;=NumberPipes){ <br>　　　　ServerNamedPipe pipe = new ServerNamedPipe(PipeName,OutBuffer,InBuffer,MAX_READ_BYTES); <br>　　　　try{ <br>　　　　　//Connect 方法将新生成的管道置为监听模式。<br>　　　　　pipe.Connect(); <br>　　　　　pipe.LastAction=DateTime.Now; <br>　　　　　System.Threading.Interlocked.Increment(refnumChannels); <br>　　　　　// 开始ServerPipeConnection 线程<br>　　　　　pipe.Start(); <br>　　　　　Pipes.Add(pipe.PipeConnection.NativeHandle,pipe); <br>　　　　} <br>　　　　catch (InterProcessIOException ex) { <br>　　　　　RemoveServerChannel(pipe.PipeConnection.NativeHandle); <br>　　　　　pipe.Dispose(); <br>　　　　} <br>　　　} <br>　　　else{ Mre.Reset(); Mre.WaitOne(1000,false); } <br>　　}<br>　}<br>　catch { //Logexception }<br>} </p>
<p>　　六、创建客户端管道连接</p>
<p>　　要使用命名管道把一个客户端应用程序连接到服务器，我们必须创建ClientPipeConnection类的一个实例，使用它的方法来读写数据。</p>
<p>IInterProcessConnectionclientConnection=null; </p>
<p>try{<br>　clientConnection=newClientPipeConnection("MyPipe",".");<br>　clientConnection.Connect();<br>　clientConnection.Write(textBox1.Text);<br>　clientConnection.Close();<br>}<br>catch{<br>　clientConnection.Dispose();<br>} </p>
<p>　　管道名称&#8220;MyPipe&#8221; 必须和服务器管道的名称一样，如果命名管道服务器也在同一台机器上，ClientPipeConnection构造器的第二个参数应该是&#8220;.&#8221;。如果不在同一台机器上，第二个参数就是服务器的网络名称。</p>
<p>　　以上，我介绍了命名管道的解决方案，我再重申一下，命名管道最有效的使用是在一个应用程序需要和另一个应用程序进行非常频繁的，短文本的消息通信的情况下，并且是在同一台机器或在局域网内部。如果您遇到了这样的情况，希望我的这些代码能给你启发和参考。<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28619.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:14 <a href="http://www.cnitblog.com/MartinYao/articles/28619.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>隻運行一個實例</title><link>http://www.cnitblog.com/MartinYao/articles/28618.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:13:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28618.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28618.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28618.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28618.html</trackback:ping><description><![CDATA[<p>using System;<br>using System.Threading;<br>using System.Windows.Forms;<br>using System.Runtime.InteropServices;<br>namespace LoGISticsSystem<br>{<br>&nbsp;/// &lt;summary&gt;<br>&nbsp;/// 使应用程序只能运行一个实例 的摘要说明。<br>&nbsp;/// &lt;/summary&gt;<br>&nbsp;public class AppSingleton<br>&nbsp;{<br>&nbsp; static Mutex m_Mutex;</p>
<p>&nbsp; public static void Run()<br>&nbsp; {<br>&nbsp;&nbsp; if(IsFirstInstance())<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; Application.ApplicationExit += new EventHandler(OnExit);<br>&nbsp;&nbsp;&nbsp; Application.Run();<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; <br>&nbsp; }<br>&nbsp; public static void Run(ApplicationContext context)<br>&nbsp; {<br>&nbsp;&nbsp; if(IsFirstInstance())<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; Application.ApplicationExit += new EventHandler(OnExit);<br>&nbsp;&nbsp;&nbsp; Application.Run(context);<br>&nbsp;&nbsp; }<br>&nbsp; }<br>&nbsp; public static void Run(Form mainForm)<br>&nbsp; {<br>&nbsp;&nbsp; if(IsFirstInstance())<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; Application.ApplicationExit += new EventHandler(OnExit);<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; Application.Run(mainForm);</p>
<p>&nbsp;&nbsp; <br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; else<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; MessageBox.Show("应用程序已启动,请在任务管理中关闭后再启动！","系统提示",MessageBoxButtons.OK,MessageBoxIcon.Warning);<br>&nbsp;&nbsp; }<br>&nbsp; }<br>&nbsp; static bool IsFirstInstance()<br>&nbsp; {<br>&nbsp;&nbsp; m_Mutex = new Mutex(false,"SingletonApp Mutext");<br>&nbsp;&nbsp; bool owned = false;<br>&nbsp;&nbsp; owned = m_Mutex.WaitOne(TimeSpan.Zero,false);<br>&nbsp;&nbsp; return owned ;<br>&nbsp; }<br>&nbsp; static void OnExit(object sender,EventArgs args)<br>&nbsp; {<br>&nbsp;&nbsp; m_Mutex.ReleaseMutex();<br>&nbsp;&nbsp; m_Mutex.Close();<br>&nbsp; }<br>&nbsp;}<br>}</p>
<p>具体使用是如下</p>
<p>using System;<br>using System.Drawing;<br>using System.Windows.Forms;<br>namespace LogisticsSystem<br>{<br>&nbsp;/// &lt;summary&gt;<br>&nbsp;/// MainClass 的摘要说明。<br>&nbsp;/// &lt;/summary&gt;<br>&nbsp;public class MainClass<br>&nbsp;{<br>&nbsp; /// &lt;summary&gt;<br>&nbsp; /// 应用程序的主入口点。<br>&nbsp; /// &lt;/summary&gt;<br>&nbsp; [STAThread]<br>&nbsp; static void Main() <br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; AppSingleton.Run(new mainForm());<br>&nbsp;&nbsp; //Application.Run(new mainForm());<br>&nbsp; }<br>&nbsp;}<br>}</p>
<p><br>另一方法<br>/////////////<br>System.Threading.Mutex appSingleton = new System.Threading.Mutex(false, "MyProgInstance_PPP"); <br>&nbsp;&nbsp;&nbsp; if(appSingleton.WaitOne(0, false)) <br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Application.Run(new FormMain(););<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; else<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show("程序已经运行");<br>&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:13 <a href="http://www.cnitblog.com/MartinYao/articles/28618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C#下的查杀进程</title><link>http://www.cnitblog.com/MartinYao/articles/28616.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:12:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28616.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28616.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28616.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28616.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28616.html</trackback:ping><description><![CDATA[<p><br>.net对进程管理的封装非常好，通过System.Diagnostics.Process.GetProcesses()方法可以获得系统内各进程的数组，通过枚举可获得系统内的所有进程（比Windows自带的进程浏览器看到的还多）。 <br>ProcessName属性获得的是文件名的前缀，即NotePad，不包含后面的.exe。</p>
<p><br>要获得更多的信息，需要取得Modules属性数组，但并不是每个进程都能获取Modules属性，所以必须要使用try..catch语句排除异常。该数组的第一个成员[ 0 ].FileName包含了执行程序的完整文件名，等同于ProcessModule；序号大于0的成员往往包含该程序调用的dll信息，得到的结果很有点像反木马程序。</p>
<p>Kill()方法用来杀进程，被杀的程序不会弹出类似保存文件的对话框，是个十足的冷血的快刀手。</p>
<p>StartInfo属性是用于启动新进程的，不适用于检索已运行的进程。</p>
<p>private void btnProcess_Click(object sender, System.EventArgs e)<br>{<br>System.Diagnostics.Process []myPs ;<br>myPs=System.Diagnostics.Process.GetProcesses();<br>this.richTextBox1.Clear();<br>foreach (System.Diagnostics.Process p in myPs)<br>{<br>if (p.Id!=0 )<br>{<br>string myS="进程名称" p.ProcessName " ID:" p.Id.ToString();<br>try//由于进程不同，有的进程不包含Modules信息，所以要用try保护<br>{<br>if (p.Modules !=null)<br>if (p.Modules.Count&gt;0)<br>{ <br>System.Diagnostics.ProcessModule pm=p.Modules[0]; </p>
<p>myS ="\n Modules[0].FileName:" pm.FileName; <br>myS ="\n Modules[0].ModuleName:" pm.ModuleName;<br>myS ="\n Modules[0].FileVersionInfo:\n" pm.FileVersionInfo.ToString();<br>if (pm.FileName.ToLower()==this.textBox1.Text.Trim().ToLower())<br>p.Kill(); <br>}<br>}<br>catch <br>{}<br>finally<br>{<br>this.richTextBox1.Text = myS "\n==========================\n";<br>}<br>}<br>}</p>
<p><br>程序的执行结果片段：</p>
<p>进程名称SearchNet ID:3092<br>Modules[0].FileName:C:\Program Files\SearchNet\SearchNet.exe<br>Modules[0].ModuleName:SearchNet.exe<br>Modules[0].FileVersionInfo:<br>File: C:\Program Files\SearchNet\SearchNet.exe<br>InternalName: SearchNet<br>OriginalFilename: <br>FileVersion: 1, 0, 2, 4<br>FileDescription: IE地址栏搜索程序<br>Product: 地址栏搜索<br>ProductVersion: 1, 0, 2, 4<br>Debug: False<br>Patched: False<br>PreRelease: False<br>PrivateBuild: False<br>SpecialBuild: False<br>Language 中文(中国)</p>
<p>==========================<br>进程名称svchost ID:1700<br>Modules[0].FileName:C:\WINDOWS\system32\svchost.exe<br>Modules[0].ModuleName:svchost.exe<br>Modules[0].FileVersionInfo:<br>File: C:\WINDOWS\system32\svchost.exe<br>InternalName: svchost.exe<br>OriginalFilename: svchost.exe<br>FileVersion: 5.1.2600.2180 (xpsp_sp2_rtm.040803-2158)<br>FileDescription: Generic Host Process for Win32 Services<br>Product: Microsoft&#174; Windows&#174; Operating System<br>ProductVersion: 5.1.2600.2180<br>Debug: False<br>Patched: False<br>PreRelease: False<br>PrivateBuild: False<br>SpecialBuild: False<br>Language 英语(美国)<br></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28616.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:12 <a href="http://www.cnitblog.com/MartinYao/articles/28616.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>調用DLL中窗體</title><link>http://www.cnitblog.com/MartinYao/articles/28617.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Sun, 17 Jun 2007 05:12:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28617.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28617.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28617.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28617.html</trackback:ping><description><![CDATA[<p>　昨天晚上花了一晚上时间写了一个从程序集中动态加载窗体的程序．将任何包含窗体的代码编译成 dll 文件，再把 dll 文件拷贝到本程序的目录下，本程序运行时即可动态检查到 dll 文件中的窗体，将窗体类的类型在程序菜单中显示出来，点击菜单即可运行对应的窗体．</p>
<p>　　本程序主要用到了 Assembly 类动态加载程序集，再得到程序集中包含类的 Type 类型，动态生成类实例，动态调用类方法．个人觉得这是一种提供高度松耦合，可随意扩展的程序结构框架，希望和大家探讨一下这种框架的应用前景！</p>
<p>　　关键性代码如下：<br>&nbsp;using&nbsp;System;<br>&nbsp;using&nbsp;System.Drawing;<br>&nbsp;using&nbsp;System.IO;<br>&nbsp;using&nbsp;System.Reflection;<br>&nbsp;using&nbsp;System.Collections;<br>&nbsp;using&nbsp;System.ComponentModel;<br>&nbsp;using&nbsp;System.Windows.Forms;<br>&nbsp;using&nbsp;System.Data;<br>&nbsp;<br>&nbsp;namespace&nbsp;WindowsFormTest<br>&nbsp;&nbsp; {<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;Form1&nbsp;的摘要说明。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;class&nbsp;FrmMain&nbsp;:&nbsp;System.Windows.Forms.Form<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;formNum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;ArrayList&nbsp;formTypes&nbsp;=&nbsp;new&nbsp;ArrayList();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;ArrayList&nbsp;formObjects&nbsp;=&nbsp;new&nbsp;ArrayList();<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MainMenu&nbsp;mnuMain;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmRun;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItemWindow;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmHelp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmCascade;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmTileHorizontal;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmTileVertical;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;menuItem1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.MenuItem&nbsp;mnuItmAbout;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.StatusBar&nbsp;staBarMain;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.StatusBarPanel&nbsp;pnlInfo;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.Windows.Forms.StatusBarPanel&nbsp;pnlNum;<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;必需的设计器变量。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;System.ComponentModel.Container&nbsp;components&nbsp;=&nbsp;null;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;FrmMain()<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Windows&nbsp;窗体设计器支持所必需的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InitializeComponent();<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;TODO:&nbsp;在&nbsp;InitializeComponent&nbsp;调用后添加任何构造函数代码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;清理所有正在使用的资源。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&lt;/summary&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;override&nbsp;void&nbsp;Dispose(&nbsp;bool&nbsp;disposing&nbsp;)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;disposing&nbsp;)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(components&nbsp;!=&nbsp;null)&nbsp;<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;components.Dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base.Dispose(&nbsp;disposing&nbsp;);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows&nbsp;窗体设计器生成的代码&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;FrmMain_Load(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assembly&nbsp;assembly&nbsp;=&nbsp;null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;windowsPath&nbsp;=&nbsp;Path.Combine(Application.StartupPath,&nbsp;"Windows");<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(string&nbsp;dllFile&nbsp;in&nbsp;Directory.GetFiles(windowsPath,&nbsp;"*.dll"))<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assembly&nbsp;=&nbsp;Assembly.LoadFile(dllFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Type[]&nbsp;types&nbsp;=&nbsp;assembly.GetTypes();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(Type&nbsp;t&nbsp;in&nbsp;types)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(t.BaseType&nbsp;==&nbsp;typeof(Form))<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.formTypes.Add(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MenuItem&nbsp;item&nbsp;=&nbsp;this.mnuItmRun.MenuItems.Add(t.FullName);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.Click&nbsp;+=&nbsp;new&nbsp;EventHandler(menuItemNewItem_Click);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;menuItemNewItem_Click(object&nbsp;sender,&nbsp;EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MenuItem&nbsp;item&nbsp;=&nbsp;(MenuItem)sender;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Type&nbsp;t&nbsp;=&nbsp;(Type)(this.formTypes[item.Index]);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;obj&nbsp;=&nbsp;Activator.CreateInstance(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.formObjects.Add(obj);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formNum&nbsp;+=&nbsp;1;<br>&nbsp;<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t.InvokeMember("MdiParent",&nbsp;BindingFlags.SetProperty,&nbsp;null,&nbsp;obj,&nbsp;new&nbsp;object[]&nbsp; {this});<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t.InvokeMember("Text",&nbsp;BindingFlags.SetProperty,&nbsp;null,&nbsp;obj,&nbsp;new&nbsp;object[]&nbsp; {t.FullName+"&nbsp;&nbsp;窗体："+formNum});<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t.InvokeMember&nbsp;("Show",&nbsp;BindingFlags.InvokeMethod,&nbsp;null,&nbsp;obj,&nbsp;new&nbsp;object&nbsp;[]&nbsp; {});<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((Form)obj).Closing&nbsp;+=&nbsp;new&nbsp;CancelEventHandler(FrmWindow_Closing);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((Form)obj).Activated&nbsp;+=&nbsp;new&nbsp;EventHandler(FrmWindow_Activated);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MenuItem&nbsp;menuItem&nbsp;=&nbsp;this.mnuItemWindow.MenuItems.Add(((Form)obj).Text);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menuItem.Click&nbsp;+=&nbsp;new&nbsp;EventHandler(menuItemWindow_Click);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlNum.Text&nbsp;=&nbsp;"当前装载了"+this.formObjects.Count+"个窗体";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlInfo.Text&nbsp;=&nbsp;"当前活动窗体："+this.ActiveMdiChild.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;menuItemWindow_Click(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MenuItem&nbsp;item&nbsp;=&nbsp;(MenuItem)sender;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((Form)(this.formObjects[item.Index-4])).Activate();<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlNum.Text&nbsp;=&nbsp;"当前装载了"+this.formObjects.Count+"个窗体";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlInfo.Text&nbsp;=&nbsp;"当前活动窗体："+this.ActiveMdiChild.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;FrmWindow_Activated(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlNum.Text&nbsp;=&nbsp;"当前装载了"+this.formObjects.Count+"个窗体";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlInfo.Text&nbsp;=&nbsp;"当前活动窗体："+this.ActiveMdiChild.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;FrmWindow_Closing(object&nbsp;sender,&nbsp;System.ComponentModel.CancelEventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&lt;this.formObjects.Count;&nbsp;i++)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(((Form)this.formObjects[i]).Text&nbsp;==&nbsp;((Form)sender).Text)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.formObjects.RemoveAt(i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.mnuItemWindow.MenuItems.RemoveAt(i+4);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlNum.Text&nbsp;=&nbsp;"当前装载了"+this.formObjects.Count+"个窗体";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.pnlInfo.Text&nbsp;=&nbsp;"当前活动窗体："+this.ActiveMdiChild.Text;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;mnuItmCascade_Click(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.LayoutMdi(MdiLayout.Cascade);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;mnuItmTileHorizontal_Click(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.LayoutMdi(MdiLayout.TileHorizontal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;mnuItmTileVertical_Click(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.LayoutMdi(MdiLayout.TileVertical);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;mnuItmAbout_Click(object&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;FrmAbout().ShowDialog(this);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;<br>程序源码下载：<a href="http://www.cnblogs.com/Files/Infinity/WindowsForm.rar">http://www.cnblogs.com/Files/Infinity/WindowsForm.rar</a><br>　　<br><br>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-17 13:12 <a href="http://www.cnitblog.com/MartinYao/articles/28617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to do something in .net</title><link>http://www.cnitblog.com/MartinYao/articles/28365.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:48:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28365.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28365.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28365.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28365.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: IntroductionThese are some tips for commonly faced problems in .NET . Some of these tips are mine and some of these i have got from different sources. My aim is not to claim the ownership of these t...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/28365.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/28365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:48 <a href="http://www.cnitblog.com/MartinYao/articles/28365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Remote Desktop Manager (MSTSCUI.NET)</title><link>http://www.cnitblog.com/MartinYao/articles/24706.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 26 Mar 2007 14:40:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24706.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24706.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24706.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24706.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24706.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net/mstscuinet.zip">
								<font color="#002c99">Download demo project - 23 Kb</font>
						</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net/mstscuinet_src.zip">
								<font color="#002c99">Download source - 19.6 Kb</font>
						</a>
				</li>
		</ul>
		<p>
				<img height="470" alt="mstscui.net" src="http://www.codeproject.com/vb/net/mstscui_net/mstscuinet1.gif" width="194" />
		</p>
		<a name="intro">
				<h2>Introduction</h2>
		</a>
		<p nd="1">MSTSCUI.NET is simply a graphical interface to Microsoft's command-line Remote Desktop Connection, <i>mstsc.exe</i>. This application makes it easier for those who use Remote Desktop sessions often through the day. It uses the .NET Framework v1.1. 
</p>
		<p nd="2">Some of the things this app demonstrates: 
</p>
		<ul>
				<li nd="3">Reading a simple XML document 
</li>
				<li nd="4">Creating a systray application 
</li>
				<li nd="5">Dynamically populating a context menu </li>
		</ul>
		<p nd="6">All in all, a very simple and useful little application. 
</p>
		<ul>
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#general">
								<font color="#002c99">General Use</font>
						</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#appconfig">
								<font color="#002c99">MSTSCUI.NET.exe.config</font>
						</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#server">
								<font color="#002c99">Server Configuration XML (mstscui_servers.xml)</font>
						</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#code">
								<font color="#002c99">Select Code Samples</font>
						</a>
				</li>
		</ul>
		<a name="general">
				<h2>General Use</h2>
		</a>
		<p>
				<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#intro">
						<font color="#002c99">top</font>
				</a>
		</p>
		<p nd="7">MSTSCUI.NET runs as a taskbar application to allow quick and easy access to RDP <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/vb/net/mstscui_net.asp#" target="_blank" itxtdid="3442227">servers</a>: 
</p>
		<p>
				<img height="40" alt="taskbar" src="http://www.codeproject.com/vb/net/mstscui_net/mstscuinet2.gif" width="163" />
		</p>
		<p>
				<i>It's generally reccommended to place an entry in your startup group so that the application launches on startup.</i>
		</p>
		<p nd="8">Simply right-click on the taskbar icon to show the menu:</p>
		<p>
				<img height="470" alt="menu" src="http://www.codeproject.com/vb/net/mstscui_net/mstscuinet1.gif" width="194" />
		</p>
		<p nd="9">Select "Edit List" for a shortcut to edit your <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/vb/net/mstscui_net.asp#" target="_blank" itxtdid="3563345">server</a> configuration file, and "Refresh List" to reload it. </p>
		<a name="appconfig">
				<h2>MSTSCUI.NET.exe.config</h2>
		</a>
		<p>
				<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#intro">
						<font color="#002c99">top</font>
				</a>
		</p>
		<p nd="10">Use the <i>app.config</i> file to set the name of the server <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/vb/net/mstscui_net.asp#" target="_blank" itxtdid="3497373">configuration</a> file with the "<code nd="11">ServerConfigFile</code>" key, and set your preferred editor with the "<code nd="12">Editor</code>" key. It's not recommended you change the "<code nd="13">ServerConfigFile</code>" key. The "<code nd="14">Editor</code>" key should be a fully qualified path if the editor you're using is not already setup in your path environment variables. </p>
		<p nd="15">Below is the listing of the default <i>app.config</i> file:</p>
		<pre lang="xml" nd="16">&lt;xmp&gt;
&lt;configuration&gt;
    &lt;appSettings&gt;

        &lt;!-- path to XML Server Configuration File --&gt;
        &lt;add key="ServerConfigFile" value="mstscui_servers.xml" /&gt;
        
        &lt;!-- XML File Editor to use --&gt;
        &lt;add key="Editor" value="notepad" /&gt;

     &lt;/appSettings&gt;
&lt;/configuration&gt;
&lt;/xmp&gt;</pre>
		<a name="server">
				<h2>Server Configuration XML (mstscui_servers.xml)</h2>
		</a>
		<p>
				<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#intro">
						<font color="#002c99">top</font>
				</a>
		</p>
		<p nd="17">Use the server configuration file to setup each server.</p>
		<p nd="18">In order to display a horizontal separator bar on the menu, use:</p>
		<pre lang="xml" nd="19">&lt;xmp&gt;
    &lt;server displayname="-" /&gt;
&lt;/xmp&gt;</pre>
		<p nd="20">Each server element has an attribute called "<code nd="21">displayname</code>" - this is the name displayed on the menu. You can be as descriptive here as necessary.</p>
		<ul>
				<li nd="23">
						<code nd="22">servername</code> - server name or IP address 
</li>
				<li nd="25">
						<code nd="24">port</code> - default is 3389 
</li>
				<li nd="27">
						<code nd="26">resolution</code> - "<code nd="28">fullscreen</code>", or any resolution entered in this fashion: 1024x728 
</li>
				<li nd="30">
						<code nd="29">console</code> - 1 for yes, takeover console session, 0 for no, do not takeover console 
</li>
				<li nd="32">
						<code nd="31">connectionfile</code> - fully qualified path to the RDP file if you want to use one </li>
		</ul>
		<p nd="33">Below is the listing of a sample server config file:</p>
		<pre lang="xml" nd="34">&lt;xmp&gt;
&lt;servers&gt;

    &lt;server displayname="eventvan02 / secure"&gt;
        &lt;servername&gt;eventvan02&lt;/servername&gt;
        &lt;port&gt;3389&lt;/port&gt;
        &lt;resolution&gt;1024x768&lt;/resolution&gt;
        &lt;console&gt;0&lt;/console&gt;
        &lt;connectionfile&gt;&lt;/connectionfile&gt;
    &lt;/server&gt;

    &lt;server displayname="-" /&gt;

    &lt;server displayname="vancorpbc1"&gt;
        &lt;servername&gt;vancorpbc1&lt;/servername&gt;
        &lt;port&gt;3389&lt;/port&gt;
        &lt;resolution&gt;fullscreen&lt;/resolution&gt;
        &lt;console&gt;0&lt;/console&gt;
        &lt;connectionfile&gt;&lt;/connectionfile&gt;
    &lt;/server&gt;

&lt;/servers&gt;
&lt;/xmp&gt;</pre>
		<a name="code">
				<h2>Select Code Samples</h2>
		</a>
		<p>
				<a href="http://www.codeproject.com/vb/net/mstscui_net.asp#intro">
						<font color="#002c99">top</font>
				</a>
		</p>
		<p nd="35">Launching <i>msctsc.exe</i>...</p>
		<pre lang="vbnet" nd="38">
				<span class="vb-keyword" nd="36">Private</span>
				<span class="vb-keyword" nd="37">Sub</span> ConnectToServer(<span class="vb-keyword" nd="39">ByVal</span> sServer <span class="vb-keyword" nd="40">As</span><span class="vb-keyword" nd="41">String</span>)
    <span class="vb-keyword" nd="42">Try</span><span class="vb-keyword" nd="43">Dim</span> sCommand <span class="vb-keyword" nd="44">As</span><span class="vb-keyword" nd="45">String</span> = _
          System.Environment.GetEnvironmentVariable(<span class="vb-string" nd="46">"SystemRoot"</span>) &amp; _
          <span class="vb-string" nd="47">"\system32\mstsc.exe"</span>
        System.Diagnostics.Process.Start(sCommand, _
               GetArgumentsForServer(sServer))
    <span class="vb-keyword" nd="48">Catch</span> ex <span class="vb-keyword" nd="49">As</span> Exception
        MessageBox.Show(ex.Message)
    <span class="vb-keyword" nd="50">End</span><span class="vb-keyword" nd="51">Try</span><span class="vb-keyword" nd="52">End</span><span class="vb-keyword" nd="53">Sub</span></pre>
		<p nd="54">Reading the XML configuration file...</p>
		<div class="precollapse" id="premain4" style="WIDTH: 100%">
				<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
				<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="55" preid="4"> Collapse</span>
		</div>
		<pre lang="vbnet" id="pre4" style="MARGIN-TOP: 0px" nd="58">
				<span class="vb-keyword" nd="56">Private</span>
				<span class="vb-keyword" nd="57">Function</span> GetArgumentsForServer(<span class="vb-keyword" nd="59">ByVal</span> sServer <span class="vb-keyword" nd="60">As</span><span class="vb-keyword" nd="61">String</span>) <span class="vb-keyword" nd="62">As</span><span class="vb-keyword" nd="63">String</span><span class="vb-keyword" nd="64">Dim</span> sConnection <span class="vb-keyword" nd="65">As</span><span class="vb-keyword" nd="66">String</span><span class="vb-keyword" nd="67">Dim</span> sPort <span class="vb-keyword" nd="68">As</span><span class="vb-keyword" nd="69">String</span><span class="vb-keyword" nd="70">Dim</span> sResolution <span class="vb-keyword" nd="71">As</span><span class="vb-keyword" nd="72">String</span><span class="vb-keyword" nd="73">Dim</span> arrResolution() <span class="vb-keyword" nd="74">As</span><span class="vb-keyword" nd="75">String</span><span class="vb-keyword" nd="76">Dim</span> sConsole <span class="vb-keyword" nd="77">As</span><span class="vb-keyword" nd="78">String</span><span class="vb-keyword" nd="79">Dim</span> sConnectionFile <span class="vb-keyword" nd="80">As</span><span class="vb-keyword" nd="81">String</span><span class="vb-keyword" nd="82">Dim</span> sArguments <span class="vb-keyword" nd="83">As</span><span class="vb-keyword" nd="84">String</span><span class="vb-keyword" nd="85">Dim</span> parentnode <span class="vb-keyword" nd="86">As</span> XmlNode = doc.SelectSingleNode(<span class="vb-string" nd="87">"servers"</span>)
    <span class="vb-keyword" nd="88">Dim</span> servernode <span class="vb-keyword" nd="89">As</span> XmlNode = _
        SelectNode(parentnode, <span class="vb-string" nd="90">"displayname"</span>, sServer)
    <span class="vb-keyword" nd="91">Dim</span> childnode <span class="vb-keyword" nd="92">As</span> XmlNode

    <span class="vb-keyword" nd="93">For</span><span class="vb-keyword" nd="94">Each</span> childnode <span class="vb-keyword" nd="95">In</span> servernode.ChildNodes
        <span class="vb-keyword" nd="96">Select</span><span class="vb-keyword" nd="97">Case</span> UCase(childnode.Name)
            <span class="vb-keyword" nd="98">Case</span><span class="vb-string" nd="99">"SERVERNAME"</span>
                sConnection = <span class="vb-string" nd="100">" /v:"</span> &amp; Trim(childnode.InnerText)
            <span class="vb-keyword" nd="101">Case</span><span class="vb-string" nd="102">"PORT"</span><span class="vb-keyword" nd="103">If</span> Trim(childnode.InnerText) &lt;&gt; <span class="vb-string" nd="104">"3389"</span><span class="vb-keyword" nd="105">And</span> _
                        childnode.InnerText &lt;&gt; <span class="vb-keyword" nd="106">String</span>.Empty <span class="vb-keyword" nd="107">Then</span>
                    sPort = <span class="vb-string" nd="108">":"</span> &amp; childnode.InnerText
                <span class="vb-keyword" nd="109">Else</span>
                    sPort = <span class="vb-keyword" nd="110">String</span>.Empty
                <span class="vb-keyword" nd="111">End</span><span class="vb-keyword" nd="112">If</span><span class="vb-keyword" nd="113">Case</span><span class="vb-string" nd="114">"RESOLUTION"</span><span class="vb-keyword" nd="115">If</span> InStr(UCase(childnode.InnerText), <span class="vb-string" nd="116">"X"</span>) &gt; <span class="vb-literal" nd="117">0</span><span class="vb-keyword" nd="118">Then</span>
                    arrResolution = Split(UCase(childnode.InnerText), <span class="vb-string" nd="119">"X"</span>)
                    sResolution = <span class="vb-string" nd="120">" /w:"</span> &amp; arrResolution(<span class="vb-literal" nd="121">0</span>) &amp; _
                                  <span class="vb-string" nd="122">" /h:"</span> &amp; arrResolution(<span class="vb-literal" nd="123">1</span>)
                <span class="vb-keyword" nd="124">Else</span><span class="vb-comment" nd="125">'anything else, default to fullscreen</span>
                    sResolution = <span class="vb-string" nd="126">" /f"</span><span class="vb-keyword" nd="127">End</span><span class="vb-keyword" nd="128">If</span><span class="vb-keyword" nd="129">Case</span><span class="vb-string" nd="130">"CONSOLE"</span><span class="vb-keyword" nd="131">If</span><span class="vb-keyword" nd="132">CBool</span>(childnode.InnerText) <span class="vb-keyword" nd="133">Then</span>
                    sConsole = <span class="vb-string" nd="134">" /console"</span><span class="vb-keyword" nd="135">Else</span>
                    sConsole = <span class="vb-keyword" nd="136">String</span>.Empty
                <span class="vb-keyword" nd="137">End</span><span class="vb-keyword" nd="138">If</span><span class="vb-keyword" nd="139">Case</span><span class="vb-string" nd="140">"CONNECTIONFILE"</span>
                sConnectionFile = childnode.InnerText
            <span class="vb-keyword" nd="141">Case</span><span class="vb-keyword" nd="142">Else</span>
                MessageBox.Show(<span class="vb-string" nd="143">"Unrecognized node in XML: "</span> &amp; _
                                childnode.InnerText)
        <span class="vb-keyword" nd="144">End</span><span class="vb-keyword" nd="145">Select</span><span class="vb-keyword" nd="146">Next</span><span class="vb-comment" nd="147">'build argument string</span>
    sArguments = sConnection &amp; sPort &amp; sResolution &amp; sConsole

    <span class="vb-keyword" nd="148">Return</span> sArguments

<span class="vb-keyword" nd="149">End</span><span class="vb-keyword" nd="150">Function</span><span class="vb-keyword" nd="151">Private</span><span class="vb-keyword" nd="152">Function</span> SelectNode(<span class="vb-keyword" nd="153">ByVal</span> parentNode <span class="vb-keyword" nd="154">As</span> XmlNode, _
        <span class="vb-keyword" nd="155">ByVal</span> attributeName <span class="vb-keyword" nd="156">As</span><span class="vb-keyword" nd="157">String</span>, _
        <span class="vb-keyword" nd="158">ByVal</span> attributeValue <span class="vb-keyword" nd="159">As</span><span class="vb-keyword" nd="160">String</span>) <span class="vb-keyword" nd="161">As</span> XmlNode
    <span class="vb-keyword" nd="162">Dim</span> node <span class="vb-keyword" nd="163">As</span> XmlNode = <span class="vb-keyword" nd="164">Nothing</span><span class="vb-keyword" nd="165">If</span> parentNode.HasChildNodes <span class="vb-keyword" nd="166">Then</span><span class="vb-keyword" nd="167">Dim</span> nodeName <span class="vb-keyword" nd="168">As</span><span class="vb-keyword" nd="169">String</span> = parentNode.ChildNodes(<span class="vb-literal" nd="170">0</span>).Name
        <span class="vb-keyword" nd="171">Dim</span> path <span class="vb-keyword" nd="172">As</span><span class="vb-keyword" nd="173">String</span> = nodeName + <span class="vb-string" nd="174">"[@"</span> + _
            attributeName + <span class="vb-string">"='"</span> + attributeValue + <span class="vb-string">"']"</span>
        node = parentNode.SelectSingleNode(path)
    <span class="vb-keyword">End</span><span class="vb-keyword">If</span><span class="vb-keyword">Return</span> node
<span class="vb-keyword">End</span><span class="vb-keyword">Function</span></pre>
		<ul>
				<li>Online help here: <a href="http://www.slotzero.com/mstscui/" target="_blank"><font color="#002c99">MSTSCUI.NET Online Help</font></a></li>
				<li>Help forums here: <a href="http://www.slotzero.com/forums/" target="_blank"><font color="#002c99">Support Forums</font></a></li>
		</ul>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24706.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-26 22:40 <a href="http://www.cnitblog.com/MartinYao/articles/24706.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SIP stack with SIP proxy - (VOIP)</title><link>http://www.cnitblog.com/MartinYao/articles/24705.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Mon, 26 Mar 2007 14:36:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24705.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24705.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24705.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24705.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24705.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/useritems/SIP_stack_with_SIP_proxy/SIP_Proxy_demo.zip">Download SIP_Proxy_demo.zip - 580.3 KB</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/useritems/SIP_stack_with_SIP_proxy/Stack.zip">Download Stack.zip - 1,612.3 KB</a>
				</li>
		</ul>
		<img height="512" alt="Screenshot - proxy.jpg" src="http://www.codeproject.com/useritems/SIP_stack_with_SIP_proxy/proxy.jpg" width="640" />
		<h2>SIP overview:</h2>
		<p nd="1">SIP is an application-layer control protocol that can establish,<br />modify, and terminate multimedia sessions (conferences) such as<br />Internet telephony calls. SIP can also invite participants to<br />already existing sessions, such as multicast conferences.<br />Normally SIP uses UDP and TCP port 5060 anf TCP 5061 for SSL communication.<br />SIP protocol is very similar to HTTP, so who has some knowledge about http <br />then easy to learn SIP.<br />SIP doesn't transfer session data like audio,video, RTP(real time protocol) is used <br />for that, SIP just helps to open RTP streams.</p>
		<p>
				<strong>
				</strong>
		</p>
		<p>
				<strong>SIP message example:</strong>
		</p>
		<p>
				<code nd="2">INVITE sip:john@domain.com SIP/2.0</code>
				<code nd="3">
						<br />From: &lt;sip:doe@domain.com&gt;;tag=2084442460</code>
				<code nd="4">
						<br />To: &lt;sip:john@1domain.com&gt;</code>
				<code nd="5">
						<br />Via: SIP/2.0/UDP domain.com:5060;branch=z9hG4bK2df7b9194cd51e25</code>
				<code nd="6">
						<br />Call-ID: <a href="mailto:john@domain.com-4524j">john@domain.com-4524j</a></code>
				<code nd="7">
						<br />CSeq: 1 INVITE</code>
				<code nd="8">
						<br />Contact: &lt;sip:doe@domain.com:5060&gt;</code>
				<code nd="9">
						<br />Content-Length: 226</code>
				<code nd="10">
						<br />Content-Type: application/sdp</code>
		</p>
		<br />
		<p>
				<code nd="11">&lt;session description data, like RTP description&gt;</code>
		</p>
		<h2>SIP server types:</h2>
		<p>
		</p>
		<table style="WIDTH: 80.92%; HEIGHT: 353px" cellspacing="1" cellpadding="1" border="1">
				<tbody>
						<tr>
								<td width="150">
										<strong>stateless</strong>
								</td>
								<td nd="12">SIP server doesn't store any transaction info.</td>
						</tr>
						<tr>
								<td width="150">
										<strong>statefull</strong>
								</td>
								<td nd="13">SIP server creates and holds SIP commands transaction state.</td>
						</tr>
						<tr>
								<td width="150">
										<strong>registrar/location</strong>
								</td>
								<td nd="14">Allows users to register their locations and later to use that info to <br />forward calls to registered contact.</td>
						</tr>
						<tr>
								<td width="150">
										<strong>B2BUA</strong>
								</td>
								<td nd="15">SIP server is like statefull + holds active calls state.<br />(This is needed if call billing or full control of call is needed)</td>
						</tr>
						<tr>
								<td width="150">
										<strong>presence </strong>
								</td>
								<td nd="16">Provides user availabilty services, like if user is online,offline, ... .</td>
						</tr>
						<tr>
								<td width="150" nd="17">...</td>
								<td style="WIDTH: 50%" nd="18">there are some more, but not so important ones.</td>
						</tr>
				</tbody>
		</table>
		<h2>Basic SIP commands:</h2>
		<p nd="19">
				<strong>INVITE</strong> - Initiates a session. This method includes information about the calling and <br />called users and the type of media that is to be exchanged.</p>
		<p nd="20">
				<br />
				<strong>ACK</strong> - Sent by the client who sends the INVITE. ACK is sent to confirm that the <br />session is established. Media can then be exchanged.</p>
		<p nd="21">
				<br />
				<strong>BYE</strong> - Terminates a session. This method can be sent by either user.</p>
		<p nd="22">
				<br />
				<strong>CANCEL</strong> - Terminates a pending request, such as an outstanding INVITE. After a session is established, a BYE method needs to be used to terminate the session.</p>
		<p nd="23">
				<br />
				<strong>OPTIONS</strong> - Queries the capabilities of the server or other devices. It can be used to check media capabilities before issuing an INVITE.</p>
		<p nd="24">
				<br />
				<strong>REGISTER</strong> - Used by a client to login and register its address with a SIP registrar server.</p>
		<p nd="25">Ok, some ABC done,there are many documents on net, so not good idea to rewrite these there.<br />If want more advanced info then see:<br />RFC 3261 (defines SIPv2)<br /><a href="http://www.iptel.org/sip/intro">http://www.iptel.org/sip/intro</a><br /><a href="http://www.sipdev.org/wiki/index.php/A_newcomer's_guide_to_SIP">http://www.sipdev.org/wiki/index.php/A_newcomer's_guide_to_SIP</a><br /><a href="http://www.tech-invite.com/">http://www.tech-invite.com/</a></p>
		<p nd="26">and also google.com is always your friend.</p>
		<p>
		</p>
		<p nd="27">
				<strong>SIP proxy demo overview.</strong>
				<br />This SIP proxy example just implements fully functional simple statefull proxy.<br />You can use hardware SIP phones or soft phones to play with this proxy.<br />This is advanced example, code is well commeneted, so beginners don't hate me beacuse no more blaaa text here. Just read rfc 3216, see info links i noted before, if you then look<br />code all more nicer then.</p>
		<p nd="28">Some free available softphones in www:<br /><a href="http://www.nch.com.au/">http://www.nch.com.au</a><br /><a href="http://www.counterpath.net/">http://www.counterpath.net</a></p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/24705.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-26 22:36 <a href="http://www.cnitblog.com/MartinYao/articles/24705.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows TCP Tunnel</title><link>http://www.cnitblog.com/MartinYao/articles/21441.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 03:05:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21441.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21441.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21441.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21441.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21441.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/cs/internet/WinTunnel/WinTunnel_src.zip">Download WinTunnel source - 30.9 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p nd="1">Microsoft .NET contains many enhanced foundation classes that makes writing high performance socket applications easy. This project demonstrates how to use asynchronous sockets to write a high performance yet simple to use TCP tunneling application--mapping ports from one machine to many others.</p>
		<p nd="2">I was looking for a simple TCP tunneling application but didn't find any on this site. My requirement is a high performance (low CPU utilization) application, that can run as a Windows Service, is easy to configure, and can handle many concurrent ports.</p>
		<h2>Overview</h2>
		<p>
				<img height="429" alt="Program/Class Design" src="http://www.codeproject.com/cs/internet/WinTunnel/WinTunnel_design.gif" width="582" />
		</p>
		<h2>Using the code</h2>
		<p nd="3">Since WinTunnel is a Windows Service, the first step is to install it. You can pass a command line switch <i>-install</i> to install it as a service using the LocalSystem account. Optionally, you can pass in a user name and password via the <i>-user</i> and <i>-password</i> switches, respectively.</p>
		<p nd="4">The program needs to read a configuration file called <i>WinTunnel.ini</i> in the current directory or the directory where the binary is located. The content of the configuration file is very simple. The first line defines a service name--can be anything because it is only used for printing debug messages. The second line defines where to listen for client connections. The third line defines the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/cs/internet/WinTunnel.asp#" target="_blank" itxtdid="1664622">server connection</a> information.</p>
		<pre lang="text" nd="5">[HTTP]
accept  = 80               &lt;====== defines which port to accept the 
                                   client connection--
                                   either &lt;Port&gt; or &lt;IP:Port&gt;
connect = 192.168.1.10:80  &lt;====== defines the target of the connection
                                   --must be in the format &lt;IP:Port&gt;

[HTTPS]
accept  = 192.168.1.100:443
connect = 192.168.1.125:443</pre>
		<h2>Interesting issues</h2>
		<p nd="6">One interesting problem I encountered is that .NET requires the use of <i>InstallUtil.exe</i> to put the C# service into the Global Assembly Cache (GAC) and register with the Service Control Manager (SCM). In order to just install it by running the binary, a background process is launched that calls <i>InstallUtil.exe</i> with all the required parameters. The output is then redirected to a buffer and printed out on the console--see the code below.</p>
		<div class="precollapse" id="premain1" style="WIDTH: 100%">
				<img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" />
				<span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="7" preid="1"> Collapse</span>
		</div>
		<pre lang="cs" id="pre1" style="MARGIN-TOP: 0px" nd="10">
				<span class="cs-keyword" nd="8">static</span>
				<span class="cs-keyword" nd="9">void</span> launchProcess(String binary, String argument)
{
    System.Diagnostics.ProcessStartInfo psInfo = 
     <span class="cs-keyword" nd="11">new</span> System.Diagnostics.ProcessStartInfo(binary, argument);

    System.Console.WriteLine();
    System.Console.WriteLine(psInfo.FileName + 
                             <span class="cpp-string" nd="12">" "</span> + psInfo.Arguments);
    System.Console.WriteLine();

    psInfo.RedirectStandardOutput = <span class="cs-keyword" nd="13">true</span>;
    psInfo.WindowStyle = 
           System.Diagnostics.ProcessWindowStyle.Hidden;
    psInfo.UseShellExecute = <span class="cs-keyword" nd="14">false</span>;
    System.Diagnostics.Process ps;
    ps = System.Diagnostics.Process.Start(psInfo);
    System.IO.StreamReader msgOut = ps.StandardOutput;
    ps.WaitForExit(<span class="cs-literal" nd="15">5000</span>); <span class="cs-comment" nd="16">//wait up to 5 seconds </span><span class="cs-keyword" nd="17">if</span> (ps.HasExited)
    {
        <span class="cs-comment" nd="18">//write the output</span>
        System.Console.WriteLine(msgOut.ReadToEnd());
    }
    <span class="cs-keyword" nd="19">return</span>;
}</pre>
		<p nd="20">In order to debug a Service application, a debug switch was added to the application. Generally, it is hard to debug a service application because there is no console. When the debug switch is set, log messages are printed on the console. In order to properly shutdown the service application in this mode, a Win32 API call is needed to capture the Ctrl-C key press. Once it is captured, an event object is signaled to properly shutdown--see <i>ConsoleEvent.cs</i> for details.</p>
		<pre lang="cs" nd="23">
				<span class="cs-comment" nd="21">//create a signal handler to detect Ctrl-C to stop the service</span>
				<span class="cs-keyword" nd="22">if</span> (m_debug)
{
    m_ctrl = <span class="cs-keyword" nd="24">new</span> ConsoleCtrl();
    m_ctrl.ControlEvent += <span class="cs-keyword" nd="25">new</span> 
       ConsoleCtrl.ControlEventHandler(consoleEventHandler);
}
  
<span class="cs-keyword" nd="26">public</span><span class="cs-keyword" nd="27">static</span><span class="cs-keyword" nd="28">void</span> consoleEventHandler(ConsoleCtrl.ConsoleEvent consoleEvent)
{
    <span class="cs-keyword" nd="29">if</span> (ConsoleCtrl.ConsoleEvent.CTRL_C == consoleEvent)
    {
        Logger.getInstance().info(<span class="cpp-string" nd="30">"Received CTRL-C from"</span> + 
                                  <span class="cpp-string" nd="31">" Console. Shutting down..."</span>);
        WinTunnel.shutdownEvent.Set();
    }
    <span class="cs-keyword" nd="32">else</span>
    {
        Logger.getInstance().warn(<span class="cpp-string" nd="33">"Received unknown"</span> + 
               <span class="cpp-string" nd="34">" event {0}.  Ignoring..."</span>, consoleEvent);
    }
}</pre>
		<h2>Code discussion</h2>
		<p>
				<img height="339" alt="ThreadPool" src="http://www.codeproject.com/cs/internet/WinTunnel/ThreadPool.gif" width="390" />
		</p>
		<h3>ThreadPool</h3>
		<p nd="35">In order to make the application design simple, the <code nd="36">ThreadPool</code> class was created. It allows the application to be broken down to various tasks and executed by the Thread Pool. This design greatly reduces the need to synchronize--a big performance bottleneck. The idea of the thread pool is that an application should not create many threads. Instead, a small number of them should be created during startup, and reused over and over again until shutdown. Any work that needs to be done in the application will have to be added to a task list, and if there is a free thread, it should pick up the task and execute it.</p>
		<p nd="37">A good analogy would be with the UPS delivery service. It will be terribly in-efficient if a new worker has to be hired every time a package needs to be delivered. Instead, UPS hires a bunch of them and asks them to deliver packages over and over again.</p>
		<p nd="38">Similar to the package in the above analogy, a piece of work that needs to be done in the application is called a task. The task must implement the <code nd="39">ITask</code> interface--which has two methods: <code nd="40">getName()</code> and <code nd="41">run()</code>. The <code nd="42">getName()</code> method is used for debugging and returns the details of the task. The <code nd="43">run()</code> method is the entry point for a thread in the thread pool to perform the work.</p>
		<pre lang="cs" nd="46">
				<span class="cs-keyword" nd="44">public</span>
				<span class="cs-keyword" nd="45">interface</span> ITask 
{
    <span class="cs-keyword" nd="47">void</span> run();
    String getName();
}</pre>
		<p nd="48">In this application, there are only three types of tasks. The first task is to listen for client connections. This is implemented by the <code nd="49">ProxyClientListenerTask</code>. When the task executes, it creates a socket, and then calls the asynchronous <code nd="50">BeginAccept()</code> method. The call requires a callback method and also an object as a parameter (<code lang="cs"><span class="cs-keyword" nd="51">this</span></code>).</p>
		<pre lang="cs" nd="54">
				<span class="cs-keyword" nd="52">public</span>
				<span class="cs-keyword" nd="53">void</span> run()
{
    listenSocket = <span class="cs-keyword" nd="55">new</span> Socket( AddressFamily.InterNetwork, 
                               SocketType.Stream, ProtocolType.Tcp);
    listenSocket.Bind(m_config.localEP);
    listenSocket.Listen(<span class="cs-literal" nd="56">10</span>); <span class="cs-comment" nd="57">//allow up to 10 pending connections</span>
    logger.info(<span class="cpp-string" nd="58">"[{0}] Waiting for client connection at {1}..."</span>, 
                m_config.serviceName, m_config.localEP.ToString());

    listenSocket.BeginAccept( <span class="cs-keyword" nd="59">new</span> AsyncCallback(
                    ProxyClientListenerTask.acceptCallBack), <span class="cs-keyword" nd="60">this</span>);
}</pre>
		<p nd="61">The callback is invoked when the client connects to the listening socket. The first thing is to retrieve the passed in object which gets the socket. <code nd="62">EndAccept()</code> is then called, and the second task is created.</p>
		<div class="precollapse" id="premain5" style="WIDTH: 100%">
				<img id="preimg5" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="5" />
				<span id="precollapse5" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="63" preid="5"> Collapse</span>
		</div>
		<pre lang="cs" id="pre5" style="MARGIN-TOP: 0px" nd="67">
				<span class="cs-keyword" nd="64">public</span>
				<span class="cs-keyword" nd="65">static</span>
				<span class="cs-keyword" nd="66">void</span> acceptCallBack(IAsyncResult ar)
{
    ProxyConnection conn = <span class="cs-keyword" nd="68">null</span>;
    <span class="cs-keyword" nd="69">try</span>
    {
        ProxyClientListenerTask listener = 
                 (ProxyClientListenerTask) ar.AsyncState;

        <span class="cs-comment" nd="70">//create a new task for connecting to the server side.</span>
        conn = m_mgr.getConnection();
        conn.serviceName = listener.m_config.serviceName;
        <span class="cs-comment" nd="71">//accept the client connection</span>
        conn.clientSocket = listener.listenSocket.EndAccept(ar);

        logger.info(<span class="cpp-string" nd="72">"[{0}] Conn#{1} Accepted new connection."</span> + 
                    <span class="cpp-string" nd="73">" Local: {2}, Remote: {3}."</span>, 
            conn.serviceName,
            conn.connNumber,
            conn.clientSocket.LocalEndPoint.ToString(), 
            conn.clientSocket.RemoteEndPoint.ToString() );

        conn.serverEP = listener.m_config.serverEP;
                
        <span class="cs-comment" nd="74">//Start listening for connection on this port again</span>
        listener.listenSocket.BeginAccept( <span class="cs-keyword" nd="75">new</span> AsyncCallback(
          ProxyClientListenerTask.acceptCallBack), listener);

        <span class="cs-comment" nd="76">//now try to connect to the server</span>
        ProxyServerConnectTask serverTask = 
                    <span class="cs-keyword" nd="77">new</span> ProxyServerConnectTask(conn);
        ThreadPool.getInstance().addTask(serverTask);
    } 
    <span class="cs-keyword" nd="78">catch</span> (SocketException se)
    {
        logger.error(<span class="cpp-string" nd="79">"[{0}] Conn# {1} Socket Error occurred"</span> + 
                     <span class="cpp-string" nd="80">" when accepting client socket. Error Code is: {2}"</span>,
                     conn.serviceName, conn.connNumber, se.ErrorCode);
        <span class="cs-keyword" nd="81">if</span> (conn != <span class="cs-keyword" nd="82">null</span>)
        {
            conn.Release();
        }
    }
    <span class="cs-keyword" nd="83">catch</span> (Exception e)
    {
        logger.error(<span class="cpp-string" nd="84">"[{0}] Conn# {1} Error occurred when"</span> + 
            <span class="cpp-string" nd="85">" accepting client socket. Error is: {2}"</span>,
            conn.serviceName, conn.connNumber, e);
        <span class="cs-keyword" nd="86">if</span> (conn != <span class="cs-keyword" nd="87">null</span>)
        {
            conn.Release();
        }
    }
    <span class="cs-keyword" nd="88">finally</span>
    {
        conn = <span class="cs-keyword" nd="89">null</span>; <span class="cs-comment" nd="90">//free reference to the object</span>
    }
}</pre>
		<p nd="91">The second task is to connect to the server that the client's connection should be mapped to. This is implemented by the <code nd="92">ProxyServerConnectTask</code>. When the task executes, it creates a new socket, and makes an asynchronous <code nd="93">BeginConnect()</code> to connect to the server.</p>
		<pre lang="cs" nd="96">
				<span class="cs-keyword" nd="94">public</span>
				<span class="cs-keyword" nd="95">void</span> run()
{
    m_conn.serverSocket = <span class="cs-keyword" nd="97">new</span> Socket( AddressFamily.InterNetwork, 
                                      SocketType.Stream, ProtocolType.Tcp);
    m_conn.serverSocket.BeginConnect(m_conn.serverEP, 
         <span class="cs-keyword" nd="98">new</span> AsyncCallback(connectCallBack), m_conn);    
}</pre>
		<p nd="99">The callback is invoked when the connection to the server is established and the third task is created.</p>
		<pre lang="cs" nd="103">
				<span class="cs-keyword" nd="100">public</span>
				<span class="cs-keyword" nd="101">static</span>
				<span class="cs-keyword" nd="102">void</span> connectCallBack(IAsyncResult ar)
{
    ProxyConnection conn = (ProxyConnection) ar.AsyncState;

    <span class="cs-keyword" nd="104">try</span>
    {
        conn.serverSocket.EndConnect(ar); 
                        
        logger.info(<span class="cpp-string" nd="105">"[{0}] ProxyConnection#{1}--connected "</span> + 
            <span class="cpp-string" nd="106">"to Server.  Server: {2}, Local: {3}."</span>,
            conn.serviceName,
            conn.connNumber,
            conn.serverSocket.RemoteEndPoint, 
            conn.serverSocket.LocalEndPoint);

        <span class="cs-comment" nd="107">//create task for proxying data between </span><span class="cs-comment" nd="108">//the client and server socket</span>
        ProxySwapDataTask dataTask  = <span class="cs-keyword" nd="109">new</span> ProxySwapDataTask(conn);
        ThreadPool.getInstance().addTask(dataTask);
    } 
    {...removed error handling code...}
}</pre>
		<p nd="110">The third task takes both the client socket and the server socket, and swaps data back and forth between them. This is implemented by the <code nd="111">ProxySwapDataTask</code>. There is a callback sending and receiving data for both the client and the server socket. There are also various checks to detect socket errors. When an error occurs, both the client and the server socket are shutdown.</p>
		<div class="precollapse" id="premain8" style="WIDTH: 100%">
				<img id="preimg8" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="8" />
				<span id="precollapse8" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="112" preid="8"> Collapse</span>
		</div>
		<pre lang="cs" id="pre8" style="MARGIN-TOP: 0px" nd="115">
				<span class="cs-keyword" nd="113">public</span>
				<span class="cs-keyword" nd="114">void</span> run()
{
    <span class="cs-comment" nd="116">//validate that both the client side and server </span><span class="cs-comment" nd="117">//side sockets are ok. If so, do read/write</span><span class="cs-keyword" nd="118">if</span> (m_conn.clientSocket == <span class="cs-keyword" nd="119">null</span> || m_conn.serverSocket == <span class="cs-keyword" nd="120">null</span>)
    {
        logger.error(<span class="cpp-string" nd="121">"[{0}] ProxyConnection#{1}--Either"</span> + 
                     <span class="cpp-string" nd="122">" client socket or server socket is null."</span>,
                     m_conn.serviceName, m_conn.connNumber);
        m_conn.Release();
        <span class="cs-keyword" nd="123">return</span>;
    }

    <span class="cs-keyword" nd="124">if</span> (m_conn.clientSocket.Connected &amp;&amp; m_conn.serverSocket.Connected)
    {
        <span class="cs-comment" nd="125">//Read data from the client socket</span>
        m_conn.clientSocket.BeginReceive( m_conn.clientReadBuffer, 
               <span class="cs-literal" nd="126">0</span>, ProxyConnection.BUFFER_SIZE, <span class="cs-literal" nd="127">0</span>,
               <span class="cs-keyword" nd="128">new</span> AsyncCallback(clientReadCallBack), m_conn);
            
        <span class="cs-comment" nd="129">//Read data from the server socket</span>
        m_conn.serverSocket.BeginReceive( m_conn.serverReadBuffer, 
               <span class="cs-literal" nd="130">0</span>, ProxyConnection.BUFFER_SIZE, <span class="cs-literal" nd="131">0</span>,
               <span class="cs-keyword" nd="132">new</span> AsyncCallback(serverReadCallBack), m_conn);
    }
    <span class="cs-keyword" nd="133">else</span>
    {
        logger.error(<span class="cpp-string" nd="134">"[{0}] ProxyConnection#{1}: Either"</span> + 
            <span class="cpp-string" nd="135">" the client or server socket got disconnected."</span>, 
            m_conn.serviceName, m_conn.connNumber );
        m_conn.Release();
    }
    m_conn = <span class="cs-keyword" nd="136">null</span>;
}</pre>
		<p nd="137">There is a callback for sending and receiving data for both the client and the server socket. There are also various checks to detect socket errors. When an error occurs, the connection is shutdown, and both the client and the server socket are closed.</p>
		<h2>End note</h2>
		<p nd="138">Unlike others, this project is the complete source of a fully functioning application. As such, there are various classes such as logging that I don't even cover in the discussion. I only try to point out the interesting things that I encountered when designing and implementing the application. Hopefully, it will be useful for someone doing something similar.</p>
		<h2>History</h2>
		<ul>
				<li nd="139">2006-06-28 - Initial implementation. 
</li>
				<li nd="140">2006-07-10 - Enhanced to show more debug messages, added more error handling logic, and cleanup of objects. Also updated this documentation. </li>
		</ul>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Han_Jun_Li</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=3148904">here</a> to view Han_Jun_Li's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21441.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 11:05 <a href="http://www.cnitblog.com/MartinYao/articles/21441.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Remote Events using Delegates – a real world example of 'chat and file send' application</title><link>http://www.cnitblog.com/MartinYao/articles/21440.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 03:04:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21440.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21440.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21440.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21440.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21440.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Introduction						Dot Net Remoting is a simple programming model/framework which allows objects from different machines/processes/app-domains to communicate each other. It allows the flexibility o...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/21440.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/21440.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 11:04 <a href="http://www.cnitblog.com/MartinYao/articles/21440.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Push and Run .NET Code on Remote Machine</title><link>http://www.cnitblog.com/MartinYao/articles/21439.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 02:55:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21439.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21439.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21439.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21439.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely_src.zip">Download source files - 69.7 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely_demo.zip">Download demo project - 43.7 Kb</a>
				</li>
		</ul>
		<p>
				<img height="242" alt="Sample Image - Run_Code_Remotely.png" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Pushing_Code_Drawing.png" width="592" align="middle" />
		</p>
		<p>
				<img height="236" alt="Sample Image - Run_Code_Remotely.png" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Run_Code_Remotely.png" width="600" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">I've been doing Windows programming for a long time, and I've always longed for the ability to do what some of the Unix guys have always been able to do: run commands on a remote machine. Although, there's a small twist. I really don't want to run some shell commands on a remote machine, but rather write some code, push it over to a remote machine, and have it execute over there without manually copying the files over. Not only that, but I'd like to interact with the process locally with standard in, standard out and standard error. What this will allow me to do is to write some administrative code and run the assembly on the remote machine(s) without any special hooks into my admin code. As well, if I have some process that will take a bunch of time to run, I can simply have it run on another machine, and report back the status and results without consuming time on my own machine. The latter approach leads to a more general subject of viewing any machine as a general computing resource.</p>
		<h2>Quick Example (output from the image above)</h2>
		<p nd="2">The image above is the output of an assembly that simply functions as a .NET version of "DIR". Basically, a search was issued for <i>*.mp3</i> on the local machine and the results were displayed. Next, the search was issued <b>in parallel</b> to all the machines that matched the name "testwin*". In this case, two machines matched that name. I ran the second one just to show that it could be run explicitly as well.</p>
		<h3>Give it a try!</h3>
		<p nd="3">If you'd like to test it out, download the demo project from the link above into a folder. Here's the usage:</p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="4" preid="0"> Collapse</span>
		</div>
		<pre lang="text" id="pre0" style="MARGIN-TOP: 0px" nd="5">Copyright (C) 2004-2005 Jim Wiese 
 
Executes the specified assembly on the remote machine and the remote <br />application will function interactively. 
 
 Usage: \\machine[,\\machine2,[...]] [-port portNumber] [-serial]<br />   [-interactive] [-timeout [days.]HH:MM:SS] [-remoteUserId userId] <br />   [-remotePassword password] [-unmanaged pathToZipFile.zip]<br />   [-debug] assembly [arguments] 
 
machine: The machine to run the assembly on. Note that this value can have <br />    the '*' character to represent multiple machines. As well, \* will match<br />    all the machines in the domain. (eg HP* -&gt; HP1, HP2) 
port:    The port number to use for the communication between processes 
    assembly: The assembly name to run on the remove machine. This assembly<br />    must have an entry point defined (ie Main). 
arguments: Any arguments to pass to the executed assembly. 
debug: Write debugging information to the standard out. 
serial:     Run the machines serially, one at a time. By default, all the <br />    machines are run in parallel. 
interactive: Allow the process to interact with the desktop process 
timeout:     Allow the process to run for the specified amount of time <br />    remoteUserId: The user ID to run under on the remote process <br />    remotePassword: The password for the remote user ID 
unmanaged:     The path to a zip file with the unmanaged resources to <br />    decompress on the remote machine


</pre>
		<h2>Possible uses for this library / tool</h2>
		<ul>
				<li nd="6">Run-time deployment of Application servers (eg for business logic) to a specific set of machines 
</li>
				<li nd="7">Run-time deployment of web servers to a specific set of machinesPerform administrative functions on Windows Domain clients such as run time installation of software 
</li>
				<li nd="8">Determine if specific files are stored on Windows machines <b>without the need for UNC shares</b> (eg. C:\&gt; runremote \\* quickdir.exe *.mp3) 
</li>
				<li nd="9">Create an on demand "Peer Grid" to solve problems such as sorting a large file before import into a database 
</li>
				<li nd="10">Force GPO updates on the client machines immeadiately 
</li>
				<li nd="11">Determine if someone is actually logged onto a console of a machine 
</li>
				<li nd="12">Log people off the console of the machine if they're logged in :) (eg for Kiosk purposes)</li>
		</ul>
		<h2>Sweetness, how does it work?</h2>
		<p nd="13">The main premise of the process is quite simple, but with a couple of small "gotchas". First, the process creates a temporary Windows service on the remote machine with a random name. I say random in the sense that a GUID is appended to the service name. This service creates a child process that will execute the specified assembly. The service waits for the child process to finish executing the assembly, then removes itself (i.e., the Service entry) from the remote machine, and exits.</p>
		<p nd="14">The interesting premise comes in two different steps:</p>
		<ol>
				<li nd="15">How does the service get the executable to run? 
</li>
				<li nd="16">How are the assemblies shuffled over to the remote machine? </li>
		</ol>
		<h2>Pushing the executable to run the temp service</h2>
		<p nd="17">Here's the first of the two "gotchas" from above: the service code is copied over to the UNC folder <i><a href="file://remotemachine/admin$/temp">file://remoteMachine/admin$/temp</a></i>. Windows exposes the administrative share named <i>Admin$</i> by default, and we make use of this share to get the executable over to the remote machine. We could have created a service that used <i>\\sourceMachine\Admin$\temp</i> (i.e., the machine from which the command was run), but any .NET code that is run under a UNC uses a much more restrict security policy. These temporary files will be removed when the entire process is finished. As a matter of fact, it goes to great length to try to make sure that the files are removed once all is finished.</p>
		<p nd="18">The one main thing to keep in mind is that the sole purpose of the service is to create a remote process. Some people refer to this as a hack method of starting processes, but in this case, I consider it a feature.</p>
		<h2>But what about the assembly that I want you to run?</h2>
		<p nd="19">The next important thing to consider is that we have not actually copied the assembly that is going to be run over to the remote machine. This is where a small piece of elegance comes in the code. What the service shim does is attempt to load "YourAssembly". Since this assembly does not exist on the remote machine, a Loader exception will occur. There is a handy little event that is fired when this happens in an AppDomain named:</p>
		<pre lang="cs" nd="29">
				<span class="cs-comment" nd="20">/// &lt;summary&gt;</span>
				<span class="cs-comment" nd="21">/// Event that is called when the domain can not find an assembly.</span>
				<span class="cs-comment" nd="22">/// We want to lookup this assembly on the remote machine and return it.</span>
				<span class="cs-comment" nd="23">/// &lt;/summary&gt;</span>
				<span class="cs-comment" nd="24">/// &lt;param name="sender"&gt;The current AppDomain&lt;/param&gt;</span>
				<span class="cs-comment" nd="25">/// &lt;param name="args"&gt;Event arguments for this event&lt;/param&gt;</span>
				<span class="cs-comment" nd="26">/// &lt;returns&gt;The assembly from the remote machine</span>
				<span class="cs-comment" nd="27">///          or null if it wasn't found&lt;/returns&gt;</span>
				<span class="cs-keyword" nd="28">private</span> Assembly CurrentDomain_AssemblyResolve(<span class="cs-keyword" nd="30">object</span> sender,
                                       ResolveEventArgs args)
{
  <span class="cs-keyword" nd="31">return</span> ResolveAssemblyOnRemoteMachine( args.Name ) ;
}</pre>
		<p nd="32">This event gives the assembly name that was attempted to be loaded in the arguments, and allows the block of code a chance to locate it and return it. In this case, a small block of code is put in to connect back to the source machine and request the lookup on that machine.</p>
		<p nd="33">Since the code does exist on the source machine, the bytes of the assembly are packaged up and sent back to the remote machine. The assembly is then loaded from these bytes and returned to the AppDomain loader.</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="34" preid="2"> Collapse</span>
		</div>
		<pre lang="cs" id="pre2" style="MARGIN-TOP: 0px" nd="40">
				<span class="cs-comment" nd="35">/// &lt;summary&gt;</span>
				<span class="cs-comment" nd="36">/// Resolve the assembly from the remote machine (machine from which the <br />/// command was run). If the assembly exists on the remote machine, it <br />//  will be shuffled over to this process and then loaded and returned.</span>
				<span class="cs-comment" nd="37">/// &lt;/summary&gt;</span>
				<span class="cs-comment" nd="38">/// &lt;param name="assemblyName"&gt;The name of the assembly to resolve&lt;/param&gt;</span>
				<span class="cs-comment" nd="39">/// &lt;returns&gt;The assembly if it existed, otherwise null&lt;/returns&gt;</span>
Assembly ResolveAssemblyOnSourceMachine( <span class="cs-keyword" nd="41">string</span> assemblyName )
{
   <span class="cs-comment" nd="42">//</span><span class="cs-comment" nd="43">// This is the main point in which we shuffle</span><span class="cs-comment" nd="44">// the assembly from the server to this</span><span class="cs-comment" nd="45">// client. //</span><span class="cs-keyword" nd="46">byte</span>[] assemblyBytes = server.GetAssembly( assemblyName ) ;
   <span class="cs-keyword" nd="47">if</span> ( assemblyBytes != <span class="cs-keyword" nd="48">null</span> &amp;&amp; assemblyBytes.Length &gt; <span class="cs-literal" nd="49">0</span> )
   {
     Assembly loaded = Assembly.Load( assemblyBytes ) ;
     <span class="cs-keyword" nd="50">return</span> loaded ;
   }
   <span class="cs-keyword" nd="51">else</span>
   {
     <span class="cs-keyword" nd="52">return</span><span class="cs-keyword" nd="53">null</span> ;
   }
}</pre>
		<p nd="54">At this point, you'll notice that I referenced a small variable named "<code nd="55">server</code>" which seems to get the bytes of the assembly. This "server" variable is actually a .NET Remoted object that is being served up by our source machine. There are probably a variety of ways that this could be done, but the .NET remoting method was too clean and easy to pass up.</p>
		<p nd="56">Once the assembly that you requested to be run is loaded, the EntryPoint is invoked with any command line arguments that you might want to pass in. Typically, the EntryPoint is the "<code lang="cs" nd="57">Main</code>" of your assembly. After your <code lang="cs" nd="58">Main( <span class="cs-keyword" nd="59">string</span>[] args )</code> is run, the return code is reported back to the source machine and the process on the remote machine exists. To appear like a local process, the "<i>runremote.exe</i>" process on the source machine exists with the same return code of the remote process so that you can use shell utilities to check the return code.</p>
		<p nd="60">Now, your executing assembly might have also needed some other dependencies. If it references those dependencies, won't it cause a problem? Thankfully, the answer is no, it won't cause a problem. Any missing dependencies cause the <code nd="61">ResolveAssembly</code> event to be called and loaded by the same mechanism.</p>
		<h2>"Down by the water, out by the sea..." (??? grunge, circa 1993)</h2>
		<p nd="62">Now, if you've made this far in the article, I'm impressed and honored. Let's not delay and delve into the depths of the nitty and the gritty of the code:</p>
		<p nd="63">All the code starts in the project RunRemote in the file <i>RunRemote.cs</i> in the class <code nd="64">RemoteProcess</code>. This is the main project if you'd like to run it programmatically, but if you're running the command version, then it's the class <code nd="65">CommandLineRemoteProcess</code> which actually calls <code nd="66">RemoteProcess.ExecuteRemotely</code>. Basically, it's all fairly simple to call from the outside. Here's the sample code to actually run an assembly remotely:</p>
		<pre lang="cs" nd="67">RemoteProcess  remote     = <span class="cs-keyword" nd="68">new</span> RemoteProcess() ;
<span class="cs-keyword" nd="69">int</span>            exitCodeOfRemoteProcess   =
                    remote.ExecuteRemotely(
                        <span class="cpp-string" nd="70">"YourAssembly"</span>, <span class="cs-comment" nd="71">// Note: partial assembly name  </span><span class="cs-keyword" nd="72">new</span><span class="cs-keyword" nd="73">string</span>[] { <span class="cpp-string" nd="74">"-arg1"</span>, <span class="cpp-string" nd="75">"-arg2"</span> },
                        <span class="cs-keyword" nd="76">new</span><span class="cs-keyword" nd="77">string</span>[] { @<span class="cpp-string" nd="78">"\\onMyMachine"</span> },
                        <span class="cs-literal" nd="79">0</span>,
                        <span class="cs-literal" nd="80">0</span>,
                        <span class="cs-keyword" nd="81">false</span>,
                        <span class="cs-keyword" nd="82">new</span> TimeSpan( Timeout.Infinite ),
                        <span class="cs-literal" nd="83">1</span> ) ;

<span class="cs-comment" nd="84">//</span><span class="cs-comment" nd="85">// NOTE: There are some other overloaded versions of this method with </span><span class="cs-comment" nd="86">// more parameters, see the source code for details</span><span class="cs-comment" nd="87">//</span></pre>
		<p nd="88">Now, I've described before that a file is copied and a service is created. I'm not going to outline the code to copy files and create the service, but I'll give some specifics that are interesting. First, the output of the project "ServiceOnRemoteMachine" generates "<i>ServiceOnRemoteMachine.exe</i>". Take a wild guess at what this file is used for :). After this file is copied to the remote machine, the service is created with the file name of "<i>%SYSTEMROOT%\temp\ServiceOnRemoteMachine.exe</i>". This path name is the resolved value of the mapped path <i>\\remoteMachine\admin$\temp\ServiceOnRemoteMachine.exe</i>, but with a local reference (e.g., <i>C:\Windows\temp\ServiceOnRemoteMachine.exe</i>). As well, the service name of the service that is running is passed as an argument as well as the remoting URI to connect back to the source machine. The last argument is the name of the assembly to load (i.e., the name of your assembly). For example, the path for the service might be:</p>
		<pre lang="text" nd="89">"C:\WINDOWS\temp\serviceonremotemachine.exe" -fromService
         ExecRemote-e2088b28-330f-4ac6-9627-799ddd435004
         "tcp://dublin:3237/83c1ca40_8305_4583_bf5b_9265a03d9de4/RunRemote.rem"
         "runthisonothermachine"</pre>
		<p nd="90">When the service first starts up in the <code lang="cs" nd="91">Main()</code> method, the argument "-fromService" is noted. This tells the service to spawn a new process using itself as the executable, but removing the service name and the "-fromService" argument.</p>
		<h3>Process and AppDomain isloation for safety (Injecting the safe way)</h3>
		<img height="228" alt="" src="http://www.codeproject.com/csharp/Run_Code_Remotely/Service_Process_Drawing.png" width="630" />
		<p>
		</p>
		<p nd="92">Now, in the new child process, a remoting object named "<code nd="93">Server</code>" is hooked up by the Activator and uses the URI to get back to the source machine.</p>
		<pre lang="cs" nd="97">
				<span class="cs-comment" nd="94">//</span>
				<span class="cs-comment" nd="95">// Setup the formatters</span>
				<span class="cs-comment" nd="96">//</span>
TcpChannel    channel        = <span class="cs-keyword" nd="98">new</span> TcpChannel() ;
ChannelServices.RegisterChannel( channel );

<span class="cs-comment" nd="99">// Create an instance on the remote server and call a method remotely</span>
server = (Server)Activator.GetObject(   <span class="cs-keyword" nd="100">typeof</span> ( Server ), <span class="cs-comment" nd="101">// Type to create</span>
                                        serverUri  );

</pre>
		<p nd="102">The whole rest of the code and purpose for the entire class comes to the final method. This method loads up the assembly, hooks up <code nd="103">STDIN</code>, <code nd="104">STDOUT</code>, <code nd="105">STDERR</code>, and runs the sucker.</p>
		<div class="precollapse" id="premain6" style="WIDTH: 100%">
				<img id="preimg6" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="6" />
				<span id="precollapse6" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="106" preid="6"> Collapse</span>
		</div>
		<pre lang="cs" id="pre6" style="MARGIN-TOP: 0px" nd="120">
				<span class="cs-comment" nd="107">/// &lt;SUMMARY&gt;</span>
				<span class="cs-comment" nd="108">/// Believe it or not, the entire application reduces to this one method. By</span>
				<span class="cs-comment" nd="109">/// this point, we are running in a process on the remote machine</span>
				<span class="cs-comment" nd="110">/// &lt;/SUMMARY&gt;</span>
				<span class="cs-comment" nd="111">/// <param name="assemblyName" />The name of the assembly to execute. This name</span>
				<span class="cs-comment" nd="112">/// can be either a partial name such as "RunThisOnOtherMachine" or a fully</span>
				<span class="cs-comment" nd="113">/// qualified name.</span>
				<span class="cs-comment" nd="114">/// </span>
				<span class="cs-comment" nd="115">/// <param name="args" />Arguments to send to the main method, or null for no args</span>
				<span class="cs-comment" nd="116">/// &lt;RETURNS&gt;The return result from the entry</span>
				<span class="cs-comment" nd="117">///   point of the requested assembly&lt;/RETURNS&gt;</span>
				<span class="cs-keyword" nd="118">public</span>
				<span class="cs-keyword" nd="119">object</span> LoadAssemblyAndRunIt( <span class="cs-keyword" nd="121">string</span> assemblyName, <span class="cs-keyword" nd="122">string</span>[] args )
{

    <span class="cs-comment" nd="123">//</span><span class="cs-comment" nd="124">// Setup the readers/writers to use the server's streams.  This will allow</span><span class="cs-comment" nd="125">// this process to read and write to the console of the process on the</span><span class="cs-comment" nd="126">// machine from which the command was run.</span><span class="cs-comment" nd="127">//</span>

    TextWriter    stdErr    = AssemblyResolutionServer.StdErr ;
    TextWriter    stdOut    = AssemblyResolutionServer.StdOut ;
    TextReader    stdIn    = AssemblyResolutionServer.StdIn ;

    m_sponsorManager.Register( stdErr );
    m_sponsorManager.Register( stdOut ) ;
    m_sponsorManager.Register( stdIn ) ;

    Console.SetError( <span class="cs-keyword" nd="128">new</span> NoExceptionTextWriter( stdErr ) ) ;
    Console.SetOut( <span class="cs-keyword" nd="129">new</span> NoExceptionTextWriter( stdOut ) ) ;
    Console.SetIn( stdIn ) ;

    <span class="cs-comment" nd="130">//</span><span class="cs-comment" nd="131">// Setup the event handlers for the domain</span><span class="cs-comment" nd="132">//</span>
    AppDomain.CurrentDomain.ResourceResolve 
                += <span class="cs-keyword" nd="133">new</span> ResolveEventHandler( CurrentDomain_ResourceResolve );
    AppDomain.CurrentDomain.AssemblyResolve    
                += <span class="cs-keyword" nd="134">new</span> ResolveEventHandler( CurrentDomain_AssemblyResolve );
    AppDomain.CurrentDomain.DomainUnload    
                += <span class="cs-keyword" nd="135">new</span> EventHandler(CurrentDomain_DomainUnload);

    <span class="cs-comment" nd="136">//</span><span class="cs-comment" nd="137">// Get any required unmanaged dependencies</span><span class="cs-comment" nd="138">//</span>
    RetrieveUnmanagedDependencies() ;

    Assembly    assemblyToExecute = AppDomain.CurrentDomain.Load( 
                                                           assemblyName ) ;

    <span class="cs-keyword" nd="139">object</span>        result          = <span class="cs-keyword" nd="140">null</span> ;

    <span class="cs-comment" nd="141">//</span><span class="cs-comment" nd="142">// Finally, execute the assembly</span><span class="cs-comment" nd="143">//</span><span class="cs-keyword" nd="144">try</span>
    {
        <span class="cs-keyword" nd="145">if</span> ( assemblyToExecute != <span class="cs-keyword" nd="146">null</span> )
        {
            <span class="cs-keyword" nd="147">if</span> ( assemblyToExecute.EntryPoint.GetParameters().Length == <span class="cs-literal" nd="148">0</span> )
            {
                result    =
                    assemblyToExecute.EntryPoint.Invoke( <span class="cs-keyword" nd="149">null</span>, <span class="cs-keyword" nd="150">null</span> ) ;
            }
            <span class="cs-keyword" nd="151">else</span>
            {
                result    =
                    assemblyToExecute.EntryPoint.Invoke( <span class="cs-keyword" nd="152">null</span>,
                                               <span class="cs-keyword" nd="153">new</span><span class="cs-keyword" nd="154">object</span>[]{ args } ) ;
            }

            
        }
    }
    <span class="cs-keyword" nd="155">catch</span>( Exception exp )
    {
        Trace.WriteLine( exp.Message + <span class="cpp-string" nd="156">"\n"</span> + exp.StackTrace, <span class="cpp-string" nd="157">"Error"</span> ) ;

        <span class="cs-keyword" nd="158">if</span> ( exp.InnerException != <span class="cs-keyword" nd="159">null</span> )
        {
            Trace.WriteLine( exp.InnerException.Message + <span class="cpp-string" nd="160">"\n"</span> + 
                            exp.InnerException.StackTrace, <span class="cpp-string">"Error"</span> ) ;
        }
    }
    <span class="cs-keyword">finally</span>
    {
        Console.Error.Flush() ;
        Console.Out.Flush() ;
    }

    <span class="cs-keyword">return</span> result ;
}
</pre>
		<h2>What about console events?</h2>
		<p>Okay, this was a pain in the rear to implement, but console events, generally CTRL-C, is used to interact with the process. When remoting the process, these events need to be remoted as well. There is a background thread on each of the machines that waits for events from the server. The console application traps any console events, passes them in parallel to each remote application, and regenerates those events on the remote machine. This is particularly painful since this is all being run underneath a service. By default, services don't have consoles associated with them. Well, one might claim this is a fairly simple endeavor, simply create a console with <code>AllocConsole()</code> and move on. It just so happens that child processes share the same console as the parent process. In this scenario, the temporary service shares the console with the child process performing the work. If we generate the console event, then it will actually send it to the service as well. So in order to handle all of this, the service itself will not respond to any console events (i.e., CTRL-C, CTRL-BREAK, etc.). This is not a big deal since we don't want the user interacting with this process anyway. None-the-less, you'll see a <code>DoNothithWithConsoleEvent()</code> handler routine that has a fairly self explanatory name.</p>
		<h2>"Confession is the road to healing..." (DC Talk, circa 1994)</h2>
		<p>Now, at this point, some of the dirty laundry must be aired in order for my conscience to feel good. There are obviously some security ramifications of this process as well as the fact that it doesn't handle any PInvoked methods via the loading mechanism. We'll tackle each of these subjects one at a time:</p>
		<h3>Security??!!</h3>
		<p>Security, you say? Well, first I should mention that in order to copy files into the <i>Admin$</i> share of the remote machine, you must be in Administrators group of the remote machine. Therefore, by default, hopefully, not everyone in the world is an Administrator of the remote machine. This is enforced by Windows default NTFS permissions.</p>
		<p>The second security issue is the identity of which the remote process runs. Since the process is running as a service, it is running under the "LOCALSYSTEM" account. I can hear the gaffe all the way to Dublin, CA. Running code under this account can be problematic in the sense that the "NT AUTHORITY\SYSTEM" account can do just about everything on the remote system. If your code does something that you didn't want it to do, you'll have to live with the consequences. I did look into the ability to impersonate the current user on the source machine in the process on the remote machine, and it is all feasible, but beyond my current allotment of time for this article. If you have some time and would like to investigate it, refer to the <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/remsspi.asp" target="_blank">article on MSDN</a>.</p>
		<p>The third and rather more obscure security issue is the fact that there isn't any authentication between the remote and source machine in the sense that the whole process could be liable to a man-in-the-middle attack. As well, there are moderate ways to transparently resolve this but they were beyond the time allotted for this article.</p>
		<p>On the other hand, I have in mind to integrate some 3rd party components to solve this problem such as the <a href="http://www.genuinechannels.com/">Genuine Channels</a> components to handle all the remoting infrastructure. Keep posted for more details.</p>
		<h3>Managing the Unmanaged</h3>
		<p>Gee, doesn't that title sound like something from a management seminar: "I will manage the unmanaged masses!" Anyway, there is a chance that you may have unmanaged method calls in your assembly. These calls will not proxy over the libraries from the source machine, but rather call the library from the local machine. If you're calling one of the system libraries, then this is actually a good thing. However, if you're making a call to a custom or third party unmanaged library, you must make sure it exists on the remote machine first. The only caveat to this is that there is an option to send over the unmanaged DLLs or any necesary files that are needed for your project. First zip them up into a zip file, then on the command line specify the file name with the argument:</p>
		<blockquote style="MARGIN-RIGHT: 0px">
				<p>-unmanaged myFiles.zip</p>
				<p>... Or if your calling this method programatically, specify this file in the ExecRemote command under the "unmanaged" parameter:</p>
				<div class="precollapse" id="premain7" style="WIDTH: 100%">
						<img id="preimg7" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="7" />
						<span id="precollapse7" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="7"> Collapse</span>
				</div>
				<pre lang="cs" id="pre7" style="MARGIN-TOP: 0px">
						<span class="cs-keyword">public</span>
						<span class="cs-keyword">int</span> ExecuteRemotely(
            <span class="cs-keyword">string</span>                      assemblyToRun,
            <span class="cs-keyword">string</span>[]                    argsForMain,
            <span class="cs-keyword">string</span>[]                    machineNames,
            <span class="cs-keyword">int</span>                         port,    
            <span class="cs-keyword">int</span>                         debugLevel,
            <span class="cs-keyword">string</span>                      remoteUserId,
            <span class="cs-keyword">string</span>                      remotePassword,
            <span class="cs-keyword">bool</span>                        interactive,
            TimeSpan                    allowedTime,
            <span class="cs-keyword">int</span>                         numberOfThreads,
            <span class="cs-keyword">string</span>                      unmanagedDependencies,
            <span class="cs-keyword">string</span>                      outputFolder,
            TextWriter                  consoleStdOut,
            TextWriter                  consoleStdErr,
            TextReader                  consoleStdIn,
            BeforeRunOnMachineDelegate  optionalBeforeHandler,
            AfterRunOnMachineDelegate   optionalAfterHandler,
            <span class="cs-keyword">object</span>                      optionalSource )
        </pre>
		</blockquote>
		<h3>Nothin' but .NET (or lack thereof)</h3>
		<p>Lastly, if .NET is not installed on the remote machine, the process will fail. The correct version of the framework must be installed on the remote machine for the code to run. Again, I considered a loader that would detect if the correct version of .NET was installed and install it if it wasn't there; all of which is possible. Yet another thing I didn't exactly have time for :) However, I have in mind a bootstrapper project that will install .NET on the remote machine before the shim process is run. Keep posted for more details.</p>
		<h2>Disclaimer</h2>
		<p>This article and the accompanying code are provided as-is. You may use it as you please. You may <b>not</b> hold me liable for any damage caused to you, your company, your neighbors or anyone else, as a result of reading this article or using the code. Whatever you do with this article and the accompanying code is at your own risk.</p>
		<h2>Version History</h2>
		<p>1.3 - Feb 28th, 2005</p>
		<ul>
				<li>Added ability to send unmanaged code / resources to the remote machine. Basically, you can send legacy DLLs or any other resouce (ie plain old files) to the remote machine. All you need to do is zip up all the resources and supply a zip file on the argument "-unmanaged myfiles.zip" to the command line or to the "unmanaged" argument for ExecRemote() method. 
</li>
				<li>Added ability to pull back any modified files on the remote machine. If you modify any files during the processing of your code, then those files can be pulled back and placed in a directory as the same name of the remote machine. 
</li>
				<li>Added a couple of extra checks to make sure that the current user has Administrative access to the remote machine and the remote resources are cleaned up in the end. Special thanks to the people at ICSharpCode.net for this functionality (<a href="http://www.icsharpcode.net/">http://www.icsharpcode.net/</a>) 
</li>
				<li>Added ability to redirect the output / input of the remote machine to a custom TextWriter / TextReader. A number of people had requested this feature, so I finally added it to the main method. However, by default, all the remote output/input are redirected to/from Console.StdOut, Console.StdIn, Console.StdErr. 
</li>
				<li>Fixed problem of running process on a source machine with multiple IP addresses. When using remoting and TCP, the source machine uses the "first" avilable IP address on that machine. For example, if you have two IP addresses "192.168.0.14" and "10.11.0.5", and your network is generally running on the 10.x.x.x network, the Uri for the remoting object is actually given out as tcp://192.168.0.14/YourRemoteObject.rem. I had a hard time believing this until I actually saw it in the debugger, but you can check out the article at <a href="http://www.glacialcomponents.com/ArticleDetail/CAOMN.aspx">http://www.glacialcomponents.com/ArticleDetail/CAOMN.aspx</a> for more details.</li>
		</ul>
		<p>1.2 - Jan 22nd, 2005</p>
		<ul>
				<li>Added ability to use a .NET configuration file that will be remoted (ie YourApp.exe.config will get proxied over) 
</li>
				<li>Added ability to use a license file (ie license.txt) 
</li>
				<li>Fixed problem with launching a program that defined main as Main() instead of Main( string[] args ). It will now function either way. 
</li>
				<li>All user assembly code is now loaded in a sepate AppDomain in the child process</li>
		</ul>
		<p>1.1 - Dec 29<sup>th</sup>, 2004</p>
		<ul>
				<li>Added usage for '-interactive' to allow application to interact with the desktop. 
</li>
				<li>Added the ability to issue a CTRL-C, CTRL-BREAK, etc. which will be echoed to all the remote applications. 
</li>
				<li>Added ability to use wildcards for the machine names(s) (e.g., <i>\\testwin*</i> will match <i>\\testwin2k</i> and <i>\\testwinxp</i> via LDAP lookup). 
</li>
				<li>Added ability to timeout on the remote application. 
</li>
				<li>Created a more useful demonstration application, <i>quickdir.exe</i>. This application will basically do a "dir" on the remote machine. </li>
		</ul>
		<p>1.0 - Dec 10<sup>th</sup>, 2004</p>
		<ul>
				<li>Initial version. </li>
		</ul>
		<h2>Special Recognition</h2>
		<p>I would like to thank <a href="mailto:mark@sysinternals.com">Mark Russinovich</a> for his article on <a href="http://www.sysinternals.com/ntw2k/freeware/psexec.shtml" target="_blank">Psexec</a> which inspired me to write this article. As well, I'd like to thank Suzanne Cook for her <a href="http://blogs.msdn.com/suzcook" target="_blank">blogs</a> on the .NET runtime loader which was a good reference for some very difficult issues. Lastly, but not least, I'd like to thank everyone who contributes to <a href="http://pinvoke.net/" target="_blank">PInvoke.net</a> from which I get most of my PInvoke method definitions (such as those to create services on Windows).</p>
		<h2>In Memory of Don Langewisch</h2>
		<p>I'd like to humbly dedicate this article to the memory of Don Langewisch (1955? - Dec. 10<sup>th</sup>, 2004). I received the news of his passing while finalizing this article. Don, a loving and dedicated man of God leaves behind a wife, two daughters and a son.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>About Jim Wiese (aka Spunk)</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">I generally attend most of the Microsoft DevDays in the south bay area (CA) and BayArea.NET functions in case any of you attend those as well. I'm always up for a lively disucussion about new technologies in the industry, Microsoft or not. Send me a note if you attend! 
<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=53069">here</a> to view Jim Wiese (aka Spunk)'s online profile.</p></td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 10:55 <a href="http://www.cnitblog.com/MartinYao/articles/21439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Server 2003 Feature Packs</title><link>http://www.cnitblog.com/MartinYao/articles/21428.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 02 Jan 2007 13:16:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21428.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21428.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21428.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21428.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21428.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td class="partition" width="67%" colspan="5">
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=9688F8B9-1034-4EF6-A3E5-2A2A57B5C8E4&amp;displaylang=en">Active Directory Application Mode</a>
																				<br />
																		</span>
																		<p class="blurb">Active Directory Application Mode (ADAM) is now available for download. Organizations, independent software vendors, and developers who want to integrate their applications with a directory service now have an additional capability within Active Directory that provides numerous benefits.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/windowsserver2003/technologies/management/ads/default.mspx">Automated Deployment Services</a>
																				<br />
																		</span>
																		<p class="blurb">With Automated Deployment Services (ADS), Microsoft is extending the Windows platform to enable faster, more flexible server deployment. Find out how ADS can help you streamline this process.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://download.microsoft.com/download/7/8/D/78DB44EF-1DB7-4D19-AB75-7455E3983FE8/DSfW.msi">DSML Services for Windows</a>
																				<br />
																		</span>
																		<p class="blurb">DSML Services for Windows (DSFW) allows Active Directory access using SOAP over HTTP based on the OASIS DSML v2 specification.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=0A6D4C24-8CBD-4B35-9272-DD3CBFC81887&amp;displaylang=en">Group Policy Management Console</a>
																				<br />
																		</span>
																		<p class="blurb">The Microsoft Group Policy Management Console (GPMC) is a new tool that unifies management of Group Policy across the enterprise. The GPMC consists of a new MMC snap-in and a set of programmable interfaces for managing Group Policy.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=d9143610-c04d-41c4-b7ea-6f56819769d5&amp;DisplayLang=en">Identity Integration Feature Pack</a>
																				<br />
																		</span>
																		<p class="blurb">The Identity Integration Feature Pack for Microsoft Windows Server Active Directory manages identities and coordinates user details across Active Directory, Active Directory Application Mode (ADAM), Microsoft Exchange 2000 Server, and Exchange Server 2003 implementations.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/windowsserversystem/virtualserver/evaluation/linuxguestsupport/default.mspx">Linux Guest Support for Virtual Server 2005 R2</a>
																				<br />
																		</span>
																		<p class="blurb">Download the Virtual Machine Additions for Linux, available from Microsoft Connect, to improve the experience of running qualified Linux distributions on Virtual Server 2003 R2.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=0a91d2e7-7594-4abb-8239-7a7eca6a6cb1&amp;DisplayLang=en">Remote Control Add-on for Active Directory Users and Computers</a>
																				<br />
																		</span>
																		<p class="blurb">Remote Control is a small add-on that adds the option to right-click a computer account in the Active Directory MMC and choose Remote Control on that computer by opening a Terminal/Remote Desktop connection to that computer.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=E382358F-33C3-4DE7-ACD8-A33AC92D295E">Shadow Copy Client</a>
																				<br />
																		</span>
																		<p class="blurb">For computers running a version of Windows earlier than Windows Server 2003, you can download the Shadow Copy Client to take advantage of the intelligent file storage capabilities of the Shadow Copies of Shared Folders feature.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/technet/downloads/winsrvr/featurepacks/wsus/default.mspx">Windows Server Update Services</a>
																				<br />
																		</span>
																		<p class="blurb">Windows Server Update Services with Service Pack 1 (WSUS SP1) delivers updates for WSUS. This service pack includes support for Microsoft SQL Server 2005 and the forthcoming Microsoft Windows Vista operating system, and provides additional stability and performance improvements.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/windowsserversystem/virtualserver/software/default.mspx">Virtual Server 2005 R2: Now Available as a Free Download</a>
																				<br />
																		</span>
																		<p class="blurb">Learn how you can use Virtual Server 2005 R2, now available as a free download, to increase hardware utilization and enable faster deployments of new servers.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=8EF6D80A-6A9C-4FB9-AB51-790980816FFE&amp;displaylang=en">Windows Rights Management Services</a>
																				<br />
																		</span>
																		<p class="blurb">Microsoft Windows Rights Management Services (RMS) for Windows Server 2003 is information protection technology that works with RMS-enabled applications to help safeguard digital information from unauthorized use—both online and offline, inside and outside of the firewall.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://go.microsoft.com/fwlink/?LinkID=18134">Windows Rights Management Services Client</a>
																				<br />
																		</span>
																		<p class="blurb">In order to run applications that provide functionality based on Windows RMS technologies and to develop with the RMS SDK, download the Windows RMS client.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/windows/sfu/">Windows Services For UNIX</a>
																				<br />
																		</span>
																		<p class="blurb">Windows Services for UNIX (SFU) 3.5 provides the tools and environment that IT professionals and developers need to integrate Windows with UNIX and Linux environments.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/windowsserver2003/technologies/sharepoint/default.mspx">Windows SharePoint Services</a>
																				<br />
																		</span>
																		<p class="blurb">Windows SharePoint Services is now available to help teams start creating Web sites for their information sharing and document collaboration needs.</p>
																</td>
														</tr>
												</tbody>
										</table>
										<table class="headline" cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr valign="top">
																<td class="bullet">•</td>
																<td>
																		<span class="label">
																				<a href="http://www.microsoft.com/technet/downloads/winsrvr/wsrm.mspx">Windows System Resource Manager</a>
																				<br />
																		</span>
																		<p class="blurb">Windows System Resource Manager (WSRM) is available for use with Windows Server 2003, Enterprise Edition and Datacenter Edition. WSRM provides resource management and enables the allocation of resources among multiple applications based on business priorities.</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21428.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-02 21:16 <a href="http://www.cnitblog.com/MartinYao/articles/21428.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>