Archive for the 'Csharp' Category
如果要想获得远程的地址,需要用sendarp这个函数来实现。具体的代码如下:
[DllImport("Iphlpapi.dll")]
private static unsafe extern int SendARP(Int32 dest,Int32 host,ref IntPtr mac,ref IntPtr length);
[DllImport("Ws2_32.dll")]
private static extern Int32 inet_addr(string ip);
Int32 ldest= inet_addr(“157.60.68.163″);//目的地的ip
Int32 lhost= inet_addr(“157.60.68.33″);//本地的ip
try
{
Byte[] macinfo=new Byte[6];
Int32 length=6;
IntPtr mac=new IntPtr(macinfo[0]);
IntPtr len=new IntPtr(6);
int ii=SendARP(ldest,lhost, ref mac, ref len);
Console.WriteLine(“Mac Add:”+mac);
Console.WriteLine(“length:”+len);
}
catch(Exception err)
{
Console.WriteLine(err);
}
SQL Server提供了一个特别的数据类型:image,它是一个包含binary数据的类型。下边这个例子就向你展示了如何将文本或照片放入到数据库中的办法。在这篇文章中我们要看到如何在SQL Server中存储和读取图片。
1、建立一个表:
在SQL SERVER中建立这样结构的一个表:
| 列名 |
类型 |
目的 |
| ID |
Integer |
主键ID |
| IMGTITLE |
Varchar(50) |
图片的标题 |
| IMGTYPE |
Varchar(50) |
图片类型. ASP.NET要以辨认的类型 |
| IMGDATA |
Image |
用于存储二进制数据 |
2、存储图片到SQL SERVER数据库中
为了能存储到表中,你首先要上传它们到你的WEB 服务器上,你可以开发一个web form,它用来将客户端中TextBox web control中的图片入到你的WEB服务器上来。将你的 encType 属性设置为:myltipart/formdata.
Stream imgdatastream = File1.PostedFile.InputStream; int imgdatalen = File1.PostedFile.ContentLength; string imgtype = File1.PostedFile.ContentType; string imgtitle = TextBox1.Text; byte[] imgdata = new byte[imgdatalen]; int n = imgdatastream.Read(imgdata,0,imgdatalen); string connstr=((NameValueCollection)Context.GetConfig(“appSettings”))["connstr"];
SqlConnection connection = new SqlConnection(connstr);
SqlCommand command = new SqlCommand (“INSERT INTO ImageStore(imgtitle,imgtype,imgdata) VALUES ( @imgtitle, @imgtype,@imgdata )”, connection );
SqlParameter paramTitle = new SqlParameter (“@imgtitle”, SqlDbType.VarChar,50 );
paramTitle.Value = imgtitle; command.Parameters.Add( paramTitle);
SqlParameter paramData = new SqlParameter( “@imgdata”, SqlDbType.Image ); paramData.Value = imgdata; command.Parameters.Add( paramData );
SqlParameter paramType = new SqlParameter( “@imgtype”, SqlDbType.VarChar,50 ); paramType.Value = imgtype; command.Parameters.Add( paramType );
connection.Open(); int numRowsAffected = command.ExecuteNonQuery(); connection.Close(); |
3、从数据库中恢复读取
现在让我们来从SQL Server中读取我们放入的数据吧!我们将要输出图片到你的浏览器上,你也可以将它存放到你要的位置。
private void Page_Load(object sender, System.EventArgs e) { string imgid =Request.QueryString["imgid"]; string connstr=((NameValueCollection) Context.GetConfig(“appSettings”))["connstr"]; string sql=”SELECT imgdata, imgtype FROM ImageStore WHERE id = ” + imgid; SqlConnection connection = new SqlConnection(connstr); SqlCommand command = new SqlCommand(sql, connection); connection.Open(); SqlDataReader dr = command.ExecuteReader(); if(dr.Read()) { Response.ContentType = dr["imgtype"].ToString(); Response.BinaryWrite( (byte[]) dr["imgdata"] ); } connection.Close(); } |
要注意的是Response.BinaryWrite 而不是Response.Write.
下面给大家一个用于C# Winform的存入、读取程序。其中不同请大家自己比较!(为了方便起见,我将数据库字段简化为二个:imgtitle和imgdata。
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Data.SqlClient;
namespace WindowsApplication21
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
private string ConnectionString = “Integrated Security=SSPI;Initial Catalog=;Data Source=localhost;”;
private SqlConnection conn = null;
private SqlCommand cmd = null;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.PictureBox pic1;
private System.Windows.Forms.OpenFileDialog openFileDialog1;
private string sql = null;
private System.Windows.Forms.Label label2;
private string nowId=null;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
conn = new SqlConnection(ConnectionString);
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if (conn.State == ConnectionState.Open)
conn.Close();
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// 设计器支持所需的方法 – 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.pic1 = new System.Windows.Forms.PictureBox();
this.button2 = new System.Windows.Forms.Button();
this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(0, 40);
this.button1.Name = “button1″;
this.button1.Size = new System.Drawing.Size(264, 48);
this.button1.TabIndex = 0;
this.button1.Text = “加入新的图片”;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// pic1
//
this.pic1.Location = new System.Drawing.Point(280, 8);
this.pic1.Name = “pic1″;
this.pic1.Size = new System.Drawing.Size(344, 264);
this.pic1.TabIndex = 3;
this.pic1.TabStop = false;
//
// button2
//
this.button2.Location = new System.Drawing.Point(0, 104);
this.button2.Name = “button2″;
this.button2.Size = new System.Drawing.Size(264, 40);
this.button2.TabIndex = 4;
this.button2.Text = “从数据库中恢复图像”;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// openFileDialog1
//
this.openFileDialog1.Filter = “\”图像文件(*.jpg,*.bmp,*.gif)|*.jpg|*.bmp|*.gif\”";
//
// label2
//
this.label2.Location = new System.Drawing.Point(0, 152);
this.label2.Name = “label2″;
this.label2.Size = new System.Drawing.Size(264, 48);
this.label2.TabIndex = 5;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(632, 273);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.label2,
this.button2,
this.pic1,
this.button1});
this.Name = “Form1″;
this.Text = “Form1″;
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void button1_Click(object sender, System.EventArgs e)
{
openFileDialog1.ShowDialog ();
if (openFileDialog1.FileName.Trim()!=”")
{
FileInfo fi = new FileInfo(openFileDialog1.FileName);
string imgtitle=openFileDialog1.FileName;
int imgdatalen=(int)fi.Length;
byte[] imgdata = new byte[imgdatalen];
Stream imgdatastream=fi.OpenRead();
int n=imgdatastream.Read(imgdata,0,imgdatalen);
if( conn.State == ConnectionState.Open)
conn.Close();
ConnectionString =”Integrated Security=SSPI;” + “Initial Catalog=mydb;” +”Data Source=localhost;”;
conn.ConnectionString = ConnectionString;
try
{
string mySelectQuery = “INSERT INTO ImageStore(imgtitle,imgdata) VALUES (@imgtitle, @imgdata )”;
//string mySelectQuery=”UPDATE ImageStore set imgtitle=@imgtitle,imgdata=@imgdata” ;
SqlCommand myCommand = new SqlCommand(mySelectQuery, conn);
SqlParameter paramTitle = new SqlParameter(“@imgtitle”, SqlDbType.VarChar,50 );
paramTitle.Value = imgtitle;
myCommand.Parameters.Add( paramTitle);
SqlParameter paramData = new SqlParameter( “@imgdata”, SqlDbType.Image );
paramData.Value = imgdata;
myCommand.Parameters.Add( paramData );
conn.Open();
int numRowsAffected = myCommand.ExecuteNonQuery();
conn.Close();
}
catch(Exception err)
{
MessageBox.Show(“您输入名称可能在数据库中已存在或输入为空,请检查!”+err.ToString() );
}
finally
{}
}
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void button2_Click(object sender, System.EventArgs e)
{
//打开数据库连接
if( conn.State == ConnectionState.Open)
conn.Close();
ConnectionString =”Integrated Security=SSPI;” + “Initial Catalog=mydb;” +”Data Source=localhost;”;
conn.ConnectionString = ConnectionString;
// 创建数据适配器
string sql=”SELECT * FROM ImageStore” ;
SqlCommand command = new SqlCommand(sql, conn);
try
{conn.Open();}
catch(Exception newerr)
{
MessageBox.Show(” 不能打开数据联接!”) ;
}
finally
{}
SqlDataReader dr = command.ExecuteReader();
if(dr.Read())
{
FileInfo fi = new FileInfo(“temp”);
FileStream myStream=fi.Open(FileMode.Create);
byte[] mydata=((byte[])dr["imgdata"]);
//label2.Text=”您现在看到的是:”+ dr["imgtitle"].ToString();
foreach(byte a in mydata)
{
myStream.WriteByte(a);
}
myStream.Close();
Image myImage=Image.FromFile(“temp”) ;
pic1.Image=myImage;
pic1.Refresh();
dr.Close ();
}
else
{
MessageBox.Show(“没有成功读入数据!”) ;
}
conn.Close();
}
}
}
原稿CSDN网友 maling(***) (
)
// 输出硬盘文件,提供下载
// 输入参数 _Request: Page.Request对象, _Response: Page.Response对象, _fileName: 下载文件名, _fullPath: 带文件名下载路径, _speed 每秒允许下载的字节数
// 返回是否成功
public static bool ResponseFile(HttpRequest _Request,HttpResponse _Response,string _fileName,string _fullPath, long _speed)
{
try
{
FileStream myFile = new FileStream(_fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader br = new BinaryReader(myFile);
try
{
_Response.AddHeader(“Accept-Ranges”, “bytes”);
_Response.Buffer = false;
long fileLength = myFile.Length;
long startBytes = 0;
int pack = 10240; //10K bytes
//int sleep = 200; //每秒5次 即5*10K bytes每秒
int sleep = (int)Math.Floor(1000 * pack / _speed) + 1;
if (_Request.Headers["Range"] != null)
{
_Response.StatusCode = 206;
string[] range = _Request.Headers["Range"].Split(new char[] {‘=’, ‘-’});
startBytes = Convert.ToInt64(range[1]);
}
_Response.AddHeader(“Content-Length”, (fileLength – startBytes).ToString());
if (startBytes != 0)
{
_Response.AddHeader(“Content-Range”, string.Format(” bytes {0}-{1}/{2}”, startBytes, fileLength-1, fileLength));
}
_Response.AddHeader(“Connection”, “Keep-Alive”);
_Response.ContentType = “application/octet-stream”;
_Response.AddHeader(“Content-Disposition”,”attachment;filename=”
+ HttpUtility.UrlEncode(_fileName,System.Text.Encoding.UTF8) );
br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
int maxCount = (int) Math.Floor((fileLength – startBytes) / pack) + 1;
for (int i = 0; i < maxCount; i++)
{
if (_Response.IsClientConnected)
{
_Response.BinaryWrite(br.ReadBytes(pack));
Thread.Sleep(sleep);
}
else
{
i=maxCount;
}
}
}
catch
{
return false;
}
finally
{
br.Close();
myFile.Close();
}
}
catch
{
return false;
}
return true;
}
调用例
Page.Response.Clear();
bool success = ResponseFile(Page.Request, Page.Response, “filename”, @”C:\download.date”, 1024000);
if(!success)
Response.Write(“下载文件出错!”);
Page.Response.End();
线程是允许进行并行计算的一个抽象概念:在另一个线程完成计算任务的同时,一个线程可以对图像进行更新,二个线程可以同时处理同一个进程发出的二个网络请求。我们在这篇文章中将重点讨论Java和C#在线程方面的不同之处,并将一些Java中线程的常用模式转换为C#。
从
概念上讲,线程提供了一种在一个软件中并行执行代码的方式━━每个线程都“同时”在一个共享的内存空间中执行指令,(当然是在一个处理器上,这是通过处于
运行状态的线程的交替执行完成的。),因此,每个线程都可以访问一个程序内的数据结构。由于这种原因,多线程编程的难度就可想而知了,因为一个程序内有许
多不同的线程需要安全地共享数据。
线程的创建和运行
Java在java.lang.Thread和
java.lang.Runnable类中提供了大部分的线程功能。创建一个线程非常简单,就是扩展Thread类,并调用start()。通过创建一个
执行Runnable()的类,并将该类作为参数传递给Thread(),也可以定义一个线程。仔细地阅读下面这个简单的Java程序,其中有2个线程同
时在从1数到5,并将结果打印出来。
public class ThreadingExample
extends Object {
public static void main( String args[] ) {
Thread[] threads = new Thread[2];
for( int count=1;count<=threads.length;count++ ) {
threads[count] = new Thread( new Runnable() {
public void run() {
count();
}
} );
threads[count].start();
}
}
public static void count() {
for( int count=1;count<=5;count++ )
System.out.print( count + ” ” );
}
}
我们可以使用System.Threading.Thread和System.Threading.ThreadStart二个类将上述的Java程序转换为C#语言:
using System.Threading;
public class ThreadingExample : Object {
public static void Main() {
Thread[] threads = new Thread[2];
for( int count=1;count<=threads.Length;count++ ) {
threads[count] = new Thread( new ThreadStart( Count ) );
threads[count].Start();
}
}
public static void Count() {
for( int count=1;count<=5;count++ )
Console.Write( count + ” ” );
}
}
这
个例子中有一些小技巧。Java允许扩展java.lang.Thread类和执行java.lang.Runnable接口,C#则没有为我们提供这些
便利。一个C#中的Thread对象是不可知的,必须通过ThreadStart进行创建,这意味着不能使用内部的类模式,而必须创建一个对象,而且必须
传递给线程一个对象的方法供线程执行用。
线程的使用
Java中存在许多编程人员希望能够对线程使用的标准操作:例如,测试线程是否存在、加入一个线程直到它死亡、杀死一个线程等。
表1:线程管理的函数
Java中java.lang.Thread中的方法和C#中System.Threading.Thread对象的对比。
setDaemon( boolean on) 方法
IsBackground 设置属性值
使一个存在的进程成为一个新线程(如果剩下的所有进程都成了新线程,程序将停止运行)。
isDaemon()方法
IsBackground 获取属性
如果该线程是一个后台线程,则返回真值。
isAlive() 方法
IsAlive 获取属性
如果该线程处于活动状态,则返回真值。
interrupt() 方法
Interrupt() 方法
尽管在Java中这一方法可以用来设置线程的中断状态,而且可以用来检查线程是否被中断。在C#中没有相应的方法,对一个没有处于阻塞状态的线程执行Interrupt方法将使下一次阻塞调用自动失效。
isInterrupted() 方法
n/a
如果该线程处于阻塞状态,则返回真值。
sleep( long millis )和sleep( long millis, int nanos )
Sleep( int millisecondTimeout ) and Sleep( System.TimeSpan )方法
使
正在执行的线程暂停一段给定的时间,或直到它被中断。这一方法将在Java中将产生一个java.lang.InterruptedException状
态,在C#中将产生System.Threading. ThreadInterruptedException状态。
join()、join( long millis )和join( long millis, int nanos ) 方法
Join
()、Join( int millisecondTimeout )和Join( System.TimeSpan ) 方法
与Java中仅依靠超时设定不同的是,在C#语言中则依据线程停止运行是由于线程死亡(返回真)或是超时(返回假)而返回一个布尔型变量。
suspend() 方法
Suspend() 方法
二者的功能相同。这一方法容易引起死循环,如果一个占有系统关健资源的线程被挂起来,则在这一线程恢复运行之前,其他的线程不能访问该资源。
resume() 方法
Resume() 方法
恢复一个被挂起的线程。
stop() 方法
Abort() 方法
参见下面的“线程停止”部分。
(特别说明,在上面的表中,每个小节的第一行是java中的方法,第二行是C#中的方法,第三行是有关的注释,由于在文本文件中不能组织表格,请编辑多费点心组织表格,原文中有表格的格式。)
线程的中止
由
于能够在没有任何征兆的情况下使运行的程序进入一种混乱的状态,Java中的Thread.stop受到了普遍的反对。根据所调用的stop()方法,一
个未经检查的java.lang.ThreadDeath错误将会破坏正在运行着的程序的栈,随着它的不断运行,能够解除任何被锁定的对象。由于这些锁被
不分青红皂白地被打开,由它们所保护的数据就非常可能陷入混乱状态中。
根据当前的Java文档,推荐的中止一个线程的方法是让运行的线程检查一个由其他的线程能够改变的变量,该变量代表一个“死亡时间”条件。下面的程序就演示了这种方法。
// 条件变量
private boolean timeToDie = false;
// 在每次迭代中对条件变量进行检查。
class StoppableRunnable
extends Runnable {
public void run() {
while( !timeToDie ) {
// 进行相应的操作
}
}
}
上
述的讨论对C#中的Abort方法也适合。根据调用的Abort方法,令人捉摸不定的
System.Threading.ThreadAbortException可能会破坏线程的栈,它可能释放线程保持的一些变量,使处于保护状态中的数
据结构出现不可预测的错误。我建议使用与上面所示的相似的方法来通知一个应该死亡的线程。
线程的同步
从概念上来看,线程非常易于理
解,实际上,由于他们可能交互地对同一数据结构进行操作,因此它们成为了令编程人员头疼的一种东西。以本文开始的ThreadingExample为例,
当它运行时,会在控制台上输出多种不同的结果。从 1 2 3 4 5 1 2 3 4 5到 1 1 2 2 3 3 4 4 5 5或 1 2 1
2 3 3 4 5 4
5在内的各种情况都是可能出现的,输出结果可能与操作系统的线程调度方式之间的差别有关。有时,需要确保只有一个线程能够访问一个给定的数据结构,以保证
数据结构的稳定,这也是我们需要线程同步机制的原因所在。
为了保证数据结构的稳定,我们必须通过使用“锁”来调整二个线程的操作顺序。二种语
言都通过对引用的对象申请一个“锁”,一旦一段程序获得该“锁”的控制权后,就可以保证只有它获得了这个“锁”,能够对该对象进行操作。同样,利用这种
锁,一个线程可以一直处于等待状态,直到有能够唤醒它信号通过变量传来为止。
表2:线程同步
需要对线程进行同步时需要掌握的关健字
synchronized
lock
C#中的lock命令实际上是为使用System.Threading.Monitor类中的Enter和Exit方法的语法上的准备
Object.wait()
Monitor.Wait( object obj )
C#中没有等待对象的方法,如果要等待一个信号,则需要使用System.Threading.Monitor类,这二个方法都需要在同步的程序段内执行。
Object.notify()
Monitor.Pulse( object obj )
参见上面的Monitor.Wait的注释。
Object.notify()
Monitor.PulseAll( object obj )
参见上面的Monitor.Wait的注释。
(特别说明,在上面的表中,每个小节的第一行是java中的方法,第二行是C#中的方法,第三行是有关的注释,由于在文本文件中不能组织表格,请编辑多费点心组织表格,原文中有表格的格式。)
我们可以对上面的例子进行一些适当的修改,通过首先添加一个进行同步的变量,然后对count()方法进行如下的修改,使变量在“锁”中被执行加1操作。
public static Object synchronizeVariable = “locking variable”;
public static void count() {
synchronized( synchronizeVariable ) {
for( int count=1;count<=5;count++ ) {
System.out.print( count + ” ” );
synchronizeVariable.notifyAll();
if( count < 5 )
try {
synchronizeVariable.wait();
} catch( InterruptedException error ) {
}
}
}
}
作
了上述的改变后,每次只有一个线程(因为一次只能有一个线程获得synchronizeVariable)能够执行for
loop循环输出数字1;然后,它会唤醒所有等待synchronizeVariable的线程(尽管现在还没有线程处于等待状态。),并试图获得被锁着
的变量,然后等待再次获得锁变量;下一个线程就可以开始执行for
loop循环输出数字1,调用notifyAll()唤醒前面的线程,并使它开始试图获得synchronizeVariable变量,使自己处于等待状
态,释放synchronizeVariable,允许前面的线程获得它。这个循环将一直进行下去,直到它们都输出完从1到5的数字。
通过一些简单的语法变化可以将上述的修改在C#中实现:
public static Object synchronizeVariable = “locking variable”;
public static void count() {
lock( synchronizeVariable ) {
for( int count=1;count<=5;count++ ) {
System.out.print( count + ” ” );
Monitor.PulseAll( synchronizeVariable );
if( count < 5 )
Monitor.Wait( synchronizeVariable );
}
}
}
C#中特有的线程功能
象我们一直对C#所抱的期望那样,C#中确实有一些Java不支持的方法、类和函数,对于铁杆的Java线程编程人员而言,这可是一件好事,因为他们可以用C#编写代码,然后在Java代码中引用。
Enter/TryEnter/Exit
要
在Java中获得某一变量的锁,必须在代码的首尾二端加上synchronized关健字,指明需要获得锁的对象。一旦线程开始执行
synchronized块中的代码,它就获得了对这一对象的锁的控制权。同样,一旦线程已经离开了synchronized块,它也将释放这一对象的
锁。我们已经知道,C#也有一个相似的被称作lock的关健字。除了lock这个关健字外,C#还提供了内置的获得和释放锁的方法:
Monitor.Enter( object obj )和 Monitor.Exit( object obj
),通过使用这些方法,编程人员可以获得与使用lock相同的作用,但提供了更精确的控制方法。例如,可以在一个方法中锁定几个变量,而不同时或在代码中
的不同部分释放它们。
对一个需要进行同步的对象执行System.Threading.Monitor.Enter操作将使线程获得该对象的
锁,或者在由其他线程控制着该对象的锁时进行阻塞。通过执行Monitor.Exit方法就可以释放锁,如果线程已经不控制着该对象的锁了,这一方法将会
产生一个System.Threading.SynchronizationLockException异常信号。
C#中的Monitor类不但包括Enter方法,还包括TryEnter方法,如果执行该方法,就会或者获得一个锁,或者返回一个表明它不能获得锁的返回值。
原子操作
System.Threading.Interlocked类提供了程序对由几个线程共享的变量进行同步访问的能力,C#把一些操作抽象为“原子”操作或“不可分割”的操作。为了说明这一问题是如何解决的,我们来看一下下面的Java代码:
public static int x = 1;
public static void increment() {
x = x + 1;
}
如果有二个不同的线程同时调用increment(),x最后的值可能是2或3,发生这种情况的原因可能是二个进程无序地访问x变量,在没有将x置初值时对它执行加1操作;在任一线程有机会对x执行加1操作之前,二个线程都可能将x读作1,并将它设置为新的值。
在Java
和C#中,我们都可以实现对x变量的同步访问,所有进程都可以按各自的方式运行。但通过使用Interlocked类,C#提供了一个对这一问题更彻底的
解决方案。Interlocked类有一些方法,例如Increment( ref int location )、Decrement( ref
int location
),这二个方法都取得整数型参数,对该整数执行加或减1操作,并返回新的值,所有这些操作都以“不可分割的”方式进行,这样就无需单独创建一个可以进行同
步操作的对象,如下例所示:
public static Object locker = …
public static int x = 1;
public static void increment() {
synchronized( locker ) {
x = x + 1;
}
}
C#中的Interlocked类可以用下面的代码完成相同的操作:
public static int x = 1;
public static void Increment() {
Interlocked.Increment( ref x );
}
Interlocked中还包括一个名字为Exchange的方法,可以“不可分割”地将一个变量的值设置为另一个变量的值。
线程池
如
果许多利用了线程的应用软件都创建线程,这些线程将会因等待某些条件(键盘或新的I/O输入等)而在等待状态中浪费大部分的时间,C#提供的
System.Threading.ThreadPool对象可以解决这一问题。使用ThreadPool和事件驱动的编程机制,程序可以注册一个
System.Threading.WaitHandle对象(WaitHandle是C#编程中等待和通知机制的对象模型。)和
System.Threading.WaitOrTimerCallback对象,所有的线程无需自己等待WaitHandle的释放,
ThreadPool将监控所有向它注册的WaitHandle,然后在WaitHandle被释放后调用相应WaitOrTimerCallback对
象的方法。
结束语
在本篇文章中我们简单地讨论了C#提供的用于线程和并行操作的机制,其中的大部分与Java相似━━C#提供了可
以运行提供的方法的Thread对象,同时提供了对代码访问进行同步的方法。与在其他方面一样,C#在线程方面也提供了一些Java不支持的语法(在一定
程度上,揭示了同步操作的一些底层的内容。),Java编程人员可能会发现这一部分非常有用。
>>原作者:刘彦青 >>来源:计算机世界
有人说web程序员不算是真正的程序员,刚听到这句话的时候很气愤,但仔细想想,这话还是很有道理的。可以说,大部分的web程序员不能算是真正的程序员,因为他们的大部分注意力在实现功能上,而对一些程序员必须要掌握的东西丝毫不在意。可以这么说,还不会爬就想跑了。
可能你不会同意上面的话,但问一下自己,除了改改例子实现功能以外,你对一些基本的东西有多少了解?先不说那些复杂的诸如面向对象一类的东西,我们就说说简单的排错、纠错吧,你做了多少?
想
想看,作为程序员恐怕每天大多数的时间是在debug,但究竟有多少人真正掌握合理的、科学的去debug呢?以前的web编程语言象
asp/php/cgi等关于debug的功能很弱,但现在的c#及java提供了丰富的debug手段,但你用了多少呢?你可能对
System.Data.SqlClient的每个类、每个方法、每个属性都了如指掌,但你对System.Diagnostics了解多少呢?
现代的编程语言如c++ , java , c#等都十分重视对错误的防止、处理,在这儿我就讲一下在c#里的排错、纠错,希望大家能从中学到一些有用的东西,希望以后不会再听到文章开头那句话。
debug
最理想的状态是什么?这个不用我说,那就是defect
free,没有bug,呵呵。但早有人说了,没有bug那还叫程序吗?win2000还60000多个bug呢。所以我们要做到的是尽量防止bug,
bug出现后能迅速定位问题所在,修正这个bug。.net提供了很丰富的debug手段,除了一些debug相关的nampespace,c#语言本身
也有相关的内容存在。常用的有条件编译、try/catch、trace以及断言(Assert)等,如果你能熟练掌握这些手段,综合运用,那么
debug将不再是一场恶梦,也不会像现在这样出现一点儿问题就满论坛追着人问:“我这儿又出错了,为什么呀?”。下面我将分别讲一下这些手段的运用。
一、捕捉异常(try / catch /finally)
这个我不用说,大家都清楚它的作用,就是捕捉程序中所有可能导致错误的异常,然后加入自己的处理措施,并且使程序继续运行,而如果不捕捉异常的话,程序将会终止,简单的把错误信息发送给客户。
所以,在进行所有可能出现错误的操作时都应该捕捉异常,象下面这个例子,捕捉数据库操作可能出现的异常。
/// <summary>
/// 取得数据库连接
/// </summary>
/// <param name=”a_strDatabase”>数据库名</param>
/// <param name=”oa_objConnection”>输出参数,空数据库连接</param>
public void GetConnection(string a_strDatabase , out SqlConnection oa_objConnection)
{
oa_objConnection = null ;
string strConnStr = “”;
try
{
strConnStr = “server=” + m_objIni.GetProperty(“server”) + “;uid=”
+ m_objIni.GetProperty(“uid”) + “;pwd=” + m_objIni.GetProperty(“password”)
+ “;database=” + a_strDatabase ;
oa_objConnection = new SqlConnection(strConnStr) ;
oa_objConnection.Open() ;
//log it
m_objLog.Write(“数据库连接ok”) ;
}
catch(SqlException e)
{
//log it
m_objLog.Write(“数据库连接出错” , e) ;
#if DEBUG
Console.WriteLine(e.ToString()) ;
#endif//DEBUG
throw(e) ;
}
}
}//end class
二、条件编译
java
不提供条件编译,这是我觉得java不好的一个原因之一,所以在写java时都是自己写一个类来实现条件编译。那么,什么是条件编译呢?就是当符合某一条
件时编译,不符合时就不编译,这就方便了debug。我们经常遇到这种情况,在某一过程或方法里我们想要知道某个变量的值,比较常用的方法是在页面或控制
台输出这个变量的值,已确定是否是自己希望的值,但如果没有条件编译的话,但当你发布发行版本时需要手工删掉这些输出语句,费时、费力,并且容易出错,而
如果有条件编译,那就方便多了。看下面这个例子:
/// <summary>
/// 初始化
/// </summary>
private void Initialize()
{
try
{
m_objConnManager = new ConnManager(m_strIniFilePath , “./config/newsdata.ini”) ;
log = new Log(“./logs/newserver.log”) ;
}
catch(Exception e)
{
#if DEBUG
Console.WriteLine(“初始化” + e.Message) ;
#endif//DEBUG
throw(new Exception(“初始化” + e.Message)) ;
}
}
注意到其中的#if DEBUG那几句吗?它的作用就是当DEBUG时,在控制台输出异常信息,以便你马上知道出现什么错误,而当不是DEBUG时,那句就不会被编译。
三、断言(Assert)
断
言真是一个值得大书特书的好东西,但可惜的是80%的程序员尤其是web程序员不用它,甚至根本就没听说过。很难给断言下一个定义,如果要详细说它的好
处,简直都可以写一本书了。简单地说,断言就是在应该是正确的地方加一个判断已确定它真的正确(这话有些拗口,下面我会详细解释),它的作用就是确保你的
程序按照预计的目标正常运行,并且能够帮助你迅速定位错误原因。断言的机制很简单,就象c#里的断言方法
System.Diagnostics.Debug.Assert的定义,判断一个条件是否成立,如果不成立的话就显示一条信息。看起来很简单,真的能起
那么大作用吗?让我们看下边这个例子。
/// <summary>
/// 存取m_strID的属性
/// </summary>
public string ID
{
get
{
return this.m_strID ;
}
set
{
#if DEBUG
//断言
Debug.Assert(value.Length % 2 == 0 , “分类id长度必须为偶数”) ;
#endif
this.m_strID = value ;
}
}//end method
这
是个很简单的方法,就是为了存取m_strID这个成员变量的值,这个m_strID是个利用编码规则实现树形结构的字符串成员变量,就像这样:
010213,两位为一间隔,通过它的长度和编码规则可以很容易得到它位于第几层,它的父节点的id等等。因为两位数为一间隔,所以这个字符串的长度必须
是个偶数。
看到Debug.Assert那句吗?它的作用就是判断这个字符串的长度是不是偶数,如果不是,则谈出一个对话框来显示”分类id长度
必须为偶数”。或许你会说看不出它有什么作用,不就是判断一个值符不符合要求吗。本来这个程序都是你自己写的,所以你给这个m_strID赋值时应该知道
这个长度为偶数的限制,一般情况下应该都是正确的,好,现在让我们假设这么一种情况,由于某种原因,你忘记了这个限制,而把一个长度是奇数的字符串赋给这
个变量,而这时虽然有问题但程序并不报错,继续运行,当过了很远时,这个错误显露出来,使整个程序崩溃或最终结果不正确,这时即使程序报错也是在离产生这
个错误的真正原因很远的地方,或者干脆就不报错,这是你要找到错误的原因就很困难了,可能要花费几小时甚至几天的时间,而如果当时你加了断言,运行到这里
的时候就会终止,告诉你错误的原因,也就避免了后面出现的问题以及你为纠正这个问题所付出的时间和精力。
怎么样,现在是不是对断言有了一定的了
解,并且有一些兴趣呢?试一下吧,慢慢的你会感受到它的威力。另外需要说的一点是断言是为了辅助deubg的,而不是进行错误处理的,所以一般把它和条件
编译结合使用,只有当编程、测试时才使用断言,而当发行正是版本时应该去掉断言,因为毕竟它是要影响效率的。
四、日志(log)
程
序记不记日志恐怕是区分传统程序员和web程序员最好的标志了。大多数应用程序都记日志,而几乎所有的web程序都不记日志,呵呵。其实日志也是一个特别
有用的东西,如果不记录日志,那很可能系统发生了什么、出现什么情况你都不清楚,尤其是时间一长,更容易出现这种情况。所以,养成良好的习惯,让你的程序
写log吧。
当然,除了上述这些,还有很多东西,如跟踪(trace)单步调试等等,你可以自己看一下资料。
方法我都讲了,用不用就是你的问题了,呵呵。
>>原作者:bigeagle >>来源:chinaasp
Microsoft.Net
Framework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间System.Net和
System.Net.Sockets包含丰富的类可以开发多种网络应用程序。.Net类采用的分层结构允许应用程序在不同的控制级别上访问网络,开发人
员可以根据需要选择针对不同的级别编制程序,这些级别几乎囊括了Internet的所有需要–从socket套接字到普通的请求/响应,更重要的是,这
种分层是可以扩展的,能够适应Internet不断扩展的需要。
抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:请求/响应层、应用协议
层、传输层。WebReqeust和WebResponse
代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层,而Socket类处于传输层。可以如下示意:

可见,传输层位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。
而在.Net中,System.Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows
Sockets (Winsock) 接口的托管实现。System.Net
命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener 和 UDPClient
类封装有关创建到 Internet 的 TCP 和 UDP
连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如
Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。
其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。

可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据;
针对Socket编程,.NET 框架的 Socket 类是 Winsock32 API
提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法,大多数情况下,Socket 类方法只是将数据封送到它们的本机 Win32
副本中并处理任何必要的安全检查。如果你熟悉Winsock
API函数,那么用Socket类编写网络程序会非常容易,当然,如果你不曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发
windows 网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。
在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:
|
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType); |
其中,addressFamily 参数指定 Socket 使用的寻址方案,socketType 参数指定 Socket 的类型,protocolType 参数指定 Socket 使用的协议。
下面的示例语句创建一个 Socket,它可用于在基于 TCP/IP 的网络(如 Internet)上通讯。
|
Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); |
若要使用 UDP 而不是 TCP,需要更改协议类型,如下面的示例所示:
|
Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp); |
一旦创建 Socket,在客户端,你将可
以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过
Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,
并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用
完 Socket 后,记住使用 Shutdown 方法禁用 Socket,并使用 Close 方法关闭 Socket。其间用到的方法/函数有:
|
Socket.Connect方法:建立到远程设备的连接
public void Connect(EndPoint remoteEP)(有重载方法)
Socket.Send 方法:从数据中的指示位置开始将数据发送到连接的 Socket。
public int Send(byte[], int, SocketFlags);(有重载方法)
Socket.SendTo 方法 将数据发送到特定终结点。
public int SendTo(byte[], EndPoint);(有重载方法)
Socket.Receive方法:将数据从连接的 Socket 接收到接收缓冲区的特定位置。
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收数据缓冲区中特定位置的数据并存储终结点。
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 与一个本地终结点相关联:
public void Bind( EndPoint localEP );
Socket.Listen方法:将 Socket 置于侦听状态。
public void Listen( int backlog );
Socket.Accept方法:创建新的 Socket 以处理传入的连接请求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的发送和接收
public void Shutdown( SocketShutdown how );
Socket.Close方法:强制 Socket 连接关闭
public void Close(); |
可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP
使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称
为终结点,在 .NET 框架中正是由 EndPoint
类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了 EndPoint
的子代;对于 IP 地址族,该类为 IPEndPoint。IPEndPoint
类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint 类形成到服务的连接点。
用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类可以得到IP地址实例:
IPAddress类:IPAddress 类包含计算机在 IP 网络上的地址。其Parse方法可将 IP 地址字符串转换为 IPAddress 实例。下面的语句创建一个 IPAddress 实例:
|
IPAddress myIP = IPAddress.Parse(“192.168.1.2″); |
Dns 类:向使用 TCP/IP
Internet 服务的应用程序提供域名服务。其Resolve 方法查询 DNS
服务器以将用户友好的域名(如”host.contoso.com”)映射到数字形式的 Internet 地址(如
192.168.1.1)。Resolve方法 返回一个 IPHostEnty
实例,该实例包含所请求名称的地址和别名的列表。大多数情况下,可以使用 AddressList 数组中返回的第一个地址。下面的代码获取一个
IPAddress 实例,该实例包含服务器 host.contoso.com 的 IP 地址。
|
IPHostEntry ipHostInfo = Dns.Resolve(“host.contoso.com”);
IPAddress ipAddress = ipHostInfo.AddressList[0];
|
你也可以使用GetHostName方法得到IPHostEntry实例:
|
IPHosntEntry hostInfo=Dns.GetHostByName(“host.contoso.com”) |
在使用以上方法时,你将可能需要处理以下几种异常:
SocketException异常:访问Socket时操作系统发生错误引发
ArgumentNullException异常:参数为空引用引发
ObjectDisposedException异常:Socket已经关闭引发
在掌握上面得知识后,下面的代码将该服务器主机( host.contoso.com的 IP 地址与端口号组合,以便为连接创建远程终结点:
|
IPEndPoint ipe = new IPEndPoint(ipAddress,11000); |
确定了远程设备的地址并选择了用于连接的端口后,应用程序可以尝试建立与远程设备的连接。下面的示例使用现有的 IPEndPoint 实例与远程设备连接,并捕获可能引发的异常:
|
try {
s.Connect(ipe);//尝试连接
}
//处理参数为空引用异常
catch(ArgumentNullException ae) {
Console.WriteLine(“ArgumentNullException : {0}”, ae.ToString());
}
//处理操作系统异常
catch(SocketException se) {
Console.WriteLine(“SocketException : {0}”, se.ToString());
}
catch(Exception e) {
Console.WriteLine(“Unexpected exception : {0}”, e.ToString());
} |
需要知道的是:Socket 类支持两种基本模式:同步和异步。其区别在于:在同步模式中,对执行网络操作的函数(如 Send 和 Receive)的调用一直等到操作完成后才将控制返回给调用程序。在异步模式中,这些调用立即返回。
另外,很多时候,Socket编程视情况不同需要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时
编制服务端应用程序处理该请求,这个过程在上面的阐述中已经提及;当然,并非所有的Socket编程都需要你严格编写这两端程序;视应用情况不同,你可以
在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。以下事例语句中的字符串就向远程主机提出页面请求:
|
string Get = “GET / HTTP/1.1\r\nHost: ” + server + “\r\nConnection: Close\r\n\r\n”; |
远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序。
综合运用以上阐述的使用Visual
C#进行Socket网络程序开发的知识,下面的程序段完整地实现了Web页面下载功能。用户只需在窗体上输入远程主机名(Dns
主机名或以点分隔的四部分表示法格式的 IP
地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就可以获取远程主机页面并保存在本地机指定文件中。如果保存格式是.htm格式,你
就可以在Internet浏览器中打开该页面。适当添加代码,你甚至可以实现一个简单的浏览器程序。

实现此功能的主要源代码如下:
|
//”开始”按钮事件
private void button1_Click(object sender, System.EventArgs e) {
//取得预保存的文件名
string fileName=textBox3.Text.Trim();
//远程主机
string hostName=textBox1.Text.Trim();
//端口
int port=Int32.Parse(textBox2.Text.Trim());
//得到主机信息
IPHostEntry ipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[] ipAddr=ipInfo.AddressList;
//得到ip
IPAddress ip=ipAddr[0];
//组合出远程终结点
IPEndPoint hostEP=new IPEndPoint(ip,port);
//创建Socket 实例
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
{
//尝试连接
socket.Connect(hostEP);
}
catch(Exception se)
{
MessageBox.Show(“连接错误”+se.Message,”提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//发送给远程主机的请求内容串
string sendStr=”GET / HTTP/1.1\r\nHost: “ + hostName +
“\r\nConnection: Close\r\n\r\n”;
//创建bytes字节数组以转换发送串
byte[] bytesSendStr=new byte[1024];
//将发送内容字符串转换成字节byte数组
bytesSendStr=Encoding.ASCII.GetBytes(sendStr);
try
{
//向主机发送请求
socket.Send(bytesSendStr,bytesSendStr.Length,0);
}
catch(Exception ce)
{
MessageBox.Show(“发送错误:”+ce.Message,”提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//声明接收返回内容的字符串
string recvStr=”";
//声明字节数组,一次接收数据的长度为1024字节
byte[] recvBytes=new byte[1024];
//返回实际接收内容的字节数
int bytes=0;
//循环读取,直到接收完所有数据
while(true)
{
bytes=socket.Receive(recvBytes,recvBytes.Length,0);
//读取完成后退出循环
if(bytes<=0)
break;
//将读取的字节数转换为字符串
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
}
//将所读取的字符串转换为字节数组
byte[] content=Encoding.ASCII.GetBytes(recvStr);
try
{
//创建文件流对象实例
FileStream fs=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
//写入文件
fs.Write(content,0,content.Length);
}
catch(Exception fe)
{
MessageBox.Show(“文件创建/写入错误:”+fe.Message,”提示信息”,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//关闭Socket
socket.Close();
}
} |
程序在WindowsXP中文版、.Net Frameworkd 中文正式版、Visual Studio.Net中文正式版下调试通过
我们已经知道,由于Visual
C#本身没有类库,他是通过.Net框架中的.Net FrameWork
SDK(软件开发包)定义的一些类来实现对注册表的操作。这就是名称空间Microsoft.Win32中封装的二个类:Registry类、
RegistryKey类。在RegistryKey类中定义了二个方法用来创建注册表中的主键、子键和键值。他们是CreateSubValue (
)方法和SetValue ( )方法。那么如何用Visual
C#来修改注册信息,在本文中,我们只是介绍了修改注册表中的键值的方法。而对于主键和子键,由于.Net FrameWork
SDK中还没有定义这方面的方法,所以还无法完成安全的修改注册表中的信息。下面就先介绍如何用Visual C#来创建注册信息。
一.Visual C#创建和修改注册信息要调用的二个方法:
(1).CreateSubKey ( String key )方法:此方法是创建以后面的字符串为名称的子键。当然这种方法不仅能够创建子键,在下面介绍的程序中,也通过此种方法来创建一个主键。
(2).SetValue ( String name , String keyvalue
)方法:此方法的作用有二点,一种可以用来重命名键值的数值,一种可以用来创建新的键值。具体情况如下:当打开的子键中,如果存在此键值,就把新值赋给
他,实现重命名操作。如果不存在,则创建一个新的键值。
二.程序设计和运行环境以及要准备的工作:
I>视窗系统2000服务器版
II>.Net FrameWork SDK Beta 2版
III>由于在程序中,要修改一个已经存在的键值,所以就要预先设置好键值所在的位置。打开注册表的编辑器,在
“HKEY_LOCAL_MACHINE”主键下面的”HARDWARE”子键下面创建”aaa”子键并在此子键下面创建一个名称为”bbb”的键值。具
体如下图所示:

图01:为程序设置的注册表的结构图示
三.程序的主要功能以及设计的重要步骤:
在下面介绍的程序中,主要的功能是用Visual
C#在注册表中创建一个主键、一个子键和修改一个指定的键值。其中要创建的子键的结构层次是在主键”HKEY_LOCAL_MACHIN”下面的
“HAREWARE”主键下,名称为”ddd”,其中包含一个键值,名称为”www”,键值的值为”1234″。
其中的要创建的主键的结构层次也是在主键”HKEY_LOCAL_MACHIN”下面的”HAREWARE”主键下,名称为
“main”,在此主键下面包含一个名称为”sub”的子键和名称为”value”键值,键值的值为”1234″。下面就来着重介绍Visual
C#是如何创建和修改这些主键、子键和键值的。
(1).如何创建一个子键,在程序中是结合CreateSubKey ( )方法和SetValue ( )方法来实现的,以下是程序中创建子键的源程序:
listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE” , true ) ; RegistryKey main1 = software.CreateSubKey ( “main” ) ; RegistryKey ddd = main1.CreateSubKey ( “sub” ) ; ddd.SetValue ( “value” , “1234″ ); |
(2).如何创建一个主键,创建一个主键和创建一个子键的过程大致是差不多的。由于主键包含若干子键,所以在创建主键的时候必须注意他们的层次关系。下面这一段程序,在参考的时候,请注意一下main键和sub键之间的关系。
listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE”, true ) ; RegistryKey main1 = software.CreateSubKey ( “main” ) ; RegistryKey ddd = main1.CreateSubKey ( “sub” ) ; ddd.SetValue ( “value” , “1234″ ) ; |
(3).如何修改注册信息。由于注册表中的信息十分重要,所以一般不要对其进行写的操作。也可能是这个原因,在.Net
FrameWork SDK 中并没有提供修改注册表键的方法。而只是提供了一个危害性相对较小的方法–SetValue (
),通过这个方法,我们可以来修改键值。下面程序代码是修改一个指定键值名称的键值。当然由于SetValue(
)方法的特性,如果它检测到这个键值不存在,就会创建一个新的键值。
listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE”, true ) ; RegistryKey dddw = software.OpenSubKey ( “aaa” , true ) ; dddw.SetValue ( “bbb” , “abcd” ) ; |
四.本文中源程序代码( reg.cs )以及编译后的程序运行界面:
以下是程序运行的界面:
图02:本文中介绍的程序运行界面
reg.cs程序代码如下:
using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using Microsoft.Win32 ; //导入使用到的名称空间
public class Form1 : Form { private System.ComponentModel.Container components ; private ListBox listBox1 ; private Button button1 ; private Button button2 ; private Button button3 ; private Button button4 ;
public Form1 ( ) { InitializeComponent ( ) ; } //清除在程序中使用过的资源 public override void Dispose ( ) { base.Dispose ( ) ; components.Dispose ( ) ; } //初始化程序中使用到的组件 private void InitializeComponent ( ) { this.components = new System.ComponentModel.Container ( ) ; this.button1 = new Button ( ) ; this.listBox1 = new ListBox ( ) ; button1.Location = new System.Drawing.Point ( 16 , 320 ) ; button1.Size = new System.Drawing.Size ( 90 , 23 ) ; button1.TabIndex = 0 ; button1.Text = “读取注册表” ; button1.Click += new System.EventHandler ( this.button1_Click ) ;
this.button2 = new Button ( ) ; button2.Location = new System.Drawing.Point ( 116 , 320 ) ; button2.Size = new System.Drawing.Size ( 90 , 23 ) ; button2.TabIndex = 1 ; button2.Text = “创建子键” ; button2.Click += new System.EventHandler ( this.button2_Click ) ;
this.button3 = new Button ( ) ; button3.Location = new System.Drawing.Point ( 216 , 320 ) ; button3.Size = new System.Drawing.Size ( 90 , 23 ) ; button3.TabIndex = 2 ; button3.Text = “创建主键” ; button3.Click += new System.EventHandler ( this.button3_Click ) ;
this.button4 = new Button ( ) ; button4.Location = new System.Drawing.Point ( 316 , 320 ) ; button4.Size = new System.Drawing.Size ( 90 , 23 ) ; button4.TabIndex = 3 ; button4.Text = “重命名键值” ; button4.Click += new System.EventHandler ( this.button4_Click ) ;
listBox1.Location = new System.Drawing.Point ( 16 , 32 ) ; listBox1.Size = new System.Drawing.Size ( 496 , 264 ) ; listBox1.TabIndex = 4 ; this.Text = “用Visual C#来创建和修改注册表中的注册信息!” ; this.AutoScaleBaseSize = new System.Drawing.Size ( 5 , 13 ) ; this.ClientSize = new System.Drawing.Size ( 528 , 357 ) ; //在窗体中加入组件 this.Controls.Add ( this.listBox1 ) ; this.Controls.Add ( this.button1 ) ; this.Controls.Add ( this.button2 ) ; this.Controls.Add ( this.button3 ) ; this.Controls.Add ( this.button4 ) ; } //以列表形式显示”HARDWARE”下面一层的子键和键值 protected void button1_Click ( object sender , System.EventArgs e ) { listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE” ) ; //打开”SYSTEM”子键 foreach ( string site in software.GetSubKeyNames ( ) ) //开始遍历由子键名称组成的字符串数组 { listBox1.Items.Add ( site ) ; //在列表中加入子键名称 RegistryKey sitekey = software.OpenSubKey ( site ) ; //打开此子键 foreach ( string sValName in sitekey.GetValueNames ( ) ) //开始遍历由指定子键拥有的键值名称组成的字符串数组 { listBox1.Items.Add ( ” ” + sValName + “: ” + sitekey.GetValue ( sValName ) ) ; //在列表中加入键名称和对应的键值 } } } //创建子键和键值 protected void button2_Click ( object sender , System.EventArgs e ) { listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE”, true ) ; RegistryKey ddd = software.CreateSubKey ( “ddd” ) ; ddd.SetValue ( “www” , “1234″ ); } //创建一个主键并创建一个键值 protected void button3_Click ( object sender , System.EventArgs e ) { listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE”, true ) ; RegistryKey main1 = software.CreateSubKey ( “main” ) ; RegistryKey ddd = main1.CreateSubKey ( “sub” ) ; ddd.SetValue ( “value” , “1234″ ) ; } //重命名一个存在的键值 protected void button4_Click ( object sender , System.EventArgs e ) { listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “HARDWARE”, true ) ; RegistryKey dddw = software.OpenSubKey ( “aaa” , true ) ; dddw.SetValue ( “bbb” , “abcd” ) ; } public static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } } |
由于Visual C#本身没有带类库,他对注册表的处理过程是通过调用.Net FrameWork
SDK中的名称空间Microsoft.Win32中封装的二个类来实现的。这二个类就是Registry类、RegistryKey类。在
RegistryKey类中定义了三个方法来删除注册表中的注册信息。他们分别是:DeleteSubKey (
)方法、DeleteSubKeyTree ( )方法、DeleteValue ( )方法。下面就具体介绍一下在Visual
C#中如何正确使用这三个方法。
一.如何用Visual C#中调用这三个方法:
在介绍如何使用这三个方法之前,还需要重新介绍一下RegistryKey类中的一个方法–OpenSubKey ( )方法。在上一文中已经介绍了,此方法是打开指定的子键。其实OpenSubKey( )方法有二种调用的方式:
- OpenSubKey (string , subkey) :这种调用方式是对于此子键只是进行读操作。
- OpenSubKey (string subkey, Boolean writable):当对子键使用写操作的时候要用此种调用方法。如果在对子键使用了写操作,但仍然使用第一种调用方法,在程序运行的时候会产生一个错误信息。
- DeleteSubKey ( )方法:
此方法是删除一个指定的子键,在使用此方法的时候,如果在此子键中还存在另外的子键,则会产生一个错误信息。在程序中调用此方法有二种原型,为:
- DeleteSubKey ( string , subkey ):这种调用方式就是直接删除指定的子键。
- DeleteSubKey ( string subkey , Boolean info
):其中的”string”是要删除的子键的名称,”Boolean”参数的意思是:如果值为”True”,则在程序调用的时候,删除的子键不存在,则产
生一个错误信息;如果值为”False”,则在程序调用的时候,删除的子键不存在,也不产生错误信息,程序依然正确运行。所以在具体的程序设计过程中,我
还是推荐使用第二种调用方法。
DeleteSubKeyTree ( )方法:
此方法是彻底删除指定的子键目录,即:删除该子键以及该子键以下的全部子键。由于此方法的破坏性是非常强的,所有在使用的时候要非常主要。在程序中调用此方法的原型就一种,为:
DeleteSubKeyTree ( string subkey ):其中”subkey”就是要彻底删除的子键名称。
DeleteValue ( )方法:
此方法是删除指定的键值。在程序中调用此方法的原型就一种,为:
DeleteValue ( string value ):其中”value”就是要删除的键值的名称。
在介绍完与删除注册表中注册信息有关方法后,将通过一个程序来说明他们在程序中具体用法。
二. 程序设计和运行环境以及要准备的工作:
- 视窗系统2000服务器版
- .Net FrameWork SDK Beta 2版
- 由于程序的功能是删除指定的主键、子键和键值,这就需要我们在注册表中先为设置好这些值的位置和名称。具体如下:
在HKEY_LOCAL_MACHINE主键下面的”SOFTWARE”子键中建立如下子键和键值:
在”SOFTWARE”子键下建立”aaa”子键。在”aaa”子键下面建立”bbb”子键和”ddd”子键。在”bbb”子键中建立名称为”ccc”的
键值,键值的值为”ccc”。子”ddd”子键中建立子键”eee”,并在此子键中建立一个”fff”键值,键值的值为”fff”。程序中要删除的键值是
“ccc”键值,要删除的子键是”bbb”,要彻底删除的子键是”ddd”。具体设定如下图所示:

图01:为程序设定的注册表结构图
三. 程序设计的重要步骤:
程序设计的主要步骤就是如何删除键值、不包含任何子键的子键、包含子键的子键。下面就通过程序来具体说明:
- 如何删除键值。在程序中要删除键值是”ccc”。以下就是程序中删除此键值的具体语句。
RegistryKey
hklm = Registry.LocalMachine ;RegistryKey software = hklm.OpenSubKey (
“SOFTWARE”, true ) ;//打开”SOFTWARE”子键RegistryKey no1 =
software.OpenSubKey ( “aaa”, true ) ;//打开”aaa”子键RegistryKey no2 =
no1.OpenSubKey ( “bbb” , true ) ;//打开”bbb”子键no2.DeleteValue( “ccc” )
;//删除名称为”ccc”的键值 |
- 如何删除不包含任何子键的子键。在程序要删除的子键是”bbb”。以下就是删除此子键的具体程序代码:
RegistryKey
hklm = Registry.LocalMachine ;RegistryKey software = hklm.OpenSubKey (
“SOFTWARE”, true ) ;//打开”SOFTWARE”子键RegistryKey no1 =
software.OpenSubKey ( “aaa”, true ) ;//打开”aaa”子键no1.DeleteSubKey (
“bbb”, false );//删除名称为”bbb”的子键 |
- 如何删除包含子键的子键。在程序中要删除的此子键是”ddd”。以下就是删除此子键的具体程序代码:
RegistryKey
hklm = Registry.LocalMachine ;hklm.DeleteSubKey ( “aaa”, false
);RegistryKey software = hklm.OpenSubKey ( “SOFTWARE”, true )
;//打开”SOFTWARE”子键RegistryKey no1 = software.OpenSubKey ( “aaa”, true )
;//打开”aaa”子键no1.DeleteSubKeyTree ( “ddd” );//删除名称为”ddd”的子键 |
四.本文中的程序源代码( reg.cs )以及运行界面:
reg.cs程序的主要功能就是删除注册表中的键值、不包含子键的子键和包含子键的子键。并且通过按钮”读取注册表”,以列表的显示方法来及时了解删除的情况。下图就是程序运行后的界面:

图02:本文中程序的运行界面
reg.cs程序源代码如下:
using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using Microsoft.Win32 ; public class Form1 : Form { private System.ComponentModel.Container components ; private ListBox listBox1 ; private Button button1 ; private Button button2 ; private Button button3 ; private Button button4 ; public Form1 ( ) { InitializeComponent ( ) ; } //清除在程序中使用过的资源 public override void Dispose ( ) { base.Dispose ( ) ; components.Dispose ( ) ; } //初始化程序中使用到的组件 private void InitializeComponent ( ) { components = new System.ComponentModel.Container ( ) ; button1 = new Button ( ) ; button2 = new Button ( ) ; button3 = new Button ( ) ; button4 = new Button ( ) ; listBox1 = new ListBox ( ) ;button1.Location = new System.Drawing.Point ( 16 , 320 ) ; button1.Size = new System.Drawing.Size ( 75 , 23 ) ; button1.TabIndex = 0 ; button1.Text = “读取注册表” ; button1.Click += new System.EventHandler ( button1_Click ) ;
button2.Location = new System.Drawing.Point ( 116 , 320 ) ; button2.Size = new System.Drawing.Size ( 75 , 23 ) ; button2.TabIndex = 0 ; button2.Text = “删除键值ccc” ; button2.Click += new System.EventHandler ( button2_Click ) ;
button3.Location = new System.Drawing.Point ( 216 , 320 ) ; button3.Size = new System.Drawing.Size ( 75 , 23 ) ; button3.TabIndex = 0 ; button3.Text = “删除子键bbb” ; button3.Click += new System.EventHandler ( button3_Click ) ;
button4.Location = new System.Drawing.Point ( 316 , 320 ) ; button4.Size = new System.Drawing.Size ( 75 , 23 ) ; button4.TabIndex = 0 ; button4.Text = “删除主键ddd” ; button4.Click += new System.EventHandler ( button4_Click ) ;
listBox1.Location = new System.Drawing.Point ( 16 , 32 ) ; listBox1.Size = new System.Drawing.Size ( 496 , 264 ) ; listBox1.TabIndex = 1 ;
this.Text = “用Visual C#来删除注册表中的主键、子键和键值!” ; this.AutoScaleBaseSize = new System.Drawing.Size ( 5 , 13 ) ; this.ClientSize = new System.Drawing.Size ( 528 , 357 ) ; this.Controls.Add ( listBox1 ) ; this.Controls.Add ( button1 ) ; this.Controls.Add ( button2 ) ; this.Controls.Add ( button3 ) ; this.Controls.Add ( button4 ) ; } protected void button1_Click ( object sender , System.EventArgs e ) { listBox1.Items.Clear ( ) ; RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “SOFTWARE” ) ; //打开”SOFTWARE”子键 RegistryKey no1 = software.OpenSubKey ( “aaa” ) ; //打开”aaa”子键 foreach ( string site in no1.GetSubKeyNames ( ) ) //开始遍历由子键名称组成的字符串数组 { listBox1.Items.Add ( site ) ; //在列表中加入子键名称 RegistryKey sitekey = no1.OpenSubKey ( site ) ; //打开此子键 foreach ( string sValName in sitekey.GetValueNames ( ) ) //开始遍历由指定子键拥有的键值名称组成的字符串数组 { listBox1.Items.Add ( ” ” + sValName + “: ” + sitekey.GetValue ( sValName ) ) ; //在列表中加入键名称和对应的键值 } } } protected void button2_Click ( object sender , System.EventArgs e ) { RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “SOFTWARE”, true ) ; //打开”SOFTWARE”子键 RegistryKey no1 = software.OpenSubKey ( “aaa”, true ) ; //打开”aaa”子键 RegistryKey no2 = no1.OpenSubKey ( “bbb” , true ) ; //打开”bbb”子键 no2.DeleteValue( “ccc” ) ; //删除名称为”ccc”的键值 } protected void button3_Click ( object sender , System.EventArgs e ) { RegistryKey hklm = Registry.LocalMachine ; RegistryKey software = hklm.OpenSubKey ( “SOFTWARE”, true ) ; //打开”SOFTWARE”子键 RegistryKey no1 = software.OpenSubKey ( “aaa”, true ) ; //打开”aaa”子键 no1.DeleteSubKey ( “bbb”, false ); //删除名称为”bbb”的子键 } protected void button4_Click ( object sender , System.EventArgs e ) { RegistryKey hklm = Registry.LocalMachine ; hklm.DeleteSubKey ( “aaa”, false ); RegistryKey software = hklm.OpenSubKey ( “SOFTWARE”, true ) ; //打开”SOFTWARE”子键 RegistryKey no1 = software.OpenSubKey ( “aaa”, true ) ; //打开”aaa”子键 no1.DeleteSubKeyTree ( “ddd” ); //删除名称为”ddd”的子键 } public static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } }
|
五.总结:
本文介绍Visual C#注册表编程的一个重要内容,即:如何删除注册信息。由于删除注册信息是一项非常具有破坏性的操作,所以在操作之前一定要注意对注册表的保护工作。
从视窗95开始,微软公司就在视窗系统中引入了注册表这个概念。注册表到底是什么东东呢?它是视窗系统的一个核心的数据库,在这个数据库中存放中与系统相关的各种参数,这些参数直接控制中系统的启动、硬件的驱动程序安装信息以及在视窗系统上运行的各种应用程序的注册信息等。这就意味着,如果注册表因为某些原因受到了破坏,轻者是视窗系统启动过程出现异常,重者就有可能导致整个系统的完全瘫痪。所以正确的认识注册表,及时的备份注册表,对于视窗用户就显得相当重要。
Vsiaul C#就可以十分方便、简洁的开发出操作注册表的程序。本文就是介绍如何利用VisualC#来读取注册表中的信息。
一.初步认识注册表:
单击”开始/运行”,在”打开”的后面填入”regedit”。就可以看到注册表的数据结构了。如下图。注:Regedit文件是微软公司提供给用户编辑注册表的一个工具。

图01:注册表结构图示
如上图左边的部分在注册表中称为”主键”,据图可见,”主键”是有层次结构的。主键的下一级主键称为该主键的”子键”。每一个主键可以对拥有多个子键。如图所示,右边的这些值就是所谓的键值了。每一个主键或者子键都可以拥有多个键值。注册表是一个庞大的数据库,在其中每一个主键,每一个键值都赋予了不同的功能。
二.Visual C#如何读取注册表中的主键和键值:
在.Net
FrameWork SDK Beta
2版中,有一个Microsoft.Win32的名称空间,在此名称空间中提供了二个用于注册表操作的类:Registry类、RegistryKey类。这二个类都是封闭类,不可以继承。在这二个类,定义了许多关于注册表的方法和属性,通过调用这二个类,在Visual
C#中就可以比较轻松的处理关于注册表的各种操作了。
- Registry类:
此类主要封装了七个公有的静态域,而这些静态域分别代表这视窗注册表中的七个基本的主键,具体如下所示:
Registry.ClassesRoot
对应于HKEY_CLASSES_ROOT主键
Registry.CurrentUser
对应于HKEY_CURRENT_USER主键
Registry.LocalMachine 对应于
HKEY_LOCAL_MACHINE主键
Registry.User 对应于 HKEY_USER主键
Registry.CurrentConfig
对应于HEKY_CURRENT_CONFIG主键
Registry.DynDa
对应于HKEY_DYN_DATA主键
Registry.PerformanceData 对应于HKEY_PERFORMANCE_DATA主键
- .RegistryKey类:
此类中主要封装了对视窗系统注册表的基本操作。在程序设计中,首先通过Registry类找到注册表中的基本主键,然后通过RegistryKey类,来找其下面的子键和处理具体的操作的。
三.通过一个读取注册表信息例子来具体说明这二个来的用法:
- (1).程序设计和运行的环境:I视窗系统2000服务器版 II Net FrameWork SDK Beta 2版
- (2)在运行程序前的一些必要的处理工作:
在程序设计时,主要功能是读取已经存在的主键键值,用户可以按照下图所示的结构新建若干个主键和对应的键值

图02:程序设计中要读取的注册表的信息
- 这里有必要说明的是上图只显示了”新项 #3″子键对应的键值。在”新项 #2″子键也有键值,对应的键值是:”新值#1″为”001″,”新值
#2″为”002″。在”新项 #1″子键中对应的键值是:”新值 #1″为”aaa”,”新值 #2″为”bbb”。
- 程序的主要功能:
程序的主要功能是读取指定主键下面的所有子键和子键拥有的键值,并以列表的形式按层次显示出来,下图是本程序运行后界面:

图03:读取注册表信息并以列表形式显示出来
4.
- .程序设计过程中的重要步骤以及应该注意的一些问题:
I
程序中读取主键、子键和键值所使用到的方法:
程序中为了读取指定主键下面的子键和子键中拥有的键值,主要使用了RegistryKey类中的四个方法:OpenSubKey,GetSubKeyNames,GetValueNames,GetValue。具体的用法和意思如下:
- OpenSubKey ( string name )方法主要是打开指定的子键。
- GetSubKeyNames ( )方法是获得主键下面的所有子键的名称,它的返回值是一个字符串数组。
- GetValueNames ( )方法是获得当前子键中的所有的键名称,它的返回值也是一个字符串数组。
- GetValue ( string name )方法是指定键的键值。
程序中具体的使用语句如下:
RegistryKey hklm = Registry.LocalMachine
;//打开”SYSTEM”子键RegistryKey software = hklm.OpenSubKey ( “SYSTEM” )
;//打开”001″子键RegistryKey no1 = software.OpenSubKey ( “001″ )
;//打开”002″子键RegistryKey no2 = no1.OpenSubKey ( “002″ )
; |
其中listBox1是程序中定义了的列表名称。
II
如何用列表形式显示注册信息:
由于GetSubKeyNames ( )方法和GetValueNames (
)方法的返回值是字符串数组,所以在程序中是通过foreach语句实现遍历这些字符串数组的。并且在遍历的时候,就通过列表形式显示出来,程序中具体实现语句如下:
foreach ( string site in no2.GetSubKeyNames ( )
)//开始遍历由子键名称组成的字符串数组{listBox1.Items.Add ( site ) ;//在列表中加入子键名称RegistryKey
sitekey = no2.OpenSubKey ( site ) ;//打开此子键foreach ( string sValName in
sitekey.GetValueNames ( ) )//开始遍历由指定子键拥有的键值名称组成的字符串数组{listBox1.Items.Add ( “” +
sValName + “: ” + sitekey.GetValue ( sValName ) )
;//在列表中加入键名称和对应的键值}} |
- 源程序代码:
通过以上的论述,我们可以得到程序的源程序代码,具体如下:
using System ;using System.Drawing ;using
System.Collections ;using System.ComponentModel ;using System.Windows.Forms
;using System.Data ;using Microsoft.Win32 ; public class Form1 : Form{private
System.ComponentModel.Container components ;private ListBox listBox1 ;private
Button button1 ;public Form1 ( ){InitializeComponent ( ) ;}//清除在程序中使用过的资源public
override void Dispose ( ){base.Dispose ( ) ;components.Dispose ( )
;}//初始化程序中使用到的组件private void InitializeComponent ( ){this.components = new
System.ComponentModel.Container ( ) ;this.button1 = new Button ( )
;this.listBox1 = new ListBox ( ) ;button1.Location = new System.Drawing.Point (
16 , 320 ) ;button1.Size = new System.Drawing.Size ( 75 , 23 ) ;button1.TabIndex
= 0 ;button1.Text = “读取注册表” ;button1.Click += new System.EventHandler(
this.button1_Click ) ;listBox1.Location = new System.Drawing.Point ( 16 , 32 )
;listBox1.Size = new System.Drawing.Size ( 496 , 264 ) ;listBox1.TabIndex = 1
;this.Text = “读取主测表信息” ;this.AutoScaleBaseSize = new System.Drawing.Size ( 5 ,
13 ) ;this.ClientSize = new System.Drawing.Size ( 528 , 357 )
;this.Controls.Add( this.listBox1 ) ;this.Controls.Add ( this.button1 )
;}protected void button1_Click ( object sender , System.EventArgs e
){listBox1.Items.Clear ( ) ;RegistryKey hklm = Registry.LocalMachine
;RegistryKey software = hklm.OpenSubKey ( “SYSTEM” ) ;//打开”SYSTEM”子键RegistryKey
no1 = software.OpenSubKey ( “001″ ) ;//打开”001″子键RegistryKey no2 = no1.OpenSubKey
( “002″ ) ;//打开”002″子键foreach ( string site in no2.GetSubKeyNames ( )
)//开始遍历由子键名称组成的字符串数组{listBox1.Items.Add ( site ) ;//在列表中加入子键名称RegistryKey
sitekey = no2.OpenSubKey ( site ) ;//打开此子键foreach ( string sValName in
sitekey.GetValueNames ( ) )//开始遍历由指定子键拥有的键值名称组成的字符串数组{listBox1.Items.Add ( “” +
sValName + “: ” + sitekey.GetValue ( sValName ) ) ;//在列表中加入键名称和对应的键值}}}public
static void Main ( ){Application.Run ( new Form1 ( ) )
;}} |
四.总结:
用Visual
C#来读取注册表中的注册信息是通过名称空间Micorsoft.Win32中的二个类来实现的。在这二个类中还定义了对注册表信息的删除、修改和重命名的一些方法。这些方法比起本文介绍的读取方法、打开方法来说,更具有破坏性,但也更实用。对应这些方法的介绍将在以后的文章中进行。
通过以上的介绍,我们发现用Visual
C#来处理注册表,其实是一件比较轻松而简单的事情。事情虽然是轻松的,但我也要提醒各位,由于注册表在视窗系统中的重要作用,所以在每一次对注册表进行操作之前,一定要备份,在操作的时候也要非常小心,因为你的每一次的误操作都可能导致你的系统崩溃。
在了解HTTP断点续传的原理之前,让我们先来了解一下HTTP协议,HTTP协议是一种基于tcp的简单协议,分为请求和回复两种。请求协议是由客户机(浏览器)向服务器(WEB
SERVER)提交请求时发送报文的协议。回复协议是由服务器(web
server),向客户机(浏览器)回复报文时的协议。请求和回复协议都由头和体组成。头和体之间以一行空行为分隔。
以下是一个请求报文与相应的回复报文的例子:
GET
/image/index_r4_c1.jpg HTTP/1.1
Accept: */*
Referer:
http://192.168.3.120:8080
Accept-Language: zh-cn
Accept-Encoding: gzip,
deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET
CLR 1.0.3705)
Host: 192.168.3.120:8080
Connection:
Keep-Alive
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue,
24 Jun 2003 05:39:40 GMT
Content-Type: image/jpeg
Accept-Ranges:
bytes
Last-Modified: Thu, 23 May 2002 03:05:40 GMT
ETag:
“bec48eb862c21:934″
Content-Length: 2827
….
下面我们就来说说”断点续传”,顾名思义,断点续传就是在上一次下载时断开的位置开始继续下载。
在HTTP协议中,可以在请求报文头中加入Range段,来表示客户机希望从何处继续下载。
比如说从第1024字节开始下载,请求报文如下:
GET
/image/index_r4_c1.jpg HTTP/1.1
Accept: */*
Referer:
http://192.168.3.120:8080
Accept-Language: zh-cn
Accept-Encoding: gzip,
deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET
CLR 1.0.3705)
Host: 192.168.3.120:8080
Range:bytes=1024-
Connection:
Keep-Alive
.NET中的相关类
明白了上面的原理,那么,我们来看看.NET
FRAMEWORK中为我们提供了哪些类可以来做这些事。
完成HTTP请求
System.Net.HttpWebRequest
HttpWebRequest
类对 WebRequest 中定义的属性和方法提供支持,也对使用户能够直接与使用 HTTP
的服务器交互的附加属性和方法提供支持。
HttpWebRequest 将发送到 Internet 资源的公共 HTTP
标头值公开为属性,由方法或系统设置。下表包含完整列表。可以将 Headers
属性中的其他标头设置为名称/值对。但是注意,某些公共标头被视为受限制的,它们或者直接由
API公开,或者受到系统保护,不能被更改。Range也属于被保护之列,不过,.NET为开发者提供了更方便的操作,就是
AddRange方法,向请求添加从请求数据的开始处或结束处的特定范围的字节范围标头
完成文件访问
System.IO.FileStream
FileStream
对象支持使用Seek方法对文件进行随机访问, Seek
允许将读取/写入位置移动到文件中的任意位置。这是通过字节偏移参考点参数完成的。字节偏移量是相对于查找参考点而言的,该参考点可以是基础文件的开始、当前位置或结尾,分别由SeekOrigin类的三个属性表示。
代码实现
了解了.NET提供的相关的类,那么,我们就可以方便的实现了。
代码如下:
static
void Main(string[] args)
{
string StrFileName=”c:\\aa.zip”;
//根据实际情况设置
string StrUrl=”http://www.xxxx.cn/xxxxx.zip”;
//根据实际情况设置
//打开上次下载的文件或新建文件
long lStartPos =0;
System.IO.FileStream
fs;
if (System.IO.File.Exists(StrFileName))
{
fs=
System.IO.File.OpenWrite(StrFileName);
lStartPos=fs.Length;
fs.Seek(lStartPos,System.IO.SeekOrigin.Current);
//移动文件流中的当前指针
}
else
{
fs = new
System.IO.FileStream(StrFileName,System.IO.FileMode.Create);
lStartPos
=0;
}
//打开网络连接
try
{
System.Net.HttpWebRequest request
=(System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(StrUrl);
if (
lStartPos>0)
request.AddRange((int)lStartPos);
//设置Range值
//向服务器请求,获得服务器回应数据流
System.IO.Stream ns=
request.GetResponse().GetResponseStream();
byte[] nbytes = new
byte[512];
int nReadSize=0;
nReadSize=ns.Read(nbytes,0,512);
while(
nReadSize
>0)
{
fs.Write(nbytes,0,nReadSize);
nReadSize=ns.Read(nbytes,0,512);
}
fs.Close();
ns.Close();
Console.WriteLine(“下载完成”);
}
catch(Exception
ex)
{
fs.Close();
Console.WriteLine(“下载过程中出现错误:”+ex.ToString());
}
}