文本框如何支持显示大于64k文件?

zhucehaoduo 2004-06-07 04:22:02
2000 下记事本可以读取大于64k的文件并显示,读取用流文件即可,显示是什么道理呢?文本框如何支持显示大于64k文件?
...全文
402 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhucehaoduo 2004-06-10
  • 打赏
  • 举报
回复
没有必要模拟,上面代码后,文本框显示200k的跟2k效果一样。
明天揭贴送分。
northwolves 2004-06-09
  • 打赏
  • 举报
回复
可以模拟:


'add a vscrollbar(垂直滚动条) and a textbox with multiline=true and scrollbars=1

Option Explicit
Dim mybyte(1024) As Byte
Dim lines() As String, num As Integer
Private Sub Form_Load()
Dim i As Long
Open "c:\xxxx.txt" For Binary As #1
num = LOF(1) \ 1024
ReDim lines(num + 10)
For i = 0 To num
Get #1, i * 1024 + 1, mybyte
lines(i) = StrConv(mybyte, vbUnicode)
Next
Close #1
Text1.Height = Me.TextHeight("1") * 12
VScroll1.Move Text1.Left + Text1.Width, Text1.Top, VScroll1.Width, Text1.Height
VScroll1.Min = 0
VScroll1.Max = num
VScroll1.SmallChange = 1
VScroll1.Value = 0
End Sub

Private Sub vscroll1_Change()
Dim temp As String, i As Long
For i = VScroll1.Value To VScroll1.Value + 10
temp = temp & lines(i)
Next
Text1.Text = temp
End Sub
zhucehaoduo 2004-06-09
  • 打赏
  • 举报
回复
哎呀,没人知道吗 。 查中文google n 个礼拜始终找不到,今天只好找英文的,很快下面方法搞定。


Load Large File Into a Textbox
Q. I thought that most 64K limits were a thing of the past—notably that psuedo-32bit OS, Windows 9x. However, when I put a multiline textbox on a form (under NT4) and tried to load a large file into it, the file was truncated, and Len(txtBox.Text) returned 65,535. Can you provide an example of how to load more than 64K into a VB textbox?

A. Microsoft designed all the intrinsic controls to lowest-common-denominator specs. Because much of Windows 9x is still 16-bit, Microsoft made compromises "for your protection."

Under NT, the underlying edit class is fully 32-bit, but you must go entirely to the API if you want to exceed the built-in 64K limits. The most reliable method to load large amounts of text into a VB textbox is to use SendMessage with WM_SETTEXT (see Listing 3). The SetWindowText API wraps this message, but has the drawback that it doesn't return the proper result code when it fails under Windows 9x, should your code also find itself running there.




VB4/32, VB5, VB6 Defy 16-Bit Limits
Option Explicit

Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As _
Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function SetWindowText Lib "user32" _
Alias "SetWindowTextA" (ByVal hWnd As Long, ByVal _
lpString As String) As Long
Private Declare Function GetWindowTextLength Lib _
"user32" Alias "GetWindowTextLengthA" (ByVal hWnd As _
Long) As Long

Private Const WM_SETTEXT = &HC
Private Const WM_GETTEXT = &HD
Private Const WM_GETTEXTLENGTH = &HE

Private Sub Form_Load()
Dim nRet As Long
Dim s As String

' Create string with >64K chars.
s = String(66000, "X")
Me.Show

' Try setting large amount of text.
nRet = SendMessage(Text1.hWnd, WM_SETTEXT, 0&, _
ByVal s)
Debug.Print "WM_SETTEXT: " & nRet

' See how much made it.
nRet = SendMessage(Text1.hWnd, WM_GETTEXTLENGTH, _
0&, ByVal 0&)
Debug.Print "WM_GETTEXTLENGTH: " & nRet

' This call doesn't report failure
' under Windows 9x!
nRet = SetWindowText(Text1.hWnd, s)
Debug.Print "SetWindowText: " & nRet

' Just for kicks...
nRet = GetWindowTextLength(Text1.hWnd)
Debug.Print "GetWindowTextLength: " & nRet
End Sub

Private Sub Form_Resize()
Text1.Move 0, 0, Me.ScaleWidth, Me.ScaleHeight
End Sub



Listing 3 IEither of these two methods can load more than 64K of text into a standard VB textbox. Both work only in Windows NT/2000. Adding to the Windows 9x frustration is the failure of the SetWindowText API to report its own failure in that environment. Stick with SendMessage for the most reliable results, and insist on NT if you need full 32-bit capabilities.

supergreenbean 2004-06-09
  • 打赏
  • 举报
回复
使用缓冲读取,比如说显示多少就读取多少,这样速度就快了
zhucehaoduo 2004-06-09
  • 打赏
  • 举报
回复
RICHTX32.OCX 控件大,不想在发行软件时附带。
zhucehaoduo 2004-06-08
  • 打赏
  • 举报
回复
要是想用RichTextBox早就换了 我不想换 因为改动代码太多

myhot(大头河马) ( ) 信誉:101 2004-06-07 20:34:00 得分: 0
最近刚研究了一下。。
textbox可以大于64K。但是采用API中的sendmessage来改变它的标题。但是这样有个问题,当多于
64K时。。textbox 的处理将会非常慢,,所以建议还是用其它控件代替。

我实验过,读取用流文件非常快,具体怎么写 2000下记事本可是非常快啊 道理何在
chenyu5188 2004-06-07
  • 打赏
  • 举报
回复
用富文本框!!!RichTextBox
dongge2000 2004-06-07
  • 打赏
  • 举报
回复
记事本的类是“Edit”啊。
myhot-河马吃草 2004-06-07
  • 打赏
  • 举报
回复
最近刚研究了一下。。
textbox可以大于64K。但是采用API中的sendmessage来改变它的标题。但是这样有个问题,当多于
64K时。。textbox 的处理将会非常慢,,所以建议还是用其它控件代替。
pigpag 2004-06-07
  • 打赏
  • 举报
回复
都说了啊

最近翻翻工具箱,发现一个叫InkEdit的控件。大家可以自己去找找。
fengyetianshi 2004-06-07
  • 打赏
  • 举报
回复
RichTextBox这个可能帮你
pc2s 2004-06-07
  • 打赏
  • 举报
回复
替换为RichTextBox即可
haipingma 2004-06-07
  • 打赏
  • 举报
回复
RichTextBox
zhujiechang 2004-06-07
  • 打赏
  • 举报
回复
RichTextBox控件
northwolves 2004-06-07
  • 打赏
  • 举报
回复
可以用RICHTEXTBOX
自己编写的串口软件,主要应用于固定收发命令的调试过程。所有的命令可以导入导出。 下面是介绍: 串口软件使用说明(2002-11-16): 1.如果想把接收的结果显示的更多列,以方便查看接收的结果,可以双击接收文本框, 接收结果文本框就会显示的更宽,再次双击,就会恢复原状。 2.可以16进制和ASC码混合发送,具体输入方法可以让鼠标停在相应控件上显示查看 例如:asdf\30 35 af\qewr239823,其中在\...\中的数据就是16进制发送数据, 中间以空格区分开; 如果要发送"\",可以输入"\\",程序会发送一个"\"; 如果在\...\中间出现除16进制的其它字符,按00发送; 如果出现\1234 4567\之类的,就只能发送每个16进制的最后两个字节"34 67"; 如果出现只有一个"\",例如"asdfa\12 35 af"之类的命令,"\"之后的就按16进制发送, 因为本程序遇到"\"就按16进制解码,直到下一个"\"出现,标志结束16进制发送; 如果只想发送16进制数据,在命令框内输入为:\10 1F DF AC 1A CB\ 然后加入命令框中,发送命令即可。 一定注意16进制数据输入不需要"0x"的前缀。支持汉语输入命令。 3.本程序的命令导出导入格式如下: [1]asdf\30 35 af\qewr239823[400] 或者: [0]asdf\30 35 af\qewr239823[500] 第一个"[]"内如果是"1"表明导入后命令发送有效,如果是"0"表明导入后命令发送无效 如果不是0或者1或者不是"["开头,那么就直接导入所有字符,并且设置发送命令无效 最后的"[400]"表明本次命令在发送之后的延时时间,单位是ms,如果在最后一对 "[]"中不是有效数据或者其它情况,就按普通字符处理,并自动设置延时为0ms; 4.发送命令有四种方式: a)单条命令发送:选中某条命令,然后点击鼠标右健,在弹出的菜单中选择发送当前命令。 b)按顺序发送所有被选中的命令一次,直接点击发送按钮即可。 c)点击下面的自动按钮,在弹出的小界面中选中启动自动发送按钮,就会自动循环发送 所有被选中的命令,当所有的命令发送完毕之后,延时一定时间(循环发送间隔文本框内设置) 重新从第一条被选中的命令发送。 d)如果输入的都是16进制字符,如:12 af cd ,可以在命令界面中点击鼠标右健,选择 按16进制发送,此时发送是16进制数据。 在多条命令发送状态,每条命令的前面的选项框必须被选中才发送,否则直接跳过而不被发送。 5.命令列表中,命令的顺序可以通过上移或者下移按钮进行调整顺序。 6.接收结果显示可以以ASC码显示或者16进制显示,当以16进制显示的时候可以设置每行显示多少 16进制数据,最多是4096个数据/每行,最少1列数据。 在接收数据时,如果10ms内没有新的数据被接收或者数据大于4096字节就认为一次命令接收结束, 这样程序能够自动区分每条命令。当命令之间时间间隔比较小(20ms之内),接收的命令显示可能 会出现串行现象。 如果文本比较长,可以选择自动换行复选框。如果不自动换行,显示界面控件会自动按一定长度换行, 复制到一般文本编辑器中之后仍然是一行。 7.接收的结果可以导出到文件中,方便查看。格式为文本格式,任何文本编辑器都可以查看。 8.计数的清除 a)自动循环发送的计数是记录所有被选中的命令循环发送了多少次。在每次启动时会自动重新计数。 b)发送字节和接收字节计数是记录本程序发送和接收的字节数,可以直接双击显示结果进行清除; 在清除接收结果的时候,接收计数也会清零;在清除发送命令的时候,发送计数也会清零; 在启动自动循环发送的时候,接收计数和发送计数都会清零; 9.文件的发送 在启动文件发送的时候,接收计数和发送计数也会清零,而且以前接收到的数据也会被清除。 在接收文件的时候,很多情况下是以4096字节附近的整行数字节为一次刷新界面显示。 10.串口的设置 在设置串口的时候,可能有些选项不能设置,这与计算机的本身限制有关。一般都支持115200bps。 11.命令发送框内的弹出菜单的功能 a)编辑已经添加的命令和此命令的延时(注意延时实在发送命令后的延时) b)统一设置每条命令的延时(单位是ms) c)选中所有命令 d)命令全部取消 e)命令选中状态全部取反 f)删除当前命令 g)清除未选中的命令 h)清除所有命令 i)按16进制发送当前被选中的命令 j)发送当前被选中的命令 12.可以通过选中左下角的ON TOP,让程序保持在最前面不被其它程序遮住。 13.所有的发送都可以通过终止按钮终止,正在发送的命令有可能只发送一部分。 以下是命令例子,包含错误语句(直接放到一个temp.zlc文本文件中即可导入发送): [0]adsfasdf[100] [1]2452345\12 35 df\asfd[100] [0][100]12 35 af[400 [1][100]12 35 af[400 [1]adsfasdf[400] [1]\\\df 12 35\[400] [1]\df 12 35 ss\[400] [20]asdf [adf]fdfddf[300] [1]\30 31 32 33 34 35\[500] 14.更为详细的使用请参考本程序的帮助文档 2005-2-25 1.添加了命令自动上移和下移功能。鼠标点击上移和下移按钮1秒以上就可以实现命令自动移动。 2.添加了双击主窗体等(只要不是按钮和显示框)就可以使接收显示框变大。 3.消除了主窗体在变窄的时候出现水平显示条和限制了主窗体的最小高度。 2005-3-1 1.改善了由于通信速率比较慢时,自动分行显示很乱的问题,如果还是不行,可以调整命令延时改善。 2.修改发送命令,在上一条命令发送完毕之后,再延时一定时间,然后再发送下一条命令。 3.限制了每行命令的最大字节为4000字节,以免字符串溢出错误。 4.限制了各个延时文本框的最大字7个节数,显示列数为4个字节,以免字符串溢出错误。 5.★★★★添加了波特率可以随意手工设置为非标准值,例如 500bps,常用波特率仍然可选★★★★ 2005-3-8 1.修改发送程序,保证点击终止之后0.5秒之内就能让发送线程退出发送命令的延时状态 2.修改接收程序,保证接收命令延时间隔最大为1.0秒,保证了接收线程的及时退出延时 2007-3-4 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ (注意导入的txt文件,一般格式不满足本软件,需要点击插入按钮,修改头和尾,重新导出再导入) ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 1.自动在字符串的前面后面添加前导、校验和后导字节 针对仪器平台的10字节通信要求 \7E ×× ×× ×× ×× ×× ×× ×× 校验 7E\ 可以保留1-4个字节,1个字节以1byte为计算宽度,2个字节以2byte 为计算宽度,3个字节以3byte为计算宽度,保留4个字节以4byte为计算宽度。 选中计算宽度的时候,要保证静数据的宽度是保留字节的整倍数,否则一般会计算 错误。 2.添加了累加和计算,可以保留1-4个字节,1个字节以1byte为计算宽度,2个字节以2byte 为计算宽度,3个字节以3byte为计算宽度,保留4个字节以4byte为计算宽度。 选中计算宽度的时候,要保证静数据的宽度是保留字节的整倍数,否则一般会计算 错误。 3.添加了CRC校验计算(16-1选项),高位在后,例如校验值为0x2211, 返回的16进制数据顺序为:11 22 有些校验位感觉是倒着的,验证方式是把要发送的数据加上产生的校验值,然后再次 发送,再次产生的校验值应该为0。 例如 1的16-1的校验值是 0x1021 ,然后在命令中输入\1 10 21\ ,得到的发送结果是 01 10 21 00 00,最后两个字节就是校验值0,说明校验通过。 程序的校验选择有很多中,下面程序中0x8000就是16位的CRC校验,如果是0x80, 就是8位的CRC校验。0x80000000,就是32位的校验,以此类推 0x1021是CRC多项式,根据不同的标准,多项式不同,即使都是32位的校验,多项式 也会有很多中类,参见最后面的一些标准。 2008-11-18 1.修改了图标 2.串口支持255个,原来是16个 3.串口速率可以超过1Mbps了,达到100Mbps,对于一些串口转换器件可以达到6Mbps的通信速率支持 4.去除了发送文件自动清除发送计数bug,发送文件只触发清除发送计数 5.插入按钮内容改为全部空了,方便大部分情况的应用,不再自动插入\7E\和异或校验值 6.增加波特率的默认速率到900K 7.底边控件上移,消除XP下显示不全问题 8.★★★在命令列表中双击由编辑改为直接发送单条信息,方便调试中不断手工更换命令的需要★★★ 9.导入导出和保存默认格式调整为txt 10.发送文件每个数据包改为发送1000字节,且可以通过设置自动循环中发送的时间间隔,每一兆字节 停止××毫秒时间不发送,方便在发送大文件时,用户对比收发是否一致,一旦丢包,用户可以重新设置 发送。
 数据的加密与解密  文件的加密与解密 第 章 加密与解密技术 第19章 加密与解密技术 829 19.1 数据的加密与解密 实例571 异或算法对数字进行加密与解密 光盘位置:光盘\MR\19\571 中级 趣味指数: 实 例说明 在实现本实例之前先来简要了解一下加密的概念,加密是指通过 某种特殊的方法,更改已有信息的内容,使得未授权的用户即使得到 了加密信息,如果没有正确解密的方法,也无法得到信息的内容。谈 到加密的话题,一些读者一定非常感兴趣,而且会联想到复杂的加密 算法,本实例主要使用异或“^”运算符简单地实现了对数字加密的 功能。实例运行效果如图19.1 所示。 关 键技术 本实例实现时主要使用了“异或”运算符对数字进行“异或”运 算,以达到简单加密数字的目的,下面对其进行详细讲解。 “异或”运算符“^”用于比较两个二进制数的相应位。在执行按位“异或”运算时,如果两个二进制数的 相应位都为1 或两个二进制数的相应位都为0,则返回0;如果两个二进制数的相应位其中一个为1 一个为0, 则返回1。 现在来了解一下使用“异或”加密或解密的执行过程,数值23 转换为二进制为10111,加密数字的数值15 转换为二进制为1111。对比两个二进制的值,从右向左按位对比,如果两个二进制数的相应位都为1 或两个二 进制数的相应位都为0,则返回0;如果两个二进制数的相应位中一个为1 一个为0,则返回1,最后得到的结 果为二进制值11000,该值转换为十进制为24,所以得到的加密结果为24。而解密过程也很简单,只是将加密 结果24与加密数字15 进行“异或”运算,将24 转换为二进制值11000,将15 转换为二进制值1111,进行“异 或”运算后,得到结果为23,这样又还原了加密的数据。  说明:本实例只是简单地使用了“异或”运算符计算两个整型数值以达到加密的目的,所以本实例只可以 对整型数值进行加密运算,并不适合其他数据的加密。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为Encrypt。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加两个GroupBox 容器控件,其中, 在第一个GroupBox 中放入3 个TextBox 控件和一个Button 按钮,分别用于输入数字、输入加密数字、显示加 密后的数字和计算加密信息;在第二个GroupBox 中放入一个TextBox 控件和一个Button 按钮,分别用于显示 解密后的信息和计算解密信息。 (3)程序主要代码如下: private void btn_Encrypt_Click(object sender, EventArgs e) { int P_int_Num, P_int_Key; //定义两个值类型变量 if (int.TryParse(txt_Num.Text, out P_int_Num) //判断输入是否是数值 && int.TryParse(txt_Key.Text, out P_int_Key)) { txt_Encrypt.Text = (P_int_Num ^ P_int_Key).ToString(); //加密数值 } else 图19.1 异或算法对数字进行加密与解密 C#开发实战1200 例(第II卷) 830 { MessageBox.Show("请输入数值", "出现错误!"); //提示输入信息不正确 } } private void btn_Revert_Click(object sender, EventArgs e) { int P_int_Key, P_int_Encrypt; //定义两个值类型变量 if (int.TryParse(txt_Encrypt.Text, out P_int_Key) //判断输入是否是数值 && int.TryParse(txt_Key.Text, out P_int_Encrypt)) { txt_Revert.Text = (P_int_Encrypt ^ P_int_Key).ToString(); //解密数值 } else { MessageBox.Show("请输入数值", "出现错误!"); //提示输入信息不正确 } } 秘 笈心法 心法领悟571:简述“异或”运算符。 本实例使用了“异或”运算符,但是在使用“异或”运算符之前,有必要了解“异或”运算符所做的“异 或”运算的机制,“异或”运算符“^”用于比较两个二进制数的相应位。在执行按位“异或”运算时,如果两 个二进制数的相应位都为1 或两个二进制数的相应位都为0,则返回0;如果两个二进制数的相应位中一个为1 一个为0,则返回1。 实例572 使用MD5算法加密数据 光盘位置:光盘\MR\19\572 中级 趣味指数: 实 例说明 MD5(Message-Digest Algorithm 5)是一种被广泛使用的“消息-摘要 算法”。“消息-摘要算法”实际上就是一个单项散列函数,数据块通过单 向散列函数得到一个固定长度的散列值,数据块的签名就是计算数据块的散 列值,MD5 算法的散列值为128 位。本实例演示如何使用MD5 算法对用户 输入的密码进行加密,实例运行效果如图19.2 所示。 关 键技术 本实例在实现时主要用到了MD5类的ComputeHash 方法,下面对其进行详细讲解。 MD5 类表示MD5 哈希算法的所有实现均从中继承的抽象类,该类位于System.Security.Cryptography 命名 空间下,其ComputeHash 方法有3种重载形式,分别介绍如下。  计算指定字节数组的哈希值,语法格式如下: public byte[] ComputeHash(byte[] buffer) 参数说明  buffer:要计算其哈希代码的输入。  返回值:计算所得的哈希代码。  计算指定Stream 对象的哈希值,语法格式如下: public byte[] ComputeHash(Stream inputStream) 参数说明  inputStream:要计算其哈希代码的输入。  返回值:计算所得的哈希代码。 图19.2 使用MD5 算法加密数据 第19章 加密与解密技术 831  计算指定字节数组的指定区域的哈希值,语法格式如下: public byte[] ComputeHash(byte[] buffer,int offset,int count) ComputeHash 方法中的参数及说明如表19.1 所示。 表19.1 ComputeHash方法中的参数及说明 参 数 说 明 buffer 要计算其哈希代码的输入 offset 字节数组中的偏移量,从该位置开始使用数据 count 数组中用作数据的字节数 返回值 计算所得的哈希代码  说明:本实例用到了ComputeHash 方法的第一种重载形式。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为MD5Arithmetic。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加两个TextBox 控件,分别用来输入 要加密的数据和显示加密后的字符串;添加一个Button 控件,用来使用MD5算法对输入的数据进行加密。 (3)程序主要代码如下: public string Encrypt(string strPwd) { MD5 md5 = new MD5CryptoServiceProvider(); //创建MD5 对象 byte[] data = System.Text.Encoding.Default.GetBytes(strPwd); //将字符编码为一个字节序列 byte[] md5data = md5.ComputeHash(data); //计算data字节数组的哈希值 md5.Clear(); //清空MD5 对象 string str = ""; //定义一个变量,用来记录加密后的密码 for (int i = 0; i < md5data.Length - 1; i++) //遍历字节数组 { str += md5data[i].ToString("x").PadLeft(2, '0'); //对遍历到的字节进行加密 } return str; //返回得到的加密字符串 } private void button1_Click(object sender, EventArgs e) { string P_str_Code = textBox1.Text; //记录要加密的密码 textBox2.Text = Encrypt(P_str_Code); //显示加密后的字符串 } 秘 笈心法 心法领悟572:如何判断是否为数字? 开发程序时,经常需要判断输入的字符串是否为数字,如判断输入的电话号码、货币金额和邮编等。在程 序中判断是否为数字的方法有很多种,可以使用正则表达式、int.Parse 方法和double.Parse 方法等。下面的代码 通过double.Parse 方法判断textBox1 文本框中的输入是否为数字。 double.Parse(textBox1.Text); 实例573 使用ROT13算法加密解密数据 光盘位置:光盘\MR\19\573 中级 趣味指数: 实 例说明 文件加密可以避免造成重要信息的泄漏,复杂的加密算法可以将信息加密得非常繁杂,但是对于一般的应 用,没有必要作类似于PGP、RSA 或DES 等复杂的加密算法。本实例介绍如何使用ROT13 算法加密和解密数 C#开发实战1200 例(第II卷) 832 据。实例运行效果如图19.3 所示。 图19.3 使用ROT13算法加密解密数据 关 键技术 本实例实现时,主要是用Convert 类的ToChar 方法来获取单个字符的Unicode 编码,然后将字母的前13 个和后13 个对调,从而实现加密的功能。下面对Convert类的ToChar 方法进行详细讲解。 ToChar 方法返回指定的Unicode字符值,并且不执行任何实际的转换,其语法格式如下: public static char ToChar (char value) 参数说明 value:一个Unicode 字符。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为ROT13Encrypt。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加两个TextBox 控件,分别用来显示 原始数据和解密后的数据;添加两个Button 控件,分别用来实现利用ROT13算法加密和解密数据的功能。 (3)程序主要代码如下: public string ROT13Encode(string InputText) { char tem_Character; //存储临时字符 int UnicodeChar; //存储临时字符的字节值 string EncodedText = ""; //存储加密或解密后的字符串 for (int i = 0; i = 97 && UnicodeChar = 110 && UnicodeChar = 65 && UnicodeChar = 78 && UnicodeChar <= 90) //对字符进行解密 { UnicodeChar = UnicodeChar - 13; } EncodedText = EncodedText + (char)UnicodeChar; //得到加密或解密字符串 } return EncodedText; //返回加密或解密后的字符串 } 秘 笈心法 心法领悟573:如何在字符串中查找指定字符? 在字符串中查找指定字符时,可以先将字符串显示在richTextBox 控件中,然后利用richTextBox 类的Find 方法在该控件中查找指定字符。在字符串中查找指定字符的代码如下: 第19章 加密与解密技术 833 M_int_index = richTextBox1.Find(textBox1.Text.Trim(), M_int_index, RichTextBoxFinds.MatchCase); if (M_int_index == -1) { MessageBox.Show("没有要查找的字符串", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); M_int_index = 0; } else M_int_index = M_int_index + textBox1.Text.Trim().Length; richTextBox1.Focus(); 实例574 使用恺撒密码算法加密密码 光盘位置:光盘\MR\19\574 中级 趣味指数: 实 例说明 恺撒密码据传是古罗马恺撒大帝用来保护重要军情的加密系统,它 是一种置换密码,通过将字母顺序推后起到加密作用。例如,将字母顺 序推后3 位,字母A 将被推作为字母D,字母B 将被推作字母E。本实 例使用C#实现了恺撒加密的算法,实例运行效果如图19.4 所示。 关 键技术 本实例实现时主要用到了string 类的ToCharArray 方法和Convert 类的ToChar 方法,下面分别对它们进行 详细介绍。 (1)string类的ToCharArray 方法 string类的ToCharArray 方法用来将字符串中的字符复制到Unicode 字符数组,该方法有两种重载形式,本 实例中用到的它的重载形式如下: public char[] ToCharArray() 参数说明 返回值:元素为此字符串的各字符的Unicode 字符数组。如果此字符串是空字符串,则返回的数组为空且 长度为零。 (2)Convert 类的ToChar 方法 Convert 类的ToChar 方法用来将指定的值转换为Unicode 字符,该方法为可重载方法,本实例中用到的它 的重载形式如下: public static char ToChar(int value) 参数说明  value:32 位有符号整数。  返回值:等效于value 的值的Unicode 字符。 设 计过程 (1)打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为CaesarArithmetic。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加两个TextBox 控件,分别用来输入 要加密的数据和显示加密后的字符串;添加一个Button 控件,用来使用恺撒密码算法对输入的数据进行加密。 (3)程序主要代码如下: public int AscII(string str) //获取字符的ASCII 码 { byte[] array = new byte[1]; //创建字节数组 array = System.Text.Encoding.ASCII.GetBytes(str); //为字节数组赋值 int asciicode = (short)(array[0]); //获取字节数组的第一项 return asciicode; //返回字节数组的第一项 } 图19.4 使用恺撒密码算法加密密码 C#开发实战1200 例(第II卷) 834 public string Caesar(string str) //凯撒加密算法的实现 { char[] c = str.ToCharArray(); //创建字符数组 string strCaesar = ""; //定义一个变量,用来存储加密后的字符串 for (int i = 0; i < str.Length; i++) //遍历字符串中的每一个字符串 { string ins = c[i].ToString(); //记录遍历到的字符 string outs = ""; //定义一个变量,用来记录加密后的字符串 bool isChar = "0123456789abcdefghijklmnopqrstuvwxyz".Contains(ins.ToLower()); //判断指定的字符串中是否包含遍历到的字符 bool isToUpperChar = isChar && (ins.ToUpper() == ins); //判断遍历到的字符是否是大写 ins = ins.ToLower(); //将遍历到的字符转换为小写 if (isChar) //判断指定的字符串中是否包含遍历到的字符 { int offset = (AscII(ins) + 5 - AscII("a")) % (AscII("z") - AscII("a") + 1); //获取字符的ASCII 码 outs = Convert.ToChar(offset + AscII("a")).ToString(); //转换为字符并记录 if (isToUpperChar) //判断是否大写 { outs = outs.ToUpper(); //全部转换为大写 } } else { outs = ins; //记录遍历的字符 } strCaesar += outs; //添加到加密字符串中 } return strCaesar; //返回加密后的字符串 } 秘 笈心法 心法领悟574:如何将新字符串添加到已有字符串中? 将新字符串添加到已有字符串中时,可以先声明一个StringBuilder类对象,以指定已有字符串的长度可变, 然后利用该对象的Append方法在字符串中添加指定字符串。将新字符串添加到已有字符串的代码如下: StringBuilder strbuilder = new StringBuilder(textBox1.Text.Trim()); strbuilder.Append(textBox2.Text.Trim()); textBox3.Text = strbuilder.ToString(); 实例575 对数据报进行加密保障通信安全 光盘位置:光盘\MR\19\575 高级 趣味指数: 实 例说明 网络传输数据时,有时候传输信息容易被不法分子截获而 用作其他用途。这样,如果传输的数据中包含有重要秘密,将 会造成非常严重的后果。为了防止这种情况的发生,可以对网 络中传输的数据进行加密,用户接收到数据后再进行解密查看, 这样可以更好地保障网络通信安全。运行本实例,首先设置端 口号,然后在窗体左下方的文本框中输入聊天信息,单击“发 送”按钮,向局域网中发送聊天信息,同时在右侧的“数据传 输信息”栏中显示数据报的发送、接收及丢失情况。实例运行 效果如图19.5 所示。 关 键技术 本实例获取数据报信息时主要用到IPGlobalProperties和UdpStatistics类,而在对数据报加密时用到DESCrypto 图19.5 对数据报进行加密保障通信安全 第19章 加密与解密技术 835 ServiceProvider 和CryptoStream 类,其中DESCryptoServiceProvider 继承于DES 类。下面对本实例中用到的关 键技术进行详细讲解。 (1)IPGlobalProperties 类 IPGlobalProperties 类提供有关本地计算机的网络连接的信息,本实例中用到它的GetIPGlobalProperties 和 GetUdpIPv4Statistics 方法,下面分别进行介绍。 GetIPGlobalProperties 为静态方法,主要用来获取一个对象,该对象提供有关本地计算机的网络连接和通信 统计数据的信息,其语法格式如下: public static IPGlobalProperties GetIPGlobalProperties() 参数说明 返回值:IPGlobalProperties 对象,该对象包含有关本地计算机的信息。 GetUdpIPv4Statistics 方法主要用来提供本地计算机的用户数据报协议/Internet 协议版本4 (UDP/IPv4)统 计数据,其语法格式如下: public abstract UdpStatistics GetUdpIPv4Statistics() 参数说明 返回值:UdpStatistics 对象,提供本地计算机的UDP/IPv4通信统计数据。 例如,本实例中创建IPGlobalProperties 对象,及调用其GetUdpIPv4Statistics 方法创建UdpStatistics 对象的 代码如下: IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties(); UdpStatistics myUdpStat = null; myUdpStat = NetInfo.GetUdpIPv4Statistics(); (2)UdpStatistics类 UdpStatistics 类提供用户数据报协议(UDP)统计数据,本实例中主要用到其DatagramsSent 属性、 DatagramsReceived属性和IncomingDatagramsDiscarded 属性,其中,DatagramsSent 属性用来获取已发送的用户 数据报协议(UDP)数据报的数量,DatagramsReceived 属性用来获取已接收的用户数据报协议(UDP)数据报 的数量,IncomingDatagramsDiscarded 属性用来获取已收到但因端口错误而丢弃的用户数据报协议(UDP)数据 报的数量。 例如,本实例中初始化已发送、已接收和丢失数据报的实现代码如下: SendNum1 = Int32.Parse(myUdpStat.DatagramsSent.ToString()); //记录发送的数据报 ReceiveNum1 = Int32.Parse(myUdpStat.DatagramsReceived.ToString()); //记录接收的数据报 DisNum1 = Int32.Parse(myUdpStat.IncomingDatagramsDiscarded.ToString()); //记录丢失的数据报  说明:IPGlobalProperties 类和UdpStatistics 类位于System.Net.NetworkInformation 命名空间下。 (3)DES 类 DES 类表示所有DES 实现都必须从中派生的数据加密标准(DES)算法的基类,其CreateEncryptor 方法和 CreateDecryptor 方法分别用来加密和解密。 CreateEncryptor 方法使用指定的Key属性和初始化向量(IV)创建对称加密器对象,其语法格式如下: public abstract ICryptoTransform CreateEncryptor(byte[] rgbKey,byte[] rgbIV) 参数说明  rgbKey:用于对称算法的密钥。  rgbIV:用于对称算法的初始化向量。  返回值:对称加密器对象。 CreateDecryptor 方法使用指定的Key属性和初始化向量(IV)创建对称解密器对象,其语法格式如下: public abstract ICryptoTransform CreateDecryptor(byte[] rgbKey,byte[] rgbIV) 参数说明  rgbKey:用于对称算法的密钥。  rgbIV:用于对称算法的初始化向量。  返回值:对称解密器对象。 C#开发实战1200 例(第II卷) 836 (4)CryptoStream 类 CryptoStream 类定义将数据流链接到加密转换的流,其构造函数的语法格式如下: public CryptoStream(Stream stream,ICryptoTransform transform,CryptoStreamMode mode) 参数说明  stream:对其执行加密转换的流。  transform:要对流执行的加密转换。  mode:CryptoStreamMode 枚举值之一,CryptoStreamMode 枚举值及说明如表19.2 所示。 表19.2 CryptoStreamMode枚举值及说明 枚 举 值 说 明 Read 对加密流的读访问 Write 对加密流的写访问 另外,在向加密或解密流中写入数据时用到CryptoStream 类的Write 方法,该方法将一个字节序列写入当 前CryptoStream,并将流中的当前位置提升写入的字节数,其语法格式如下: public override void Write(byte[] buffer,int offset,int count) 参数说明  buffer:字节数组,此方法将count 个字节从buffer 复制到当前流。  offset:buffer 中的字节偏移量,从此偏移量开始将字节复制到当前流。  count:要写入当前流的字节数。  说明:DES 类和CryptoStream 类位于System.Security.Cryptography 命名空间下。 设 计过程 (1)打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为EncryptDataReport。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加两个RichTextBox 控件,分别用来 输入聊天信息和显示聊天信息;添加4 个TextBox 控件,分别用来输入端口号和显示已发送数据报、已接收数 据报、丢失数据报;添加4 个Button 控件,分别用来执行设置端口号、发送聊天信息、清空聊天信息和关闭应 用程序操作。 (3)程序主要代码如下。 Frm_Main 窗体的后台代码中,首先创建程序所需要的.NET 对象及公共变量,代码如下: #region 定义全局对象及变量 private IPEndPoint Server; //服务器端 private IPEndPoint Client; //客户端 private Socket mySocket; //套接字 private EndPoint ClientIP; //IP地址 byte[] buffer, data; //接收缓存 bool blFlag = true; //标识是否第一次发送信息 bool ISPort = false; //判断端口打开 int SendNum1, ReceiveNum1, DisNum1; //记录窗体加载时的已发送\已接收\丢失的数据报 int SendNum2, ReceiveNum2, DisNum2; //记录当前已发送\已接收\丢失的数据报 int SendNum3, ReceiveNum3, DisNum3; //缓存已发送\已接收\丢失的数据报 int port; //端口号 #endregion Frm_Main 窗体加载时,初始化已发送、已接收和丢失的数据报,并使用全局变量记录,实现代码如下: //初始化已发送、已接收和丢失的数据报 private void Form1_Load(object sender, EventArgs e) { if (blFlag == true) { IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties(); //创建一个IPGlobalProperties 对象 UdpStatistics myUdpStat = null; //声明UdpStatistics 对象 myUdpStat = NetInfo.GetUdpIPv4Statistics(); //创建UdpStatistics 对象 第19章 加密与解密技术 837 SendNum1 = Int32.Parse(myUdpStat.DatagramsSent.ToString()); //记录发送的数据报 ReceiveNum1 = Int32.Parse(myUdpStat.DatagramsReceived.ToString()); //记录接收的数据报 DisNum1 = Int32.Parse(myUdpStat.IncomingDatagramsDiscarded.ToString()); //记录丢失的数据报 } } 单击“设置”按钮,使用指定的端口号连接服务器端与客户端,并开始接收消息。“设置”按钮的Click 事件的代码如下: private void button4_Click(object sender, EventArgs e) //设置端口号 { try { port = Convert.ToInt32(textBox4.Text); //记录端口号 CheckForIllegalCrossThreadCalls = false; //指定线程中可以调用窗体的控件对象 buffer = new byte[1024]; data = new byte[1024]; Server = new IPEndPoint(IPAddress.Any, port); //创建服务器端 Client = new IPEndPoint(IPAddress.Broadcast, port); //创建客户端 ClientIP = (EndPoint)Server; //获取服务器端IP 地址 //创建Socket 对象 mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //设置Socket 网络操作 mySocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); mySocket.Bind(Server); //绑定服务器端 //开始接收消息 mySocket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref ClientIP, new AsyncCallback(StartLister), null); ISPort = true; //打开指定端口号 } catch { } } 单击“发送”按钮,首先判断是否有打开的端口,如果没有,弹出提示信息,否则根据发送和接收的消息 计算已发送、已接收和丢失的数据报,并显示在相应的文本框中,然后使用DES对要发送的消息进行加密发送。 “发送”按钮的Click事件的代码如下: //发送信息 private void button2_Click(object sender, EventArgs e) { if (ISPort == true) //判断是否有打开的端口号 { IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties(); UdpStatistics myUdpStat = null; myUdpStat = NetInfo.GetUdpIPv4Statistics(); try { if (blFlag == false) //非第一次发送 { SendNum2 = Int32.Parse(myUdpStat.DatagramsSent.ToString()); ReceiveNum2 = Int32.Parse(myUdpStat.DatagramsReceived.ToString()); DisNum2 = Int32.Parse(myUdpStat.IncomingDatagramsDiscarded.ToString()); textBox1.Text = Convert.ToString(SendNum2 - SendNum3); textBox2.Text = Convert.ToString(ReceiveNum2 - ReceiveNum3); textBox3.Text = Convert.ToString(DisNum2 - DisNum3); } SendNum2 = Int32.Parse(myUdpStat.DatagramsSent.ToString()); ReceiveNum2 = Int32.Parse(myUdpStat.DatagramsReceived.ToString()); DisNum2 = Int32.Parse(myUdpStat.IncomingDatagramsDiscarded.ToString()); SendNum3 = SendNum2; //记录本次的发送数据报 ReceiveNum3 = ReceiveNum2; //记录本次的接收数据报 DisNum3 = DisNum2; //记录本次的丢失数据报 if (blFlag == true) //第一次发送 { textBox1.Text = Convert.ToString(SendNum2 - SendNum1); textBox2.Text = Convert.ToString(ReceiveNum2 - ReceiveNum1); textBox3.Text = Convert.ToString(DisNum2 - DisNum1); blFlag = false; C#开发实战1200 例(第II卷) 838 } } catch (Exception ex) { MessageBox.Show(ex.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } string str = EncryptDES(rtbSend.Text, "mrsoftxk"); //加密要发送的信息 data = Encoding.Unicode.GetBytes(str); mySocket.SendTo(data, data.Length, SocketFlags.None, Client); //发送消息 rtbSend.Text = ""; } else { MessageBox.Show("请首先打开端口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); button4.Focus(); } } 上面的代码中用到了EncryptDES 方法,该方法为自定义的、返回值类型为string 的方法,主要用来使用 DES 加密数据报,它有两个string 类型的参数,分别用来表示待加密的字符串和加密密钥,返回值为加密后的 字符串。EncryptDES 方法的实现代码如下: #region DES 加密字符串 /// ///DES 加密字符串 /// ///待加密的字符串 ///加密密钥,要求为8 位 ///加密成功返回加密后的字符串,失败返回源字符串 public string EncryptDES(string str, string key) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(key.Substring(0, 8)); //将加密密钥转换为字节数组 byte[] rgbIV = Keys; //记录原始密钥数组 byte[] inputByteArray = Encoding.UTF8.GetBytes(str); //将加密字符串转换为字节数组 DESCryptoServiceProvider myDES = new DESCryptoServiceProvider(); //创建加密对象 MemoryStream MStream = new MemoryStream(); //创建内存数据流 //创建加密流对象 CryptoStream CStream = new CryptoStream(MStream, myDES.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); CStream.Write(inputByteArray, 0, inputByteArray.Length); //向加密流中写入数据 CStream.FlushFinalBlock(); //释放加密流对象 return Convert.ToBase64String(MStream.ToArray()); //返回内存流中的数据 } catch { return str; } } #endregion 秘 笈心法 心法领悟575:如何根据标点符号分行? 根据标点符号分行时,首先要使用string 类的Split 方法分割字符串,然后再通过“\n”回车换行符将分割 的字符串换行显示。根据标点符号分行的代码如下: string oldstr = textBox1.Text.Trim(); string[] newstr = oldstr.Split('。'); for (int i = 0; i < newstr.Length; i++) { if (richTextBox1.Text == "") richTextBox1.Text = newstr[i].ToString(); else richTextBox1.Text += "\n" + newstr[i].ToString(); } 第19章 加密与解密技术 839 实例576 使用one-time pad算法加密数据 光盘位置:光盘\MR\19\576 高级 趣味指数: 实 例说明 在密码学里,有一种理想的加密方案,叫做一次一密乱码本,即 one-time pad 算法,该算法是最安全的加密算法,双方一旦安全交换 了密钥,之后交换信息的过程就可以保证绝对安全。本实例使用C# 实现了one-time pad 加密算法,实例运行效果如图19.6 所示。  注意:程序中使用one-time pad 算法时,一定要保证密钥和密文 的长度是一样的。 关 键技术 本实例在实现one-time pad 加密算法时,主要用到了Encoding 类的GetBytes 方法和GetString 方法,下面 分别对它们进行详细介绍。 (1)Encoding 类的GetBytes方法 Encoding 类表示字符编码,其GetBytes方法主要用来将一组字符编码为一个字节序列,该方法为可重载方 法,本实例中用到的它的重载形式如下: public virtual byte[] GetBytes(string s) 参数说明  s:字符串。  返回值:一个字节数组,包含对指定的字符集进行编码的结果。  说明:Encoding 类位于System.Text 命名空间下。 (2)Encoding 类的GetString方法 Encoding 类的GetString方法主要用来将一个字节序列解码为一个字符串,该方法为可重载方法,本实例中 用到的它的重载形式如下: public virtual string GetString(byte[] bytes) 参数说明  bytes:包含要解码的字节序列的字节数组。  返回值:包含指定字节序列解码结果的字符串。 设 计过程 (1) 打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为OneTimePadArithmetic。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加4 个TextBox 控件,分别用来输入 要加密的数据和密钥,以及显示加密后的数据和解密后的数据;添加两个Button控件,分别用来实现使用one-time pad 算法加密数据和解密数据的功能。 (3)程序主要代码如下。 在Frm_Main 窗体中输入要加密的数据和密钥后,单击“加密”按钮,使用one-time pad 算法对输入的数据 进行加密,实现代码如下: private void button1_Click(object sender, EventArgs e) { textBox2.Text = ""; //清空文本框 Encoding encoding = Encoding.Default; //获取字符编码 byte[] btData = encoding.GetBytes(textBox1.Text); //将要加密的数据转换为字节数组 byte[] btKey = encoding.GetBytes(textBox4.Text); //将密钥转换为字节数组 图19.6 使用one-time pad 算法加密数据 C#开发实战1200 例(第II卷) 840 if (btData.Length == btKey.Length) //判断长度是否相等 { byte[] btEncrypt = Encrypt(btData, btKey); //加密数据 for (int i = 0; i < btEncrypt.Length; i++) //遍历加密后的字节数组 { textBox2.Text += btEncrypt[i]; //显示文本框中 } } } 上面的代码中用到了Encrypt 方法,该方法为自定义的、返回值类型为byte[]的方法,主要用来对指定的数 据使用one-time pad 算法进行加密。Encrypt方法的实现代码如下: public static byte[] Encrypt(byte[] btData, byte[] btKey) { if (btKey.Length != btData.Length) //判断长度是否相等 { MessageBox.Show("请确保要加密数据的长度与密钥的长度一致!"); } byte[] btResult = new byte[btData.Length]; //声明一个字节数组,用来存储加密数据 for (int i = 0; i < btResult.Length; ++i) //遍历字节数组 { btResult[i] = (byte)(btKey[i] ^ btData[i]); //为字节数组赋值 } return btResult; //返回得到的加密数据 } 单击“解密”按钮,调用Encrypt 方法对加密过的数据进行逆向加密,并返回一个byte[]数组,然后使用 Encoding 类的GetString方法从该数组中获取解密字符串。“解密”按钮的Click事件的代码如下: private void button2_Click(object sender, EventArgs e) { Encoding encoding = Encoding.Default; //获取字符编码 byte[] btData = encoding.GetBytes(textBox1.Text); //将要加密的数据转换为字节数组 byte[] btKey = encoding.GetBytes(textBox4.Text); //将密钥转换为字节数组 if (btData.Length == btKey.Length) //判断长度是否相等 { byte[] btDecrypt = Encrypt(Encrypt(btData, btKey), btKey); //解密数据 textBox3.Text = encoding.GetString(btDecrypt); //将解密后的字节数组转换为字符串并显示 } } 秘 笈心法 心法领悟576:如何在字符串中添加多个空格? 开发程序时,有时会根据需要在字符串中添加一些空格,这时可以使用string 类的Insert方法,该方法可以 在字符串中的指定位置插入一个新的字符串(包括空格)。在字符串中添加空格的代码如下: textBox3.Text = textBox1.Text.Insert(Convert.ToInt32(textBox2.Text.Trim()), " "); 实例577 使用伪随机数加密技术加密用户登录密码 光盘位置:光盘\MR\19\577 高级 趣味指数: 实 例说明 为了保障用户登录密码的安全,本实例使用伪随机数技术对用 户的登录密码进行加密,运行本实例,当用户在“登录密码”文本 框中输入登录密码时,程序会自动将使用过伪随机数加密技术加密 过的登录密码显示在下面的“加密密码”文本框中,单击“登录” 按钮,程序对“加密密码”文本框中的加密数据进行解密,然后再 与用户输入的登录密码相比较,如果相同,则登录成功;否则,登 录失败。实例运行效果如图19.7 所示。 图19.7 使用伪随机数加密技术 加密用户登录密码 第19章 加密与解密技术 841 关 键技术 本实例对用户登录密码加密时用到伪随机数加密技术,伪随机数加密技术实质上就是通过伪随机数序列使 登录密码字符串的字节值发生变化而产生密文,由于相同的初值能得到相同的随机数序列,因此,可以采用同 样的伪随机数序列来对密文进行解密。产生伪随机数时主要用到Random 类,该类表示伪随机数生成器,它是 一种能够产生满足某些随机性统计要求的数字序列的设备,其Next方法用来返回随机数,语法格式如下: public virtual int Next(int maxValue) 参数说明  maxValue:要生成的随机数的上界(随机数不能取该上界值),maxValue 必须大于等于零。  返回值:大于等于零且小于maxValue 的32 位带符号整数,即返回值的范围通常包括零但不包括 maxValue;不过,如果maxValue 等于零,则返回maxValue。 设 计过程 (1)打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为PRanDataEncrypt。 (2)更改默认窗体Form1 的Name属性为Frm_Main,在该窗体中添加3 个TextBox 控件,分别用来输入 登录用户、登录密码和显示加密密码;添加两个Button 控件,分别用来执行用户登录和清空文本框操作。 (3)程序主要代码如下。 Frm_Main 窗体的后台代码中,首先定义加密用户密码所用的伪随机数,代码如下: //定义加密用户密码所用的伪随机数 private string randStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; 当在“登录密码”文本框中输入登录密码时,实时将使用伪随机数加密过的登录密码显示在“加密密码” 文本框中,实现代码如下: private void textBox2_TextChanged(object sender, EventArgs e) { textBox3.Text = EncryptPwd(textBox2.Text); //显示加密后的用户登录密码 } 上面的代码中用到EncryptPwd 方法,该方法为自定义的、返回值类型为string 的方法,主要用来使用伪随 机数技术加密用户登录密码,它有一个参数,用来表示用户登录密码。EncryptPwd 方法的实现代码如下: /// /// 使用伪随机数加密用户登录密码 /// /// 用户登录密码 /// 加密后的用户登录密码 private string EncryptPwd(string str) { byte[] btData = Encoding.Default.GetBytes(str); //将登录密码转换为字节数组 int j, k, m; int len = randStr.Length; //记录伪随机数长度 StringBuilder sb = new StringBuilder(); //创建StringBuilder对象 Random rand = new Random(); //创建Random 对象 for (int i = 0; i < btData.Length; i++) { j = (byte)rand.Next(6); //产生伪随机数 btData[i] = (byte)((int)btData[i] ^ j); //使用伪随机数对密码字节数组进行移位 k = (int)btData[i] % len; m = (int)btData[i] / len; m = m * 8 + j; sb.Append(randStr.Substring(k, 1) + randStr.Substring(m, 1)); //组合加密字符串 } return sb.ToString(); //返回生成的加密字符串 } 单击“登录”按钮,判断“加密密码”文本框是否为空。如果不为空,调用DecryptPwd 方法解密“加密 密码”文本框中的字符串;然后使用解密后的字符串与“登录密码”文本框中的字符串相比较,如果相同,则 用户登录成功;否则,弹出提示信息。“登录”按钮的Click事件代码如下: C#开发实战1200 例(第II卷) 842 private void button1_Click(object sender, EventArgs e) { if (textBox3.Text != "") { if (DecryptPwd(textBox3.Text) == textBox2.Text) //对加密过的登录密码进行解密 MessageBox.Show("用户登录成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); else MessageBox.Show("用户密码错误!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } 上面的代码中用到了DecryptPwd 方法,该方法为自定义的、返回值类型为string 的方法,主要用来解密用 户登录密码,它有一个参数,主要用来表示经过加密的用户登录密码。DecryptPwd 方法的实现代码如下: /// /// 解密用户登录密码 /// /// 经过加密的用户登录密码 /// 解密后的用户登录密码 private string DecryptPwd(string str) { try { int j, k, m, n = 0; int len = randStr.Length; //获取伪随机数长度 byte[] btData = new byte[str.Length / 2]; //定义一个字节数组,并指定长度 for (int i = 0; i < str.Length; i += 2) //对登录密码进行解密 { k = randStr.IndexOf(str[i]); m = randStr.IndexOf(str[i + 1]); j = m / 8; m = m - j * 8; btData[n] = (byte)(j * len + k); btData[n] = (byte)((int)btData[n] ^ m); n++; } return Encoding.Default.GetString(btData); //返回解密后的登录密码 } catch { return ""; } } 秘 笈心法 心法领悟577:如何将字符串颠倒输出? 颠倒输出字符串时,可以先将要输出的字符串保存到一个char 类型的数组中,然后使用Array 类的Reverse 方法。将字符串颠倒输出的代码如下: string str1 = textBox1.Text.Trim(); char[] charstr = str1.ToCharArray(); Array.Reverse(charstr); string str2 = new string(charstr); textBox2.Text = str2; 实例578 以XML格式导入导出密钥 光盘位置:光盘\MR\19\578 高级 趣味指数: 实 例说明 本实例主要实现以XML 格式导入导出密钥,从而实现对数据进行加密和解密的功能。运行本实例,首先 在窗体中显示生成的公钥和私钥,然后输入明文数据,单击“加密”按钮,对输入的明文数据进行加密;单击 “解密”按钮,对加密后的数据进行解密。实例运行效果如图19.8 所示。 第19章 加密与解密技术 843 图19.8 以XML 格式导入导出密钥 关 键技术 本实例实现时主要用到了RSACryptoServiceProvider 类的ToXmlString方法、Encrypt 方法和Decrypt 方法, 下面对本实例中用到的关键技术进行详细讲解。 (1)RSACryptoServiceProvider 类的ToXmlString方法 RSACryptoServiceProvider 类用来使用加密服务提供程序(CSP)提供的RSA 算法的实现执行不对称加密和 解密,其ToXmlString 方法主要用来创建并返回包含当前RSA 对象的密钥的XML 字符串,该方法的语法格式 如下: public override string ToXmlString(bool includePrivateParameters) 参数说明  includePrivateParameters:true 表示同时包含RSA公钥和私钥,false 表示仅包含公钥。  返回值:包含当前RSA对象的密钥的XML字符串。  说明:RSACryptoServiceProvider 类位于System.Security.Cryptography 命名空间下。 (2)RSACryptoServiceProvider 类的Encrypt方法 该方法主要使用RSA算法对数据进行加密,其语法格式如下: public byte[] Encrypt(byte[] rgb,bool fOAEP) 参数说明  rgb:要加密的数据。  fOAEP:如果为true,则使用OAEP 填充(仅在运行Microsoft Windows XP 或更高版本的计算机上可用) 执行直接的RSA 加密;如果为false,则使用PKCS#1 1.5 版填充。  返回值:字节数组,表示已加密的数据。 (3)RSACryptoServiceProvider 类的Decrypt方法 该方法主要使用RSA算法对数据进行解密,其语法格式如下: public byte[] Decrypt(byte[] rgb,bool fOAEP) 参数说明  rgb:要解密的数据。  fOAEP:如果为true,则使用OAEP 填充(仅在运行Microsoft Windows XP 或更高版本的计算机上可用) 执行直接的RSA 解密;如果为false,则使用PKCS#1 1.5 版填充。  返回值:字节数组,表示已解密的数据,它是加密前的原始纯文本。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为KeyToXML。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加5 个TextBox 控件,分别用来显示 C#开发实战1200 例(第II卷) 844 公钥、显示私钥、输入明文数据、显示加密后的数据和显示解密后的数据;添加两个Button 控件,分别用来执 行数据加密和解密操作。 (3)程序主要代码如下。 在Frm_Main 窗体的后台代码中,首先创建RSACryptoServiceProvider 对象,并且定义一个字节数组,用来 存储临时数据,代码如下: RSACryptoServiceProvider RSACrypto = new RSACryptoServiceProvider(); //创建RSA 算法加密解密对象 byte[] M_bt_Data; //定义一个字节数组,用来存储临时数据 Frm_Main 窗体加载时,在文本框显示程序自动生成的公钥和私钥数据,代码如下: private void Frm_Main_Load(object sender, EventArgs e) { this.textBox1.Text = RSACrypto.ToXmlString(true); //显示生成的公钥 this.textBox2.Text = RSACrypto.ToXmlString(false); //显示生成的私钥 } 当用户输入明文数据之后,单击“加密”按钮,调用RSACryptoServiceProvider 类的Encrypt方法对数据进 行加密,并且使用Encoding 类的UTF8 编码方式的GetString 方法得到加密后的数据,显示文本框中。“加密” 按钮的Click事件代码如下: private void button1_Click(object sender, EventArgs e) { if (textBox3.Text != "") //判断是否输入了要加密的数据 { byte[] P_bt_Encrypt = Encoding.UTF8.GetBytes(textBox3.Text); //将要加密的数据转换为字节数组 M_bt_Data = RSACrypto.Encrypt(P_bt_Encrypt, false); //加密数据 textBox4.Text = Encoding.UTF8.GetString(M_bt_Data); //显示加密数据 } } 单击“解密”按钮,调用RSACryptoServiceProvider 类的Decrypt方法对加密过的数据进行解密,并且使用 Encoding 类的UTF8 编码方式的GetString 方法得到解密后的数据,显示文本框中。“解密”按钮的Click 事 件代码如下: private void button2_Click(object sender, EventArgs e) { if (textBox4.Text != "") //判断是否有加密过的数据 { byte[] P_bt_Decrypt = RSACrypto.Decrypt(M_bt_Data, false); //对数据进行解密 textBox5.Text = Encoding.UTF8.GetString(P_bt_Decrypt); //显示解密数据 } } 秘 笈心法 心法领悟578:如何判断字符串是否为日期格式? 判断字符串是否为日期格式时,可以使用正则表达式。验证日期格式的正则表达式主要有以下3 种: \b(?\d{2,4})/(?\d{1,2})/(?\d{1,2})\b 或 \b(?\d{2,4})-(?\d{1,2})-(?\d{1,2})\b 或 \b(?\d{2,4})年(?\d{1,2})月(?\d{1,2})日\b 实例579 以参数格式导入导出密钥 光盘位置:光盘\MR\19\579 高级 趣味指数: 实 例说明 本实例主要实现以参数格式导入导出密钥,从而实现对数据进行加密和解密的功能。运行本实例,在窗体 第19章 加密与解密技术 845 中输入明文数据,单击“加密”按钮,对输入的明文数据进行加密;单击“解 密”按钮,对加密后的数据进行解密。实例运行效果如图19.9 所示。 关 键技术 本实例实现时主要用到了RSACryptoServiceProvider 类的ExportParameters 方法、ImportParameters 方法、Encrypt 方法和Decrypt 方法,下面对本实例 中用到的关键技术进行详细讲解。 (1)RSACryptoServiceProvider 类的ExportParameters 方法 该方法主要用来导出RSAParameters标准参数,其语法格式如下: public override RSAParameters ExportParameters(bool includePrivateParameters) 参数说明  includePrivateParameters:如果要包括私有参数,则为true;否则为false。  返回值:RSA 算法的标准参数。 (2)RSACryptoServiceProvider 类的ImportParameters 方法 该方法主要用来导入指定的RSAParameters标准参数,其语法格式如下: public override void ImportParameters(RSAParameters parameters) 参数说明 parameters:RSA 算法的标准参数。  说明:关于RSACryptoServiceProvider 类的Encrypt 方法和Decrypt 方法的详细讲解,请参见实例578 中的 关键技术。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为KeyToParameter。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加3 个TextBox 控件,分别用来输入 明文数据、显示加密后的数据和解密后的数据;添加两个Button 控件,分别用来执行数据加密和解密操作。 (3)程序主要代码如下。 Frm_Main 窗体的后台代码中,首先创建RSACryptoServiceProvider 对象和RSAParameters 标准参数对象, 并且定义一个字节数组,用来存储临时数据,代码如下: RSACryptoServiceProvider RSACrypto; //声明RSA 算法加密解密对象 RSAParameters RSAParame; //声明RSAParameters 参数对象 byte[] M_bt_Data; //定义一个字节数组,用来存储临时数据 在Frm_Main 窗体的构造函数中,调用RSACryptoServiceProvider 类的ImportParameters 方法导入 RSAParameters标准参数,实现代码如下: public Frm_Main() { InitializeComponent(); RSACrypto = new RSACryptoServiceProvider(); //初始化RSA 算法加密解密对象 RSAParame = RSACrypto.ExportParameters(true); //初始化RSAParameters 参数 RSACrypto.Clear(); //清空RSACryptoServiceProvider 对象 RSACrypto = new RSACryptoServiceProvider(); //初始化RSA 算法加密解密对象 RSACrypto.ImportParameters(RSAParame); //导入密钥 } 当用户输入明文数据之后,单击“加密”按钮,调用RSACryptoServiceProvider 类的Encrypt方法对数据进 行加密,并且使用Encoding 类的UTF8 编码方式的GetString 方法得到加密后的数据,显示文本框中。“加密” 按钮的Click事件代码如下: private void button1_Click(object sender, EventArgs e) { if (textBox1.Text != "") //判断是否输入了要加密的数据 { 图19.9 以参数格式导入导出密钥 C#开发实战1200 例(第II卷) 846 byte[] P_bt_Encrypt = Encoding.UTF8.GetBytes(textBox1.Text); //将要加密的数据转换为字节数组 M_bt_Data = RSACrypto.Encrypt(P_bt_Encrypt, false); //加密数据 textBox2.Text = Encoding.UTF8.GetString(M_bt_Data); //显示加密数据 } } 单击“解密”按钮,调用RSACryptoServiceProvider 类的Decrypt方法对加密过的数据进行解密,并且使用 Encoding 类的UTF8 编码方式的GetString 方法得到解密后的数据,显示文本框中。“解密”按钮的Click 事 件代码如下: private void button2_Click(object sender, EventArgs e) { if (textBox2.Text != "") //判断是否有加密过的数据 { byte[] P_bt_Decrypt = RSACrypto.Decrypt(M_bt_Data, false); //对数据进行解密 textBox3.Text = Encoding.UTF8.GetString(P_bt_Decrypt); //显示解密数据 } } 秘 笈心法 心法领悟579:巧截字符串中的数字。 截取字符串中的数字时,可以先使用CharEnumerator 对象的MoveNext 方法循环访问字符串中的每个字符, 并将字符用System.Text.Encoding 类中ASCII 编码方式的GetBytes 方法进行编码,然后判断经过编码之后的字符 的ASCII码值是否介于48和57之间,如果是,则将其显示在textBox文本框中。截取字符串中数字的代码如下: CharEnumerator CEnumerator = textBox1.Text.GetEnumerator(); while (CEnumerator.MoveNext()) { byte[] array = new byte[1]; array = System.Text.Encoding.ASCII.GetBytes(CEnumerator.Current.ToString()); int asciicode = (short)(array[0]); if (asciicode >= 48 && asciicode <= 57) { textBox2.Text += CEnumerator.Current.ToString(); } } 19.2 文件的加密与解密 实例580 文本文件加密与解密 光盘位置:光盘\MR\19\580 高级 趣味指数: 实 例说明 在本实例的窗体中,首先选择要加密或解密的文本文件,然后单击“加 密”或“解密”按钮对文本文件进行加密或解密。实例运行效果如图19.10 所示。 关 键技术 本实例实现时主要用到了System.Security.Cryptography命名空间下的 RijndaelManaged 类的CreateDecryptor 方法、CreateEncryptor 方法和CryptoStream 类的Write 方法,下面对本实 例中用到的关键技术进行详细讲解。 (1)RijndaelManaged 类 该类是访问System.Security.Cryptography.Rijndael 对称加密算法的托管版本,其语法格式如下: public sealed class RijndaelManaged : Rijndael 图19.10 文本文件加密与解密 第19章 加密与解密技术 847  注意:此算法支持128、192或256 位的密钥长度。 (2)CreateDecryptor 方法 该方法位于RijndaelManaged 类中,使用指定的Key和初始化向量(IV)创建对称的Rijndael 解密器对象, 其语法格式如下: public override IcryptoTransform CreateDecryptor (byte[] rgbKey,byte[] rgbIV) 参数说明  rgbKey:用于对称算法的机密密钥。  rgbIV:用于对称算法的IV。  返回值:对称的Rijndael 解密器对象。 (3)CreateEncryptor 方法 该方法位于RijndaelManaged 类中,使用指定的Key和初始化向量(IV)创建对称的Rijndael 加密器对象, 其语法格式如下: public override ICryptoTransform CreateEncryptor (byte[] rgbKey,byte[] rgbIV) 参数说明  rgbKey:用于对称算法的机密密钥。  rgbIV:用于对称算法的IV。  返回值:对称的Rijndael 加密器对象。  说明:关于CryptoStream 类的Write 方法的详细讲解,请参见实例575中的关键技术。 设 计过程 (1) 打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为EncryptTextFileOne。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加一个TextBox 控件,用来显示文本 文件路径;添加一个OpenFileDialog 控件,用来选择要加密或解密的文本文件;添加3 个Button 控件,用来执 行选择文本文件、加密和解密操作。 (3)程序主要代码如下。 单击“加密”按钮实现对选择的文本文件进行加密,“加密”按钮的Click事件的代码如下: private void button2_Click(object sender, EventArgs e) { if (textBox1.Text == "") //若未选择要加密的文本文件 { MessageBox.Show("请选择要加密的文件"); } //如果没有选择则弹出提示 else { try{ string strPath = textBox1.Text; //加密文件的路径 int intLent=strPath.LastIndexOf("\\")+1; //设置截取的起始位置 int intLong = strPath.Length; //设置截取的长度 string strName = strPath.Substring(intLent,intLong-intLent); //要加密的文件名称 int intTxt = strName.LastIndexOf("."); //设置截取的起始位置 int intTextLeng = strName.Length; //设置截取的长度 string strTxt = strName.Substring(intTxt,intTextLeng-intTxt); //取出文件的扩展名 strName = strName.Substring(0,intTxt); //加密后的文件名及路径 string strOutName = strPath.Substring(0, strPath.LastIndexOf("\\") + 1) + strName + "Out" + strTxt; //加密文件密钥 byte[] key = { 24, 55, 102, 24, 98, 26, 67, 29, 84, 19, 37, 118, 104, 85, 121, 27, 93, 86, 24, 55, 102, 24, 98, 26, 67, 29, 9, 2, 49, 69, 73, 92 }; byte[] IV ={ 22, 56, 82, 77, 84, 31, 74, 24, 55, 102, 24, 98, 26, 67, 29, 99 }; RijndaelManaged myRijndael = new RijndaelManaged(); FileStream fsOut = File.Open(strOutName, FileMode.Create, FileAccess.Write); FileStream fsIn = File.Open(strPath, FileMode.Open, FileAccess.Read); //写入加密文本文件 CryptoStream csDecrypt = new CryptoStream(fsOut, myRijndael.CreateEncryptor(key, IV), CryptoStreamMode.Write); BinaryReader br = new BinaryReader(fsIn); //创建阅读器来读加密文本 csDecrypt.Write(br.ReadBytes((int)fsIn.Length), 0, (int)fsIn.Length); //将数据写入加密文本 C#开发实战1200 例(第II卷) 848 csDecrypt.FlushFinalBlock(); csDecrypt.Close(); //关闭CryptoStream 对象 fsIn.Close(); //关闭FileStream 对象 fsOut.Close(); //关闭FileStream 对象 if (MessageBox.Show("加密成功!加密后的文件名及路径为:\n" + strOutName + ",是否删除源文件", "信息提示", MessageBoxButtons. YesNo) == DialogResult.Yes) { File.Delete(strPath); //删除指定文件 textBox1.Text = ""; //清空文本框 }else { textBox1.Text = ""; } } catch (Exception ee) //如果出现异常 { MessageBox.Show(ee.Message); //输出异常信息 } } } 单击“解密”按钮实现对加密的文本文件进行解密,“解密”按钮的Click事件代码如下: private void button3_Click(object sender, EventArgs e) { if (textBox1.Text == "") //若未选择要解密的文件 { MessageBox.Show("请选择要解密的文件路径"); //如果没有选择则弹出提示 } else { string strPath = textBox1.Text; //加密文件的路径 int intLent = strPath.LastIndexOf("\\") + 1; //设置截取字符串的起始位置 int intLong = strPath.Length; //设置截取长度 string strName = strPath.Substring(intLent, intLong - intLent); //要加密的文件名称 int intTxt = strName.LastIndexOf("."); //截取字符串的起始位置 int intTextLeng = strName.Length; //截取长度 strName = strName.Substring(0, intTxt); //获取扩展名 if (strName.LastIndexOf("Out") != -1) { strName = strName.Substring(0, strName.LastIndexOf("Out")); } else { strName = strName + "In"; } //加密后的文件名及路径 string strInName = strPath.Substring(0, strPath.LastIndexOf("\\") + 1) + strName + ".txt"; //解密文件密钥 byte[] key = { 24, 55, 102, 24, 98, 26, 67, 29, 84, 19, 37, 118, 104, 85, 121, 27, 93, 86, 24, 55, 102, 24, 98, 26, 67, 29, 9, 2, 49, 69, 73, 92 }; byte[] IV ={ 22, 56, 82, 77, 84, 31, 74, 24, 55, 102, 24, 98, 26, 67, 29, 99 }; RijndaelManaged myRijndael = new RijndaelManaged(); //创建RijndaelManaged 对象 //创建FileStream 对象 FileStream fsOut = File.Open(strPath, FileMode.Open, FileAccess.Read); CryptoStream csDecrypt = new CryptoStream(fsOut, myRijndael.CreateDecryptor(key, IV), CryptoStreamMode.Read); StreamReader sr = new StreamReader(csDecrypt); //把文件读出来 StreamWriter sw = new StreamWriter(strInName); //解密后写入一个新文件 sw.Write(sr.ReadToEnd()); sw.Flush(); sw.Close(); sr.Close(); fsOut.Close(); if (MessageBox.Show("解密成功!解密后的文件名及路径为:"+strInName+",是否删除源文件", "信息提示", MessageBoxButtons.YesNo) == DialogResult.Yes) { File.Delete(strPath); //删除指定文件 textBox1.Text = ""; //清空文本框 } else { 第19章 加密与解密技术 849 textBox1.Text = ""; } } } 秘 笈心法 心法领悟580:如何存储变长字符串? 在程序中存储变长字符串时,需要使用StringBuilder对象。相对于string 对象来说,StringBuilder 对象是可 变的,不用生成中间对象,因此,在连接的字符串较多或字符串长度较长时,通常都使用StringBuilder 对象。 实例581 利用图片加密文件 光盘位置:光盘\MR\19\581 高级 趣味指数: 实 例说明 本实例在加密时,使用指定的图片生成加密密钥,然后对文本文件进 行加密;在解密时,使用加密时的图片生成解密密钥,然后对加密的文本 文件进行解密。运行本实例,首先打开一张图片,用来生成加密或解密的 密钥,然后选择要加密或解密的文本文件,最后单击“加密”或“解密” 按钮,实现对文本文件的加密或解密。实例运行效果如图19.11 所示。 关 键技术 本实例实现时主要用到了RC2CryptoServiceProvider 类、BinaryWriter 类的Write 方法、File 类的Delete 方法和Copy 方法,下面对本实例中用到 的关键技术进行详细讲解。 (1)RC2CryptoServiceProvider 类 该类定义访问RC2算法的加密服务提供程序(CSP)实现的包装对象,无法继承此类。 (2)BinaryWriter 类 该类以二进制形式将基元类型写入流,并支持用特定的编码写入字符串,其构造器的语法格式如下: public BinaryWriter (Stream output) 参数说明 output:表示输出流。 (3)BinaryWriter 类的Write 方法 该方法将一个无符号字节写入当前流,并将流的位置提升一个字节,其语法格式如下: public virtual void Write (byte value) 参数说明 value:表示要写入的无符号字节。 (4)File 类的Delete 方法 File 类提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建FileStream 对象,该类是个 静态类,其Delete方法用于删除指定的文件,如果指定的文件不存在,则引发异常。该方法的语法格式如下: public static void Delete (string path) 参数说明 path:表示要删除的文件的名称。 (5)File 类的Copy 方法 该方法将现有文件复制到新文件,不允许改写同名的文件,其语法格式如下: public static void Copy (string sourceFileName,string destFileName) 图19.11 利用图片加密文件 C#开发实战1200 例(第II卷) 850 参数说明  sourceFileName:要复制的文件。  destFileName:目标文件的名称,不能是一个目录或现有文件。 设 计过程 (1) 打开Visual Studio 2008开发环境,新建一个Windows窗体应用程序,并将其命名为EncryptTextFileTwo。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加一个TextBox 控件,用来显示加密 或解密文件的路径;添加一个OpenFileDialog 控件,用来选择要加密或解密的文件和打开密钥的图片;添加4 个Button 控件,分别用来执行加密、解密、打开文件和打开图片操作;添加一个PictureBox 控件,用于显示密 钥图片。 (3)程序主要代码如下。 单击“加密”按钮,实现利用图片对文本文件进行加密的功能,“加密”按钮的Click 事件的代码如下: private void button3_Click(object sender, EventArgs e) { try { if (pictureBox1.ImageLocation==null) //判断是否选择了图片 { MessageBox.Show("请选择一幅图片用于加密"); return; } //如果没有选择则弹出提示 if (textBox1.Text == "") //若未选择需要加密的文件 { MessageBox.Show("请选择加密文件路径"); return; } //如果没有选择则弹出提示 //图片流 FileStream fsPic = new FileStream(pictureBox1.ImageLocation, FileMode.Open, FileAccess.Read); //加密文件流 FileStream fsText = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read); //初始化对称算法的密钥和向量 byte[] bykey = new byte[16]; //定义存储密钥的字节数组 byte[] byIv = new byte[8]; //定义存储向量的字节数组 fsPic.Read(bykey, 0, 16); //把图片流写入密钥缓冲区 fsPic.Read(byIv, 0, 8); //把图片流写入向量缓冲区 //临时加密文件 string strPath = textBox1.Text; //加密文件的路径 int intLent = strPath.LastIndexOf("\\") + 1; int intLong = strPath.Length; string strName = strPath.Substring(intLent, intLong - intLent); //要加密的文件名称 string strLinPath = "C:\\" + strName; //临时加密文件路径 FileStream fsOut = File.Open(strLinPath, FileMode.Create, FileAccess.Write); //开始加密,首先创建RC2CryptoServiceProvider 对象 RC2CryptoServiceProvider desc = new RC2CryptoServiceProvider(); BinaryReader br = new BinaryReader(fsText); //创建BinaryReader 对象 //创建CryptoStream 对象,用于写入临时加密文件 CryptoStream cs = new CryptoStream(fsOut, desc.CreateEncryptor(bykey, byIv), CryptoStreamMode.Write); cs.Write(br.ReadBytes((int)fsText.Length), 0, (int)fsText.Length); //写入加密流 cs.FlushFinalBlock(); cs.Flush(); cs.Close(); fsPic.Close(); fsText.Close(); fsOut.Close(); File.Delete(textBox1.Text.TrimEnd()); //删除原文件 File.Copy(strLinPath, textBox1.Text); //复制加密文件 File.Delete(strLinPath); //删除临时文件 MessageBox.Show("加密成功"); pictureBox1.ImageLocation = null; textBox1.Text = ""; } catch (Exception ee) { MessageBox.Show(ee.Message); } } 第19章 加密与解密技术 851 单击“解密”按钮,实现利用图片对加密的文本文件进行解密的功能,“解密”按钮的Click事件的代码如下: private void button4_Click(object sender, EventArgs e) { try { //图片流 FileStream fsPic = new FileStream(pictureBox1.ImageLocation, FileMode.Open, FileAccess.Read); //解密文件流 FileStream fsOut = File.Open(textBox1.Text, FileMode.Open, FileAccess.Read); //初始化对称算法的密钥和向量 byte[] bykey = new byte[16]; //定义存储密钥的字节数组 byte[] byIv = new byte[8]; //定义存储向量的字节数组 fsPic.Read(bykey, 0, 16); //把图片流写入密钥缓冲区 fsPic.Read(byIv, 0, 8); //把图片流写入向量缓冲区 //创建临时解密文件 string strPath = textBox1.Text; //加密文件的路径 int intLent = strPath.LastIndexOf("\\") + 1; //获取不含文件名的路径长度 int intLong = strPath.Length; //获取含文件名的路径长度 //获取要解密文件的名称,即加密文件的名称 string strName = strPath.Substring(intLent, intLong - intLent); string strLinPath = "C:\\" + strName; //临时解密文件路径 FileStream fs = new FileStream(strLinPath, FileMode.Create, FileAccess.Write); //开始解密,首先创建RC2CryptoServiceProvider 对象 RC2CryptoServiceProvider desc = new RC2CryptoServiceProvider(); //创建CryptoStream 对象,用于读取加密文件 CryptoStream csDecrypt = new CryptoStream(fsOut, desc.CreateDecryptor(bykey, byIv), CryptoStreamMode.Read); BinaryReader sr = new BinaryReader(csDecrypt); //创建BinaryReader 对象 BinaryWriter sw = new BinaryWriter(fs); //创建BinaryWriter 对象 sw.Write(sr.ReadBytes(Convert.ToInt32(fsOut.Length))); //写入解密流 sw.Flush(); sw.Close(); sr.Close(); fs.Close(); fsOut.Close(); fsPic.Close(); csDecrypt.Flush(); File.Delete(textBox1.Text.TrimEnd()); //删除原文件 File.Copy(strLinPath, textBox1.Text); //复制加密文件 File.Delete(strLinPath); //删除临时文件 MessageBox.Show("解密成功"); //弹出提示信息 pictureBox1.ImageLocation = null; //清空图片 textBox1.Text = ""; //清空文本框 } catch (Exception ee) //如果出现异常 { MessageBox.Show(ee.Message); //输出异常 } } 秘 笈心法 心法领悟581:如何去除字符串尾空格? 去除字符串尾空格需要使用string 类的Trim 方法,该方法用来从字符串的开始和末尾处移除空白字符的所 有匹配项。例如,下面的代码用来去掉textBox1 文本框中字符串的尾空格,并将结果显示在textBox2 文本框中: textBox2.Text = textBox1.Text.Trim(); 实例582 对文件进行加密保护 光盘位置:光盘\MR\19\582 高级 趣味指数: 实 例说明 随着计算机的普及,文件的安全越来越重要,本实例使用C#制作了一个对文件进行加密保护的实例。运行 C#开发实战1200 例(第II卷) 852 本实例,选择要加密或解密的文件,用程序来判断是否是加密过的文件, 如果不是,输入加密密码,单击“加密”按钮,加密已选择的文件;如果 是,输入解密密码,单击“解密”按钮,解密选择的加密文件。实例运行 效果如图19.12 所示。 关 键技术 本实例制作对文件进行加密保护程序时,首先选择要加密或解密的文 件,并输入加密或解密密码,然后启动一个新的线程,使用输入的密码对 指定的文件进行加密或解密操作。另外,如果对文件执行的是加密操作,则加密成功后删除原文件。具体实现 过程中,主要用到了DES 类的CreateEncryptor 和CreateDecryptor 方法、CryptoStream 类的构造函数及其Write 方法。  说明:关于DES 类的CreateEncryptor 方法和CreateDecryptor 方法、CryptoStream 类的构造函数及其Write 方法的详细讲解,请参见实例575中的关键技术。 设 计过程 (1)打开Visual Studio 2008 开发环境,新建一个Windows窗体应用程序,并将其命名为ProtectFile。 (2)更改默认窗体Form1 的Name 属性为Frm_Main,在该窗体中添加一个OpenFileDialog 控件,用来显 示“打开”对话框;添加两个TextBox 控件,分别用来显示选择的文件路径和输入加密、解密密码;添加3 个 Button控件,分别用来执行选择加密或解密的文件、加密文件和解密文件操作;添加一个ProgressBar控件,用 来显示加密或解密的进度。 (3)程序主要代码如下。 Frm_Main 窗体加载时,首先将加密文件

7,762

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧