一共470多例winform 界面特效的源碼。算法
實例030 窗口顏色的漸變數據庫
實例說明編程
在程序設計時,能夠經過設置窗體的BackColor屬性來改變窗口的背景顏色。可是這個屬性改變後整個窗體的客戶區都會變成這種顏色,而且很是單調。若是窗體的客戶區能夠向標題欄同樣可以體現顏色的漸變效果,那麼窗體風格將會另有一番風味。本例設計了一個顏色漸變的窗體。效果如圖1.30所示。api
技術要點數組
C#中能夠經過Color.FromArgb( )方法返回一種顏色,下面詳細介紹一下該方法。安全
Color.FromArgb( )方法用來返回Color的顏色值,該方法語法結構以下:網絡
public static Color FromArgb (dom
int red,異步
int green,分佈式
int blue
)
參數說明以下。
l red:新Color的紅色份量值。有效值爲從0~255。
l green:新Color的綠色份量值。有效值爲從0~255。
l blue:新Color的藍色份量值。有效值爲從0~255。
l 返回值:此方法建立的Color。
該函數就是用3種不一樣的色值來返回一個顏色,而稍微的調整某一種顏色值就可使總體的顏色發生細微的變化,在窗體中至上而下每行填充一種稍微調整後的顏色,這樣總體看來就會產生漸變的效果。能夠利用窗體的Graphics對象對窗體進行繪圖,該對象能夠徹底操控窗體的客戶區。
注意:顏色值在0~255之間。
實現過程
(1)建立一個項目,將其命名爲Ex01_30,默認窗體爲Form1。
(2)在Form1窗體中添加Button用來使顏色漸變;添加TextBox控件用來輸入顏色RGB值。
(3)主要程序代碼。
觸發從新繪製事件的實現代碼以下:
private void button2_Click(object sender, EventArgs e)
{
InvokePaintBackground( );
this.Hide( );
this.Visible=true;
}
從新繪製窗體背景顏色的實現代碼以下:
protected override void OnPaintBackground(PaintEventArgs e)
{
int y, dy;
y = this.ClientRectangle.Location.Y;
dy = this.ClientRectangle.Height / 256;
for (int i = 255; i >= 0; i--)
{
Color c = new Color( );
c = Color.FromArgb(Convert.ToInt32(textBox1.Text.ToString( )), i,Convert.ToInt32(textBox2.Text.ToString( )));
SolidBrush sb = new SolidBrush(c);
Pen p = new Pen(sb, 1);
e.Graphics.DrawRectangle(p,this.ClientRectangle.X, y, this.Width,y+dy);
y = y + dy;
}
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
把窗體設置成單一的顏色。
利用Timer組體,使窗體動態改變顏色。
1.9 窗體動畫
本節主要對窗體進行動畫設置,在窗體上添加一些動畫效果,能夠爲操做者添加一些樂趣,下面的幾個例子將詳細介紹窗體動畫的相關技術。
實例031 窗體中的滾動字幕
實例說明
普通窗體中的文字位置都是固定的,一些窗體中須要讓文字動起來,例如一些廣告性較強的界面中須要作一些滾動的字幕。本例實現了一個具備滾動字幕效果的窗體,運行本例,單擊【演示】按鈕,看到窗口中的文字開始滾動。單擊【暫停】按鈕,可使字幕中止滾動。本例運行效果如圖1.31所示。
技術要點
滾動字幕的效果其實就是改變了文字的位置,在窗體中顯示一串文字最好的辦法就是利用Label控件。將Label控件的位置改變就能夠實現文字的位置變換,若是該控件的位置不斷的向水平方向移動,就會實現文字的滾動效果。改變Label控件的水平位置能夠經過改變Label控件的Left的值來實現。用Timer控件對文字的移動進行時間控制。
實現過程
(1)建立一個項目,將其命名爲Ex01_31,默認窗體爲Form1。
(2)在窗體上添加Label控件用來顯示消息;添加Button控件用來控制消息的運動;添加Timer控件用來控制滾動速度。
(3)主要程序代碼。
private void timer1_Tick(object sender, EventArgs e)//用Timer來控制滾動速度
{
label1.Left -= 2;
if (label1.Right < 0)
{
label1.Left = this.Width;
}
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true; //開始滾動
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Enabled = false; //中止滾動
}
注意:要特別注意文字滾動的方向問題,向左則減,向右則加。
觸類旁通
根據本實例,讀者能夠開發如下程序。
能夠在窗體中設置一個滾動的圖片。
能夠在窗體中設置一個滾動的提示信息。
實例032 動畫顯示窗體
實例說明
當用戶啓動程序後,普通的程序窗口都是瞬間顯示到屏幕上,這樣未免有些生硬。若是窗口可以慢慢的展示在用戶面前,將會是什麼樣的效果?本例設計的是一個動畫顯示的窗體,該程序運行後,窗體是慢慢的以拉伸的效果顯示到用戶的面前。當關閉時也是同樣慢慢的消失。本例運行效果如圖1.32所示。
技術要點
Windows提供了一個API函數Animate Window,該函數能夠實現窗體的動畫效果,AnimateWindow函數在C#中的聲明以下。
[DllImportAttribute("user32.dll")]
private static extern bool AnimateWindow(IntPtr hwnd, int dwTime, int dwFlags);
參數說明以下。
l hwnd:目標窗口句柄。
l dwTime:動畫的持續時間,數值越大動畫效果的時間就越長。
l DwFlags:DwFlags參數是動畫效果類型選項,該參數在C#中的聲明以下:
public const Int32 AW_HOR_POSITIVE = 0x00000001;
public const Int32 AW_HOR_NEGATIVE = 0x00000002;
public const Int32 AW_VER_POSITIVE = 0x00000004;
public const Int32 AW_VER_NEGATIVE = 0x00000008;
public const Int32 AW_CENTER = 0x00000010;
public const Int32 AW_HIDE = 0x00010000;
public const Int32 AW_ACTIVATE = 0x00020000;
public const Int32 AW_SLIDE = 0x00040000;
public const Int32 AW_BLEND = 0x00080000;
DwFlags參數可選值含義如表1.1所示
表1.1 參數說明
標 志 |
描 述 |
AW_SLIDE |
使用滑動類型。缺省則爲滾動動畫類型。當使用AW_CENTER標誌時,這個標誌就被忽略 |
AW_ACTIVE |
激活窗口。在使用了AW_HIDE標誌後不要使用這個標誌 |
AW_BLEND |
使用淡入效果。只有當hWnd爲頂層窗口的時候纔可使用此標誌 |
AW_HIDE |
隱藏窗口,缺省則顯示窗口 |
AW_CENTER |
若使用了AW_HIDE標誌,則使窗口向內重疊;若未使用AW_HIDE標誌,則使窗口向外擴展 |
AW_HOR_POSITIVE |
自左向右顯示窗口。該標誌能夠在滾動動畫和滑動動畫中使用。當使用AW_CENTER標誌時,該標誌將被忽略 |
AW_HOR_NEGATIVE |
自右向左顯示窗口。當使用了 AW_CENTER 標誌時該標誌被忽略 |
AW_VER_POSITIVE |
自頂向下顯示窗口。該標誌能夠在滾動動畫和滑動動畫中使用。當使用AW_CENTER標誌時,該標誌將被忽略 |
AW_VER_NEGATIVE |
自下向上顯示窗口。該標誌能夠在滾動動畫和滑動動畫中使用。當使用AW_CENTER標誌時,該標誌將被忽略 |
實現過程
(1)建立一個項目,將其命名爲Ex01_32,默認窗體爲Form1。
(2)在窗體上添加PictureBox控件。
(3)設置PictureBox控件的Image屬性。
(4)主要代碼以下。
public Form1( )
{
InitializeComponent( );
AnimateWindow(this.Handle, 300, AW_SLIDE + AW_VER_NEGATIVE);//開始窗體動畫
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{ //結束窗體動畫
AnimateWindow(this.Handle, 300, AW_SLIDE + AW_VER_NEGATIVE + AW_HIDE);
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
實現窗體的淡入淡出。
實現窗體從中間擴散顯示。
實例033 製做閃爍的窗體
實例說明
Windows系統中,當程序在後臺運行時,若是某個窗口的提示信息須要用戶瀏覽,該窗口就會不停的閃爍,這樣就會吸引用戶的注意。一樣,若是在本身的程序中使某個窗口不停的閃爍就會吸引用戶的注意。本例設計了一個閃爍的窗體,運行程序,單擊【開始閃爍】按鈕,窗體就會不停的閃爍,單擊【中止】按鈕,窗體就會中止閃爍。本例運行效果如圖1.33所示。
技術要點
Windows提供了一個API函數FlashWIndow,該函數可使窗體閃爍一下。FlashWIndow函數在C#中聲明以下:
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool FlashWindow(IntPtr handle, bool bInvert);
參數說明以下。
l handle:表示將要閃爍的窗體。
l bInvert:是否恢復狀態。
利用該函數只能使窗體閃爍一下,若是讓窗口不停地閃爍,就須要用一個Timer控件每隔一段時間就調用該函數使窗體閃爍。
實現過程
(1)建立一個項目,將其命名爲Ex01_33,默認窗體爲Form1。
(2)在窗體上添加PictureBox控件用來顯示窗體;添加Button、Timer控件用來開始和中止閃爍。
(3)設置PictureBox控件的Image屬性。
(4)主要程序代碼。
timer1的Tick事件處理代碼以下:
private void timer1_Tick(object sender, EventArgs e)
{
FlashWindow(this.Handle,true);
}
【開始閃爍】按鈕的單擊事件,用來啓動窗體閃爍:
private void button1_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
【中止】按鈕的單擊事件,用來中止窗體的閃爍:
private void button2_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
利用Visible屬性製做一個閃爍的圖片。
製做一個閃爍的按鈕。
實例034 直接在窗體上繪圖
實例說明
含有Graphics對象的控件都可以在其上進行繪圖,不少軟件就是經過Graphics對象來美化程序的主界面,由於窗體中含有Graphics對象,因此能夠將窗體看做一個大畫板,一個能夠在上面繪圖的特殊控件。本例設計了一個簡單的繪圖軟件,該軟件就利用了在窗體上繪圖的方法,運行本軟件能夠在窗體上進行繪圖。實例效果如圖1.34所示。
技術要點
窗體中含有Graphics對象,使用該對象就可以完成大部分繪圖功能,Graphics對象已經對Windows底層的一些繪圖API進行了封裝,使用起來比較方便。下面介紹Graphics對象的經常使用方法。
Graphics.DrawLine繪圖方法用來繪製一條鏈接由座標對指定的兩個點的線條。其語法結構以下:
public void DrawLine (Pen pen,int x1,int y1,int x2,int y2)
參數說明以下。
l pen:Pen對象,肯定線條的顏色、寬度和樣式。
l x1:第一個點的x座標。
l y1:第一個點的y座標。
l x2:第二個點的x座標。
l y2:第二個點的y座標。
實現過程
(1)建立一個項目,將其命名爲Ex01_34,默認窗體爲Form1。
(2)向Form1窗口中添加GroupBox控件,用做RadioButton控件的容器;添加Button控件用來推出程序。
(3)主要程序代碼。
在窗體單元的private中添加變量以下:
int startX,startY;
Graphics g;
單擊鼠標事件。具體代碼以下:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
startX=e.X;
startY = e.Y;
}
鼠標在窗體中的移動事件。具體代碼以下:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
g = this.CreateGraphics( );
Pen p = new Pen(Color.Black, 1);
if(radioButton2.Checked==true)
{
g.DrawRectangle(p, e.X, e.Y, 1, 1);
}
}
鼠標擡起事件。具體代碼以下:
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
g = this.CreateGraphics( );
Pen p = new Pen(Color.Black, 2);
if (radioButton1.Checked == true )
{
g.DrawLine(p, startX, startY, e.X, e.Y);
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
改變畫筆的顏色。
在窗體上繪製矩型。
實例035 動畫形式的程序界面
實例說明
在不少的程序界面中,都是以菜單或工具欄的形式顯示窗體界面,這種顯示方式是以靜止狀態顯示的,界面不夠生動。下面介紹一個以動畫顯示窗體界面的設計方法。運行本例,效果如圖1.35所示。
技術要點
在該實例中用到了Microsoft Animation Control 6.0(SP4)COM組件,因此要從工具箱「選擇項」中將該組件添加到工具箱,而後繼續將該組件從工具箱添加到窗體便可。下面介紹本例中用到的相關方法。
AxAnimation.open方法用來播放動畫文件。其結構以下:
Public void AxAnimation.open(string bstrFilename)
參數說明以下。
l bstrFilename:將要播放的文件名。
注意:由於使用了AxAnimation類,因此要添加對WMPLib命名空間的引用。
實現過程
(1)建立一個項目,將其命名爲Ex01_35,默認窗體爲Form1。
(2)在Form1窗體添加PictureBox控件用來顯示圖片,添加Microsoft Animation Control 6.0 (SP4)COM組件用來播放動畫。
(3)主要程序代碼。
private void Form1_Load(object sender, EventArgs e)
{
axAnimation1.Open("Electron.avi");
axAnimation2.Open("zybiao.avi");
axAnimation3.Open("gd.avi");
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
製做摸擬網頁。
製做動畫播放器。
1.10 標題欄窗體
本節主要是對窗體的標題欄進行設置,標題欄是一個顯著的位置,在這個位置添加按鈕或進行一些個性化的設置,都會給人一種新奇的感受。經過如下實例的學習,讀者將掌握此技術。
實例036 使窗體標題欄文字右對齊
實例說明
窗口標題欄中的文字是窗口的重要說明,該文字能夠標示窗口的功能、狀態或名稱等信息,通常該文字是居左顯示的,在本例中設計一個標題欄文字右對齊的窗口。本實例運行結果如圖1.36所示。
技術要點
在C# 2.0中實現這一功能很是容易,只需將窗體的RightToLeft屬性設置爲Yes便可。
Form. RightToLeft屬性用來獲取或設置一個值,該值指示是否將控件的元素對齊以支持使用從右向左的字體的區域設置,其語法結構以下:
public virtual RightToLeft RightToLeft { get; set; }
l 屬性值:RightToLeft值之一。默認爲Inherit。
實現過程
(1)建立一個項目,將其命名爲Ex01_36,默認窗體爲Form1。
(2)爲Form1窗體添加背景圖片。
(3)設置RightToLeft屬性爲Yes。
觸類旁通
根據本實例,讀者能夠開發如下程序。
利用Timer控件使窗體標題欄的文字進行左右閃動。
製做窗體標題欄滾動播放圖片的窗體。
實例037 沒有標題欄可義改變大小的窗口
實例說明
隱藏Windows窗口的標題欄以後,窗口只剩下一個客戶區域,有點像Panel控件在窗口中的樣子,而這樣的窗口一般是不可以改變大小的。由於屏蔽其標題欄以後,窗口默認將邊框也去除了,本例將用特殊的方法創建一個沒有標題欄可是能夠改變其大小的窗體。實例運行效果如圖1.37所示。
技術要點
窗口的樣式是在窗口創建時肯定的,在C#中實現窗體沒有標題欄可是能夠改變大小的窗口,有一個巧妙的方法就是將窗體的Text屬性設爲空,同時將ControlBox屬性設爲False。下面介紹一下相關的屬性。
ControlBox屬性用來獲取或設置一個值,該值指示在該窗體的標題欄中是否顯示控件框,其語法結構以下:
public bool ControlBox { get; set; }
l 屬性值:若是該窗體在窗體的左上角顯示控件框,則爲True;不然爲False。默認爲True。
實現過程
(1)建立一個項目,將其命名爲Ex01_37,默認窗體爲Form1。
(2)在Form1窗口中添加Label、Button控件,用來設計界面。
(3)主要程序代碼。
private void Form1_Load(object sender, EventArgs e)
{
ControlBox = false;
}
注意:必須將窗體的Text屬性設爲空。
觸類旁通
根據本實例,讀者能夠開發如下程序。
在窗體顯示時最小化。
在許多的軟件中,都會對窗體的大小、位置和移動進行限定。在不一樣分辨率的顯示器中如何正確顯示窗體、如何設置窗體始終在最上面,這些都須要本節的技術。
實例038 設置窗體在屏幕中的位置
實例說明
在窗體中能夠設置窗體居中顯示,本例經過設置窗體的Left屬性和Top屬性能夠準確設置窗體的位置。運行本例,效果如圖1.38所示。
技術要點
設置窗體在屏幕中的位置,能夠經過設置窗體的屬性來實現。窗體的Left屬性表示窗體距屏幕左側的距離,Top屬性表示窗體距屏幕上方的距離。
實現過程
(1)建立一個項目,將其命名爲Ex01_38,默認窗體爲Form1。
(2)在窗體上添加Label控件;添加TextBox控件用來輸入距屏幕的距離;添加Button控件用來設置窗體在屏幕上的位置。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
this.Left = Convert.ToInt32(textBox1.Text);
this.Top = Convert.ToInt32(textBox2.Text);
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
根據分辨率的變化動態設置窗體位置。
用Timer控件實時顯示窗體位置。
實例039 始終在最上面的窗體
實例說明
Windows桌面上容許多個窗體同時顯示,可是隻有一個窗體可以獲得焦點,當一個窗體獲得焦點後在其上面的窗體會被獲得焦點的窗體遮擋,獲得焦點的窗體會顯示在最上層,這樣被覆蓋的窗體就不能徹底的顯示給用戶,若是該窗體中具備實時性和比較重要的信息時,須要該窗口始終在最上層。本例就實現了此功能,運行本例後,主窗體會始終在桌面的最上面。實例效果如圖1.39所示。
技術要點
在其餘開發環境中實現窗體始終在最上面比較複雜,但在C# 2.0中實現很是簡單,只要將TopMost屬性設爲True便可。下面介紹一下TopMost屬性。
Form.TopMost屬性用來獲取或設置一個值,指示該窗體是否應顯示爲最頂層窗體。其結構以下:
public bool TopMost { get; set; }
l 屬性值:若是將窗體顯示爲最頂層窗體,則爲True;不然爲False。默認爲False。
實現過程
(1)建立一個項目,將其命名爲Ex01_39,默認窗體爲Form1。
(2)爲Form1窗體添加背景圖片,並設置窗體TopMost屬性爲True。
觸類旁通
根據本實例,讀者能夠開發如下程序。
能夠將設爲最上層的窗體設置成爲一個電子錶,以便觀看時間。
能夠將設爲最上層的窗體設置成爲一個工做計劃表,以便隨時提醒本身。
1.12 設置窗體大小
用戶打開軟件後首先看到的就是窗體和窗體上的控件,如何設置窗體的大小及合理的設置窗體和控件的關係就變得十分重要,下面的實例將介紹這方面的知識。
實例040 限制窗體大小
實例說明
Windows窗體是能夠隨意改變大小的,然而對於一些要求嚴格的窗體,開發人員不但願用戶隨意的改變其大小,例如,定位準確的地圖和遊戲軟件等。遇到這種狀況必須對窗口的大小進行一些限制。本例設計一個限制了大小的窗體,用戶雖然能夠改變其大小,可是,大小的範圍是受到限制的。實例效果如圖1.40所示。
技術要點
在此C#中實現限制大小很是方便,只要設置窗體的最大和最小範圍便可。下面介紹一下相關屬性。
Form.MinimumSize屬性用來獲取或設置窗體可調整到的最小大小,其語法格式以下:
public override Size MinimumSize { get; set; }
l 屬性值:Size,表示該窗體的最小大小。
Form.MaximumSize屬性用來獲取或設置窗體可調整到的最大大小,其語法格式以下:
public override Size MaximumSize{ get; set; }
l 屬性值:Size,表示該窗體的最大大小。
實現過程
(1)建立一個項目,將其命名爲Ex01_27,默認窗體爲Form1。
(2)主要程序代碼。
private void Form1_Load(object sender, EventArgs e)
{
MinimumSize = new Size(200, 200);
MaximumSize = new Size(400, 400);
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
在窗體顯示時規定其大小。
在窗體運行時規定其大小。
實例041 獲取桌面大小
實例說明
獲取桌面分辨率可使用API函數GetDeviceCaps,但API函數參數較多,使用不方便,如何更方便的獲取桌面分辨率呢?在本例中,經過讀取Screen對象的屬性,來獲取桌面分辨率信息,以像素爲單位。運行本例,效果如圖1.41所示。
技術要點
C#中提供了Screen對象,在該對象中封裝了屏幕相關信息。能夠經過讀取Screen對象的相關屬性,來獲取屏幕的信息,Screen.PrimaryScreen.WorkingArea.Width用於讀取桌面寬度;Screen.PrimaryScreen.WorkingArea.Height能夠讀取桌面的高度。下面介紹一下相關屬性。
Screen.PrimaryScreen.WorkingArea屬性用於獲取顯示器的工做區。工做區是顯示器的桌面區域,不包括任務欄、停靠窗口和停靠工具欄。其結構以下:
public Rectangle WorkingArea { get; }
l 屬性值:一個Rectangle,表示顯示器的工做區。
實現過程
(1)建立一個項目,將其命名爲Ex01_41,默認窗體爲Form1。
(2)在Form1窗體上添加一個Button控件,用來獲取桌面大小;添加兩個TextBox控件,用來輸出所獲取的桌面大小。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
textBox2.Text = Screen.PrimaryScreen.WorkingArea.Height.ToString( );
textBox1.Text = Screen.PrimaryScreen.WorkingArea.Width.ToString( );
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
根據顯示器的分辨率信息設置窗體大小及位置。
根據顯示器的分辨率信息調整窗體界面。
實例042 在窗口間移動按扭
實例說明
窗體中每一個可視控件都有全部者和父對象兩個重要屬性,全部者是控件創建時指定的所屬對象,該對象能夠是不可視控件,而父對象必須是可視控件。所以能夠經過窗體中可視控件的Parent屬性來判斷控件是否在這個窗體中,還能夠用Form.Controls.Add( )方法爲窗體添加控件。本例以一個能夠在窗口間移動的按鈕來演示父對象改變後的運行效果。運行本例,在窗口中單擊按鈕,按鈕就會移動到另一個窗口中。實例效果如圖1.42和圖1.43所示。
圖1.42 在窗口間移動按鈕 圖1.43 在窗口間移動按鈕
技術要點
可視控件包含一個Parent屬性,該屬性表示控件的父對象。通常將此屬性設置爲一個窗口。經過該屬性能夠控制所屬窗體。
實現過程
(1)建立一個項目,將其命名爲Ex01_42,默認窗體爲Form1。
(2)添加一個窗體,默認窗體的Name屬性爲Form2。
(3)在Form1窗口中添加一個Button控件。併爲Form1和Form2設置背景圖片。
(4)主要程序代碼。
單擊按鈕在兩個窗體之間移動,具體代碼以下:
private void button1_Click(object sender, EventArgs e)
{
if (button1.Parent == this)
{
f.Controls.Add(this.button1);
this.button1.Text = "返回原地";
}
else
{
this.Controls.Add(button1);
this.button1.Text = "開始移動";
}
}
Form1窗體加載時同時顯示Form2窗體,具體代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
f = new Form2( );
f.Show( );
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
試作從一個窗體將控件拖到另外一個窗體。
試作用一個窗體控制另外一個窗體。
實例043 如何實現Office助手
實例說明
用過Office的人都知道,Office助手是一個很是漂亮的小工具,有了它,即便對Office不太熟悉的用戶也能夠操做自如。本實例使用C#製做了一個相似Office助手的程序,實例效果如圖1.44所示。
技術要點
要實現Office助手效果,須要使用Microsoft提供的第3方控件。在工具箱中單擊「選擇項」,從彈出的對話框中選擇COM組件選項卡中的Microsoft Agent Control 2.0組件並加入工具箱中,而後再添加到窗體中。
實現過程
(1)建立一個項目,將其命名爲Ex01_43,默認窗體爲Form1。
(2)在Form1窗體上添加一個ListBox控件用來讓用戶選擇人物的動做。
(3)主要程序代碼。
聲明成員變量及字符串數組,具體代碼以下:
IAgentCtlCharacterEx ICCE;
IAgentCtlRequest ICR;
string[] ws = new string[10] { "Acknowledge", "LookDown", "Sad", "Alert", "LookDownBlink", "Search", "Announce", "LookUp", "Think", "Blink"};
爲ListBox添加選項的實現代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
listBox1.Items.Add(ws[i]);
}
ICR = axAgent1.Characters.Load("merlin", "merlin.acs");
ICCE = axAgent1.Characters.Character("merlin");
ICCE.Show(0);
}
隨着選項改變Office表情的實現代碼以下:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
ICCE.StopAll("");
ICCE.Play(ws[listBox1.SelectedIndex]);
}
觸類旁通
根據本實例,讀者能夠實現如下程序。
瑞星助手。
在本身的程序中加入Office助手。
實例044 在關閉窗口前加入確認對話框
實例說明
用戶對程序進行操做時,不免會有錯誤操做的狀況,例如不當心關閉程序,若是尚有許多資料沒有保存,那麼損失將很是嚴重,因此最好使程序具備靈活的交互性。人機交互過程通常都是經過對話框來實現的,對話框中有提示信息,而且提供按鈕讓用戶選擇,例如【是】或【否】。這樣用戶就可以對所作的動做進行確認。正如前面所說的不當心關閉程序,若是在關閉程序以前提示用戶將要關閉程序,而且提供用戶選擇是否繼續下去,這樣就大大減小了誤操做現象。本例程序中的窗口在關閉時會顯示一個對話框,該對話框中有兩個按鈕【是】與【否】表明是否贊成關閉程序操做。實例運行結果如圖1.45所示。
技術要點
窗口正要關閉可是沒有關閉以前會觸發FormClosing事件,該事件中的參數FormClosingEventArgs e中包含Cancel屬性,若是設置該屬性爲True,窗口將不會被關閉。因此在該事件處理代碼中能夠提示用戶是否關閉程序,若是用戶不想關閉程序,則設置該參數爲True。利用MessageBox參數的返回值能夠知道用戶所選擇的按鈕。下面詳細介紹一下相關屬性。
CancelEventArgs.Cancel屬性用來獲取或設置指示是否應取消事件的值。該屬性結構以下:
public bool Cancel { get; set; }
l 屬性值:若是應取消事件,則爲True;不然爲False。
實現過程
(1)建立一個項目,將其命名爲Ex01_44,默認窗體爲Form1。
(2)主要程序代碼。
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("將要要關閉窗體,是否繼續?", "詢問", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
觸類旁通
根據本實例,讀者能夠實現如下程序。
使窗體的關閉按鈕無效。
使窗體關閉出如今托盤中。
實例045 使用任意組件拖動窗體
實例說明
一般將鼠標按住窗口的標題欄纔可以拖動窗口,可是,在沒有窗口標題欄的狀況下如何拖動窗體呢?本例將會利用窗口中的控件拖動窗口,將鼠標放在按鈕上而後按住鼠標左鍵移動鼠標便可拖動窗體。實例效果如圖1.46所示。
技術要點
經過控件移動窗體時,須要判斷用戶的鼠標動做。用戶準備拖動窗體時必須在控件上按住鼠標左鍵,因此應該在鼠標MouseDown事件處理過程當中來實現窗體的拖動。當用戶在按鈕上將鼠標左鍵按下時,觸發MouseDown事件,在該事件處理代碼中,MouseEventArgs e的Button屬性記錄了當前按下的鼠標按鈕,若是按鍵是鼠標左鍵,則表示能夠移動窗口,鼠標移動時,窗體就能夠跟着移動了。
實現過程
(1)建立一個項目,將其命名爲Ex01_45,默認窗體爲Form1。
(2)在Form1窗體上添加兩個Button控件,分別用來拖動窗體和關閉窗體。而後設置窗體的背景顏色。
(3)主要程序代碼。
聲明記錄鼠標按下時初始位置的變量,具體代碼以下:
private int startX, StartY;
鼠標按下事件處理代碼,具體代碼以下:
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
startX = e.X;
StartY = e.Y;
}
}
鼠標移動事件處理代碼,具體代碼以下:
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Left += e.X - startX;
this.Top += e.Y - StartY;
}
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
能夠用窗體的用戶區拖動窗體。
不能夠拖動的窗體。
實例046 修改提示字體及顏色
實例說明
若是設置了控件的ToolTip屬性,當鼠標移到該控件後,會提示相關的文本,但沒有提供對提示字體及顏色的設置屬性,如何改變提示文本的樣式和字體呢?本例能夠設置提示文本的字體及顏色。運行本例,效果如圖1.47所示。
技術要點
C# 2.0中提供了ToolTip控件,能夠指定關聯控件併爲每一個控件提供提示文本,其中ToolTipTitle屬性指定文本提示盒中的文本。下面介紹相關的屬性和方法。
(1)SetToolTip方法
使工具提示文本與指定的控件相關聯。其語法結構以下:
public void SetToolTip (Control control,string caption)
參數說明以下。
l control:要將工具提示文本與其關聯的Control。
l caption:指針位於控件上方時要顯示的工具提示文本。
(2)ToolTip.ToolTipTitle屬性
獲取或設置工具提示窗口的標題。其語法結構以下:
public string ToolTipTitle { get; set; }
l 屬性值:包含窗口標題的String。該標題在窗口中做爲一行粗體文本顯示在標準的工具提示控件說明文本的上方。一般,標題只用於區分窗體上不一樣類別的控件,或做爲較長控件說明的簡介。
實現過程
(1)建立一個項目,將其命名爲Ex01_46,默認窗體爲Form1。
(2)在Form1窗體上添加Button控件用來在其上方顯示提示文本;添加ToolTip控件用來設計提示文本。
(3)主要程序代碼。
設置提示文本,及提示文本的關聯控件,具體代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
this.toolTip1.OwnerDraw = true;
this.toolTip1.SetToolTip(this.button1,"設置提示的字體及顏色");
this.toolTip1.Draw += new DrawToolTipEventHandler(toolTip1_Draw);
}
設置文本的提示樣式,具體代碼以下:
void toolTip1_Draw(object sender, DrawToolTipEventArgs e)
{
// throw new Exception("The method or operation is not implemented.");
e.DrawBackground( );
e.DrawBorder( );
using (StringFormat sf = new StringFormat( ))
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
sf.FormatFlags = StringFormatFlags.NoWrap;
using (Font f = new Font("宋體", 12))
{
e.Graphics.DrawString(e.ToolTipText, f,
SystemBrushes.ActiveCaptionText, e.Bounds, sf);
}
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
修改任意控件提示文本的樣式。
提示時加提示的聲音。
1.14 其他技術
本節主要介紹瞭如何建立和關閉MDI窗體。在大型項目和產品的開發中經常將系統設計爲MDI界面。
實例047 如何爲MDI類型窗體設置背景圖片
實例說明
MDI窗體是一種應用很是普遍的窗體類型,在一個主窗體內包含多個子窗體,子窗體永遠不會顯示在主窗體的外面。當子窗體不能徹底的顯示在主窗體中時,主窗體會顯示滾動條來調整可視範圍,在其餘開發環境中爲MDI窗體添加背景圖片十分困難。但在C# 2.0中實現很是容易。在本例中實現了一個具備背景的MDI窗體。實例效果如圖1.48所示。
技術要點
在C# 2.0中直接提供了BackgroundImage 屬性,該屬性能夠直接設置窗體的背景圖片。設置IsMdiContainer屬性爲True可使窗體成爲MDI主窗體。下面詳細介紹一下相關屬性。
(1)BackgroundImage屬性
獲取或設置在控件中顯示的背景圖像。其語法結構以下:
public virtual Image BackgroundImage { get; set; }
l 屬性值:一個Image,表示在控件的背景中顯示的圖像。
(2)Form.IsMdiContainer屬性
獲取或設置一個值,該值指示窗體是否爲多文檔界面(MDI)子窗體的容器。其語法結構以下:
public bool IsMdiContainer { get; set; }
l 屬性值:若是該窗體是MDI子窗體的容器,則爲True;不然爲False。默認爲False。
此屬性將窗體的顯示和行爲更改成MDI父窗體。當此屬性設置爲True時,該窗體顯示具備凸起邊框的凹陷工做區。全部分配給該父窗體的MDI子窗體都在該父窗體的工做區內顯示。
實現過程
(1)建立一個項目,將其命名爲Ex01_47,默認窗體爲Form1。
(2)添加一個窗體,默認窗體的Name屬性爲Form2。
(3)爲Form1窗體中添加背景圖片。
(4)設置Form1窗體的IsMdiContainer屬性爲True,該窗口做爲MDI主窗體。
(5)主要程序代碼。
private void Form1_Load(object sender, EventArgs e)
{
Form2 f = new Form2( );
f.MdiParent = this;
f.Show( );
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
爲非MDI窗體制做背景。
爲MDI子窗體設定顯示區域。
實例048 向提示框中添加圖標
實例說明
在開發程序時,爲了讓用戶熟悉操做,常用一些提示框,顯示提示信息。默認狀況下,提示信息框只包含提示信息,未免有些單調,若是在提示信息框中顯示一個圖標,程序或許就別具風格了。本實例實現了在提示框中添加圖標的功能,實例運行結果如圖1.49所示。
技術要點
要修改提示信息框的風格,首先須要瞭解C#中提示信息框的設計原理。在C#中,提示信息框是用ToolTip控件來實現的。ToolTip控件的ToolTipIcon屬性能夠設置提示時顯示的圖片,下面詳細介紹一下該屬性。
ToolTip.ToolTipIcon屬性用來獲取或設置一個值,該值定義要在工具提示文本旁顯示的圖標的類型。其語法結構以下:
public ToolTipIcon ToolTipIcon { get; set; }
l 屬性值:System.Windows.Forms.ToolTipIcon枚舉值之一。
實現過程
(1)建立一個項目,將其命名爲Ex01_48,默認窗體爲Form1。
(2)在Form1窗口中添加3個Label控件,用來顯示文字。
(3)在窗體上添加ToolTip控件用來顯示提示內容和提示樣式。
(4)主要程序代碼。
. private void Form1_Load(object sender, EventArgs e)
{
toolTip1.SetToolTip(label1,"人生格言");
toolTip1.SetToolTip(label2, "人生格言");
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
自定義提示信息框。
製做氣泡樣式提示信息框。
|
|
|
|
|
第13章 硬件相關開發技術
13.1 串口控制
串行口是計算機的標準接口,如今的PC機(我的電腦)通常至少有兩個串行口COM1和COM2。串行口應用普遍,在數據通訊、計算機網絡以及分佈式工業控制系統中,常常採用串行通訊來交換數據和信息。本節經過幾個實例,介紹串口應用的技術和方法。
實例418 經過串口發送數據
實例說明
如今大多數硬件設備均採用串口技術與計算機相連,所以串口的應用程序開發愈來愈廣泛。例如,在計算機沒有安裝網卡的狀況下,將本機上的一些信息數據傳輸到另外一臺計算機上,那麼利用串口通訊就能夠實現。運行本程序,在「發送數據」文本框中輸入要傳送的數據,單擊【發送】按鈕,將傳送的數據發送到所選擇的端口號中;單擊【接收】按鈕,傳遞的數據被接收到「接收數據」文本框中。如圖13.1所示。
技術要點
在.NET Framework 2.0中提供了SerialPort類,該類主要實現串口數據通訊等。下面主要介紹該類的主要屬性(表13.1)和方法(表13.2)。
表13.1 SerialPort類的經常使用屬性
名 稱 |
說 明 |
BaseStream |
獲取 SerialPort 對象的基礎 Stream 對象 |
BaudRate |
獲取或設置串行波特率 |
BreakState |
獲取或設置中斷信號狀態 |
BytesToRead |
獲取接收緩衝區中數據的字節數 |
BytesToWrite |
獲取發送緩衝區中數據的字節數 |
CDHolding |
獲取端口的載波檢測行的狀態 |
CtsHolding |
獲取「能夠發送」行的狀態 |
DataBits |
獲取或設置每一個字節的標準數據位長度 |
DiscardNull |
獲取或設置一個值,該值指示 Null 字節在端口和接收緩衝區之間傳輸時是否被忽略 |
DsrHolding |
獲取數據設置就緒 (DSR) 信號的狀態 |
DtrEnable |
獲取或設置一個值,該值在串行通訊過程當中啓用數據終端就緒 (DTR) 信號 |
Encoding |
獲取或設置傳輸先後文本轉換的字節編碼 |
Handshake |
獲取或設置串行端口數據傳輸的握手協議 |
IsOpen |
獲取一個值,該值指示 SerialPort 對象的打開或關閉狀態 |
NewLine |
獲取或設置用於解釋 ReadLine( )和WriteLine( )方法調用結束的值 |
Parity |
獲取或設置奇偶校驗檢查協議 |
續表
名 稱 |
說 明 |
ParityReplace |
獲取或設置一個字節,該字節在發生奇偶校驗錯誤時替換數據流中的無效字節 |
PortName |
獲取或設置通訊端口,包括但不限於全部可用的 COM 端口 |
ReadBufferSize |
獲取或設置 SerialPort 輸入緩衝區的大小 |
ReadTimeout |
獲取或設置讀取操做未完成時發生超時以前的毫秒數 |
ReceivedBytesThreshold |
獲取或設置 DataReceived 事件發生前內部輸入緩衝區中的字節數 |
RtsEnable |
獲取或設置一個值,該值指示在串行通訊中是否啓用請求發送 (RTS) 信號 |
StopBits |
獲取或設置每一個字節的標準中止位數 |
WriteBufferSize |
獲取或設置串行端口輸出緩衝區的大小 |
WriteTimeout |
獲取或設置寫入操做未完成時發生超時以前的毫秒數 |
表13.2 SerialPort類的經常使用方法
方 法 名 稱 |
說 明 |
Close |
關閉端口鏈接,將 IsOpen 屬性設置爲False,並釋放內部 Stream 對象 |
Open |
打開一個新的串行端口鏈接 |
Read |
從 SerialPort 輸入緩衝區中讀取 |
ReadByte |
從 SerialPort 輸入緩衝區中同步讀取一個字節 |
ReadChar |
從 SerialPort 輸入緩衝區中同步讀取一個字符 |
ReadLine |
一直讀取到輸入緩衝區中的 NewLine 值 |
ReadTo |
一直讀取到輸入緩衝區中指定 value 的字符串 |
Write |
已重載。將數據寫入串行端口輸出緩衝區 |
WriteLine |
將指定的字符串和 NewLine 值寫入輸出緩衝區 |
注意:用跳線使串口的第二、3針鏈接,能夠在本地計算機上實現串口通訊,因此,經過串口的第二、3針的鏈接能夠對程序進行檢測。串口截面圖如圖13.2所示。
圖13.2 串口截面圖
實現過程
(1)新建一個項目,命名爲Ex13_01,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,分別用於執行發送數據和接受數據,添加兩個TextBox控件,用於輸入發送數據和顯示接收數據。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
serialPort1.PortName = "COM1";
serialPort1.BaudRate = 9600;
serialPort1.Open();
byte[] data = Encoding.Unicode.GetBytes(textBox1.Text);
string str = Convert.ToBase64String(data);
serialPort1.WriteLine(str);
MessageBox.Show("數據發送成功!","系統提示");
}
private void button2_Click(object sender, EventArgs e)
{
byte[] data = Convert.FromBase64String(serialPort1.ReadLine());
textBox2.Text = Encoding.Unicode.GetString(data);
serialPort1.Close();
MessageBox.Show("數據接收成功!","系統提示");
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
遠程監控對方計算機屏幕。
下位機控制程序。
實例419 經過串口關閉對方計算機
實例說明
在網絡應用程序中,主要經過網卡實現數據的傳輸,所以能夠利用套接字技術實現遠程關閉計算機。若是計算機中沒有安裝網卡,該如何實現遠程關閉計算機呢?本例實現了利用串口關閉對方計算機,程序運行結果如圖13.3所示。
技術要點
本實例使用SerialPort類的屬性和方法,請參見實例「經過串口發送數據」。下面主要介紹SerialPort類的DataReceived 事件,DataReceived 事件爲本實例的主要使用技術。DataReceived事件表示將處理 SerialPort 對象的數據接收事件的方法。串行接收事件能夠由 SerialData 枚舉中的任何項引發,是否引起此事件由操做系統決定,因此不必定會報告全部奇偶校驗錯誤。
注意:本實例從開發到測試,都是由本地計算機完成的,用戶只須要使用跳線將串口的第二、3針鏈接,能夠在本地計算機上實現串口通訊。跳線鏈接請參見圖13.2。
實現過程
(1)新建一個項目,命名爲Ex13_02,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,分別用於打開通訊串口和關閉對方計算機。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
//打開串口
serialPort1.PortName = "COM1";
serialPort1.Open();
button1.Enabled = false;
button2.Enabled = true;
} //數據接收事件,等待接收關機命令
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] data = Convert.FromBase64String(serialPort1.ReadLine());
string str = Encoding.Unicode.GetString(data);
serialPort1.Close();
if (str == "關機")
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.WriteLine("shutdown /s");
p.StandardInput.WriteLine("exit");
}
} //發送關機命令
private void button2_Click(object sender, EventArgs e)
{
if (button2.Text == "關閉計算機")
{
//發送關機命令數據
byte[] data = Encoding.Unicode.GetBytes("關機");
string str = Convert.ToBase64String(data);
serialPort1.WriteLine(str);
button2.Text = "取消關機";
}
else
{
button2.Text = "關閉計算機";
button1.Enabled = true;
button2.Enabled = false;
//取消關機
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.WriteLine("shutdown /a");
p.StandardInput.WriteLine("exit");
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
遠程控制對方計算機操做。
定時控制對方計算機關閉。
13.2 加 密 狗
一些商務管理軟件,爲了防止盜版,常用加密狗將軟件加密。下面的兩個實例將介紹如何將密碼寫入加密狗及利用加密狗來設計加密程序。
實例420 密碼寫入與讀出加密狗
實例說明
在使用加密狗時,須要向加密狗中寫入或讀取數據。例如,將密碼寫入或讀取加密狗,如何實現呢?運行本例,在文本框中設置密碼後,單擊【寫入】按鈕,便可將設置的密碼寫入加密狗,成功寫入後,單擊【讀出】按鈕,便可將寫入的密碼讀出並顯示在文本框中。如圖13.4所示。
技術要點
在購買加密狗時,廠家一般會附帶有開發手冊和一張光盤。開發手冊中介紹了加密狗的使用方法和開發資料。本例使用賽孚耐信息技術有限公司的加密狗產品,該產品提供了.NET中非託管的類庫,來完成加密狗的數據讀寫功能。下面介紹有關加密狗的類庫中的讀寫函數。
● DogWrite 函數
該函數將pdogData指向的數據寫入加密狗中,從DogAddr地址開始寫入,到DogBytes地址中止。
函數聲明以下:
[DllImport("Win32dll.dll", CharSet = CharSet.Ansi)]
public static unsafe extern uint DogWrite(uint idogBytes, uint idogAddr, byte* pdogData);
參數說明以下。
l idogAddr:對軟件狗讀寫操做時用戶區中的首地址。取值範圍爲0~99。
l IdogBytes:對軟件狗讀寫操做時的字節長度。讀寫時取值範圍爲1~100,而且與idogAddr之和不能超過100。
l pdogData:指針型變量。指向讀寫操做或變換的數據緩衝區。
l 返回值:0表示操做成功,其餘值是錯誤碼。
● DogRead函數
該函數從加密狗中的idogAddr開始的存儲區讀出數據,存入pdogData指定的緩衝區,讀出字節數爲idogBytes。切記,緩衝區大小要足夠長。
函數聲明以下:
[DllImport("Win32dll.dll", CharSet = CharSet.Ansi)]
public static unsafe extern uint DogRead(uint idogBytes, uint idogAddr, byte* pdogData);
參數說明以下。
l idogAddr:對軟件狗讀寫操做時用戶區中的首地址。取值範圍爲0~99。
l idogBytes:對軟件狗讀寫操做時的字節長度。讀寫時取值範圍爲1~100,而且與idogAddr之和不能超過100。
l pdogData:指針型變量。指向讀寫操做或變換的數據緩衝區。
l 返回值:0表示操做成功,其餘值是錯誤碼。
注意如下幾點。
在使用這個函數以前,必須將隨加密狗附帶的安裝程序安裝完整,並將安裝目錄下的Win32dll.dll文件複製到系統目錄下。例如:
在Windows 2003下將安裝目錄下的「\SafeNet China\SoftDog SDK V3.1\Win32\Win32dll\HighDll\ Win32dll.dll」文件複製到「C:\WINDOWS\system32\」文件夾中。
實現過程
(1)新建一個項目,命名爲Ex13_03,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,用於執行向加密狗數據的寫入與讀出數據,添加兩個TextBox控件,分別用於填寫向加密狗中寫入的數據和顯示讀取加密狗中的數據。
(3)主要程序代碼。
設置加密狗類,而且完善加密狗的讀寫功能,代碼以下:
[StructLayout(LayoutKind.Sequential)]
//這個類用於讀寫加密狗
public unsafe class Dog
{
public uint DogBytes, DogAddr; //設置加密狗字節長度和起始地址
public byte[] DogData; //設置數據的長度
public uint Retcode;
[DllImport("Win32dll.dll", CharSet = CharSet.Ansi)]
public static unsafe extern uint DogRead(uint idogBytes, uint idogAddr, byte* pdogData);
[DllImport("Win32dll.dll", CharSet = CharSet.Ansi)]
public static unsafe extern uint DogWrite(uint idogBytes, uint idogAddr, byte* pdogData);
public unsafe Dog(ushort num)
{
DogBytes = num;
DogData = new byte[DogBytes]; //設置數據的長度
}
public unsafe void ReadDog()
{
fixed (byte* pDogData = &DogData[0])
{
Retcode = DogRead(DogBytes, DogAddr, pDogData); //將數據讀出加密狗
}
}
public unsafe void WriteDog()
{
fixed (byte* pDogData = &DogData[0])
{
Retcode = DogWrite(DogBytes, DogAddr, pDogData); //將數據寫入加密狗
}
}
}
調用加密狗類,進行加密狗的讀寫功能,代碼以下:
private void button1_Click_1(object sender, EventArgs e)
{
Dog dog = new Dog(100);
dog.DogAddr = 0;
dog.DogBytes = 10;
string str = textBox1.Text;
for (int i = 0; i < str.Length; i++)
{
dog.DogData[i] = (byte)str[i];
}
dog.WriteDog();
MessageBox.Show("密碼已成功寫入加密狗!", "成功提示!", MessageBoxButtons.OK, MessageBoxIcon.Information);
textBox1.ReadOnly = true;
button1.Enabled = false;
button2.Enabled = true;
}
private void button2_Click_1(object sender, EventArgs e)
{
Dog dog = new Dog(100);
dog.DogAddr = 0;
dog.DogBytes = 10;
dog.ReadDog();
if (dog.Retcode == 0) //開始讀加密狗數據
{
char[] chTemp = new char[textBox1.Text.Length];
for (int i = 0; i < textBox1.Text.Length; i++)
{
chTemp[i] = (char)dog.DogData[i];
}
String str = new String(chTemp);
textBox2.Text = str;
}
else
{
textBox2.Text = "2:" + dog.Retcode;
}
textBox1.ReadOnly = false;
button2.Enabled = false;
button1.Enabled = true;
}
注意:本程序所使用的代碼爲不安全代碼,正常編譯是沒法經過的,那麼須要設置開發環境容許運行不安全代碼,設置步驟爲:在菜單欄中選擇「項目」/「屬性」/「生成」子菜單,在「生成」選項卡中選中「容許不安全代碼」選項便可。
觸類旁通
根據本實例,讀者能夠開發如下程序。
利用加密狗加密本身的軟件。
利用加密狗設計控制計算機使用的程序。
實例421 使用加密狗進行身份驗證
實例說明
在程序開發過程當中,對於一些機密的數據,開發人員須要將其有效的保護起來。例如,對於用戶的密碼,若是從數據庫中驗證用戶密碼,很容易被非法人員發現甚至破解。本例實現了利用加密狗進行身份驗證。實例運行結果如圖13.5所示。
技術要點
本例的關鍵是從加密狗中讀取數據,可使用ReadDog函數實現。有關該函數的介紹請參考實例「密碼寫入與讀出加密狗」中的「技術要點」部分。
實現過程
(1)新建一個項目,命名爲Ex13_03,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,用於數據驗證和退出程序,添加兩個TextBox控件,分別用於輸入用戶名稱和密碼。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
Dog dog = new Dog(100);
dog.DogAddr = 0;
dog.DogBytes = 6;
dog.ReadDog();
if (dog.Retcode == 0)
{
char[] chTemp = new char[6];
for (int i = 0; i < 6; i++)
{
chTemp[i] = (char)dog.DogData[i];
}
String str = new String(chTemp);
if (textBox2.Text==str)
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show("error");
}
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
利用加密狗設計加密軟件。
使用加密狗控制用戶使用權限。
13.3 IC卡應用
IC(Integrated Circuit)卡,也被稱做智能卡(Smart Card),具備寫入數據和存儲數據的功能,IC卡內存儲器的內容能夠根據須要有條件地供外部讀取,完成信息處理和斷定。因爲其內部具備集成電路,不但能夠存儲大量信息,具備極強的保密性能,而且還具備抗干擾、無磨損、壽命長等特性。所以在各個領域中獲得普遍應用。下面經過兩個實例介紹IC卡的簡單應用。
實例422 向IC卡中寫入數據
實例說明
IC卡是攜帶應用信息和數據的媒體,空白IC卡是不能當即使用的,必須對IC卡應用系統進行初始化,寫入系統IC卡和我的密碼,我的專用信息和應用數據。下面介紹如何向IC卡中寫入數據。運行本例,在「數據」文本框中輸入要存入IC卡中的數據,單擊「寫數據」按鈕,便可將輸入的數據寫入IC卡中。如圖13.6所示。
技術要點
本例使用的是深圳明華生產的明華IC卡讀寫器,用戶在使用時將驅動程序安裝完畢後,便可正常使用本系統。
本例經過調用Mwic_32.dll連接庫,進行IC卡的讀寫工做。下面介紹與IC卡寫操做相關的幾個函數。
(1)auto_init函數
該函數用於初始化IC卡讀卡器。語法以下:
public static extern int auto_init(int port, int baud);
參數說明以下。
l port:標識端口號,Com1對應的端口號爲0;Com2對應的端口號爲1,依此類推。
l baud:標識波特率。
l 返回值:若是初始化成功,返回值是IC卡設備句柄;若是初始化失敗,返回值小於零。
(2)setsc_md函數
該函數用於設置設備密碼模式。語法以下:
public static extern int setsc_md(int icdev, int mode);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l mode:標識設備密碼模式,若是爲0,設備密碼有效,設備在加電時必須驗證設備密碼才能對設備進行操做。若是爲1,設備密碼無效。
l 返回值:若是函數執行成功返回值爲零,不然小於零。
(3)get_status函數
該函數用於獲取設備的當前狀態。語法以下:
public static extern Int16 get_status(int icdev, Int16* state);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l state:用於接收函數返回的結果。若是爲0表示讀卡器中無卡,爲1表示讀卡器中有卡。
l 返回值:若是函數執行成功返回值爲零,不然小於零。
(4)csc_4442函數
該函數用於覈對IC卡密碼。語法以下:
public static extern Int16 Csc_4442(int icdev, int len, [MarshalAs(UnmanagedType.LPArray)] byte[] p_string);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l len:標識密碼長度,其值爲3。
l p_string:標識設置的密碼。
l 返回值:若是函數執行成功返回值爲零,不然小於零。
(5)swr_4442函數
該函數用於向IC卡中寫入數據。語法以下:
public static extern int swr_4442(int icdev, int offset, int len, char* w_string);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l offset:標識地址的偏移量,範圍是0~255。
l len:標識字符串長度。
l w_string:標識寫入的數據。
(6)ic_exit函數
該函數用於關閉設備端口。語法以下:
public static extern int ic_exit(int icdev);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
(7)dv_beep函數
該函數使讀卡器嗡鳴。語法以下:
public static extern int dv_beep(int icdev, int time);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l time:標識嗡鳴持續的時間,單位是10毫秒。
實現過程
(1)新建一個項目,命名爲Ex13_05,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,用於執行向卡中寫入數據和退出程序的操做,添加一個TextBox控件,將TextBox中數據寫入IC卡中。
(3)主要程序代碼。
將程序所使用的操做IC卡的函數,封裝在類IC中。代碼以下:
[StructLayout(LayoutKind.Sequential)]
public unsafe class IC
{
//對設備進行初始化
[DllImport("Mwic_32.dll", EntryPoint = "auto_init", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int auto_init(int port, int baud);
//設備密碼格式
[DllImport("Mwic_32.dll", EntryPoint = "setsc_md", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int setsc_md(int icdev, int mode);
//獲取設備當前狀態
[DllImport("Mwic_32.dll", EntryPoint = "get_status", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern Int16 get_status(int icdev, Int16* state);
//關閉設備通信接口
[DllImport("Mwic_32.dll", EntryPoint = "ic_exit", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int ic_exit(int icdev);
//使設備發出蜂鳴聲
[DllImport("Mwic_32.dll", EntryPoint = "dv_beep", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int dv_beep(int icdev, int time);
//向IC卡中寫數據
[DllImport("Mwic_32.dll", EntryPoint = "swr_4442", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int swr_4442(int icdev, int offset, int len, char* w_string);
//覈對卡密碼
[DllImport("Mwic_32.dll", EntryPoint = "csc_4442", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern Int16 Csc_4442(int icdev, int len, [MarshalAs(UnmanagedType.LPArray)] byte[] p_string);
}
下面代碼主要用於將TextBox中數據寫入到IC卡中。代碼以下:
private void button1_Click(object sender, EventArgs e)
{
//初始化
int icdev = IC.auto_init(0, 9600);
if (icdev < 0)
MessageBox.Show("端口初始化失敗,請檢查接口線是否鏈接正確。","錯誤提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
int md = IC.setsc_md(icdev, 1); //設備密碼格式
unsafe
{
Int16 status = 0;
Int16 result = 0;
result = IC.get_status(icdev, &status);
if (result != 0)
{
MessageBox.Show("設備當前狀態錯誤!");
int d1 = IC.ic_exit(icdev); //關閉設備
return;
}
if (status != 1)
{
MessageBox.Show("請插入IC卡");
int d2 = IC.ic_exit(icdev); //關閉設備
return;
}
}
unsafe
{
//卡的密碼默認爲6個f(密碼爲:ffffff),1個f的16進制是15,兩個f的16進制是255
byte[] pwd = new byte[3] { 255, 255, 255 };
//byte[] pwd = new byte[3] { 0xff, 0xff, 0xff };
//char[] pass=new ch{0xff,0xff,0xff};
Int16 checkIC_pwd = IC.Csc_4442(icdev, 3, pwd);
if (checkIC_pwd < 0)
{
MessageBox.Show("IC卡密碼錯誤!");
return;
}
char str = 'a';
int write=-1;
for (int j = 0; j < textBox1.Text.Length; j++)
{
str = Convert.ToChar(textBox1.Text.Substring(j, 1));
write = IC.swr_4442(icdev, 33 + j, textBox1.Text.Length, &str);
}
if (write == 0)
{
int beep = IC.dv_beep(icdev, 20); //發出蜂鳴聲
MessageBox.Show("數據已成功寫入IC卡中!");
}
else
MessageBox.Show("數據寫入IC卡失敗!");
}
int d = IC.ic_exit(icdev); //關閉設備
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
在圖書借閱中使用IC卡。
利用IC卡控制上網。
實例423 讀取IC卡中的數據
實例說明
向IC卡寫入數據後,就能夠進行讀卡操做了。運行本例,將寫入數據的IC卡插入讀卡器,單擊【讀卡】按鈕,IC卡中的數據將顯示在文本框中。如圖13.7所示。
技術要點
本例中主要調用srd_4442函數讀取IC卡中的數據,相關函數介紹請參考實例「向IC卡中寫入數據」中的「技術要點」部分。這裏只介紹讀卡函數。
q srd_4442函數
該函數用於讀取IC卡中的數據。語法以下:
public static extern int srd_4442(int icdev, int offset, int len, char* r_string);
參數說明以下。
l icdev:標識設備句柄,一般是auto_init函數的返回值。
l offset:標識地址的偏移量,範圍是0~255。
l len:標識字符串長度。
l r_string:用於存儲返回的數據。
實現過程
(1)新建一個項目,命名爲Ex13_06,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,用於讀取卡中的數據和退出程序,添加一個TextBox控件,顯示卡中的數據。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
//初始化
int icdev = IC.auto_init(0, 9600);
if (icdev < 0)
MessageBox.Show("端口初始化失敗,請檢查接口線是否鏈接正確。", "錯誤提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
int md = IC.setsc_md(icdev, 1); //設備密碼格式
int i = IC.dv_beep(icdev, 10); //發出蜂鳴聲
unsafe
{
Int16 status = 0;
Int16 result = 0;
result = IC.get_status(icdev, &status);
if (result != 0)
{
MessageBox.Show("設備當前狀態錯誤!");
int d1 = IC.ic_exit(icdev); //關閉設備
return;
}
if (status != 1)
{
MessageBox.Show("請插入IC卡");
int d2 = IC.ic_exit(icdev); //關閉設備
return;
}
}
unsafe
{
char str = 'a';
int read = -1;
for (int j = 0; j < 6; j++)
{
read = IC.srd_4442(icdev, 33 + j, 1, &str);
textBox1.Text = textBox1.Text + Convert.ToString(str);
}
if (read == 0)
MessageBox.Show("IC卡中數據讀取成功!");
}
int d = IC.ic_exit(icdev); //關閉設備
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
讀取IC卡電話系統。
公交車刷卡系統。
實例424 利用IC卡製做考勤程序
實例說明
IC卡普遍應用於各行業,包括銀行卡、公交車刷卡系統、讀書卡等。下面介紹使用IC卡製做簡單的公司考勤系統。運行本例,單擊【刷卡】按鈕,便可對員工進行考勤。實現效果如圖13.8所示。
技術要點
有關IC卡的操做函數請參考實例「向IC卡中寫入數據」和「讀取IC卡中的數據」中的「技術要點」部分。
下面主要介紹經過IC卡如何實現員工考勤。主要將寫入IC卡中的卡號讀取出來,而後從數據表中查詢員工信息。具體代碼請參考實現過程。
實現過程
(1)新建一個項目,命名爲Ex13_07,默認窗體爲Form1。
(2)在Form1窗體中,主要添加5個TextBox控件和6個Label控件,用途如圖13.7所示,添加一個Button控件,執行刷IC卡命令。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
//初始化
int icdev = IC.auto_init(0, 9600);
if (icdev < 0)
label6.Text = "端口初始化失敗,請檢查接口線是否鏈接正確。";
unsafe
{
Int16 status = -1;
Int16 result = IC.get_status(icdev, &status);
int md = IC.setsc_md(icdev, 1); //設備密碼格式
if (result < 0)
{
int d1 = IC.ic_exit(icdev); //關閉設備
return;
}
else if ((result == 0) && (status == 0))
{
int d2 = IC.ic_exit(icdev); //關閉設備
label6.Text = "請插入IC卡";
return;
}
}
unsafe
{
char str = 'a';
int read = -1;
string ic = "";
for (int j = 0; j < 6; j++)
{
read = IC.srd_4442(icdev, 33 + j, 1, &str);
ic = ic + Convert.ToString(str);
}
textBox1.Text = ic;
if (read == 0)
label6.Text = "刷卡成功!";
int beep = IC.dv_beep(icdev, 20); //發出蜂鳴聲
int d3 = IC.ic_exit(icdev); //關閉設備
}
int d = IC.ic_exit(icdev); //關閉設備
//根據卡號,查找相應數據
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "price.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("select * from worker where ICID='"+textBox1.Text+"'", con);
DataSet ds = new DataSet();
dap.Fill(ds, "table");
if (ds.Tables.Count > 0)
{
textBox2.Text = ds.Tables[0].Rows[0][0].ToString();
textBox3.Text = ds.Tables[0].Rows[0][1].ToString();
textBox4.Text = ds.Tables[0].Rows[0][2].ToString();
textBox5.Text = ds.Tables[0].Rows[0][3].ToString();
}
else
{
label6.Text = "不存在該用戶!";
}
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
代金卡系統。
工資發放系統。
13.4 監 控
在一些銀行、大型商場、辦公樓、升降電梯中,爲了保障公有財產、商品、辦公設備、資料、人身等的安全,都設有監控系統。在出現問題時,用戶能夠經過監控系統查找緣由。下面的幾個實例分別實現了攝像頭監控與定時監控的功能。
實例425 簡易視頻程序
實例說明
利用普通的簡易攝像頭,經過C#語言便可開發成簡易視頻程序。本實例利用市場上購買的普通攝像頭,利用VFW技術,實現單路視頻監控系統。運行程序,窗體中將顯示艦體攝像頭採集的視頻信息。如圖13.9所示。
技術要點
本實例主要使用了VFW(Video for Windows)技術。VFW 是Microsoft公司爲開發Windows平臺下的視頻應用程序提供的軟件工具包,提供了一系列應用程序編程接口(API),用戶能夠經過這些接口很方便地實現視頻捕獲、視頻編輯及視頻播放等通用功能,還可利用回調函數開發比較複雜的視頻應用程序。該技術的特色是播放視頻時不須要專用的硬件設備,並且應用靈活,能夠知足視頻應用程序開發的須要。Windows操做系統自身就攜帶了VFW技術,系統安裝時,會自動安裝VFW的相關組件。
VFW技術主要由六個功能模塊組成,下面進行簡單說明。
l AVICAP32.DLL:包含執行視頻捕獲的函數,給AVI文件的I/O處理和視頻,音頻設備驅動程序提供一個高級接口。
l MSVIDEO.DLL:包含一套特殊的DrawDib函數,用來處理程序上的視頻操做。
l MCIAVI.DRV:包括對VFW的MCI命令解釋器的驅動程序。
l AVIFILE.DLL:包含由標準多媒體I/O(mmio)函數提供的更高級的命令,用來訪問.AVI文件。
l ICM:壓縮管理器,用於管理的視頻壓縮/解壓縮的編譯碼器。
l ACM:音頻壓縮管理器,提供與ICM類似的服務,適用於波形音頻。
其中13.4節全部的實例主要使用AVICAP32.DLL中的函數和USER32.DLL中的函數,函數語法及結構以下。
(1)capCreateCaptureWindow函數
該函數用於建立一個視頻捕捉窗口。語法以下:
[DllImport("avicap32.dll")]
public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
參數說明以下。
l lpszWindowName:標識窗口的名稱。
l dwStyle:標識窗口風格。
l x、y:標識窗口的左上角座標。
l nWidth、nHeight:標識窗口的寬度和高度。
l hWnd:標識父窗口句柄。
l nID:標識窗口ID。
l 返回值:視頻捕捉窗口句柄。
(2)SendMessage函數
用於向Windows系統發送消息機制。
[DllImport("User32.dll")]
private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
參數說明以下。
l hWnd:窗口句柄。
l wMsg:將要發送的消息。
l wParam、lParam:消息的參數,每一個消息都有兩個參數,參數設置由發送的消息而定。
實現過程
(1)新建一個項目,命名爲Ex13_08,默認窗體爲Form1,添加1個類文件(.CS),用於編寫視頻類。
(2)在Form1窗體中,主要添加1個PictrueBox控件,用於顯示視頻;添加4個Button控件,用於打開視頻、關閉視頻、拍攝照片和退出程序。
(3)主要程序代碼。
視頻類中主要實現打開視頻、關閉視頻以及經過視頻拍攝照片的功能。代碼以下:
public class VideoAPI //視頻API類
{
// 視頻API調用
[DllImport("avicap32.dll")]
public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
[DllImport("avicap32.dll")]
public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
[DllImport("User32.dll")]
public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
[DllImport("User32.dll")]
public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
// 常量
public const int WM_USER = 0x400;
public const int WS_CHILD = 0x40000000;
public const int WS_VISIBLE = 0x10000000;
public const int SWP_NOMOVE = 0x2;
public const int SWP_NOZORDER = 0x4;
public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
public const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;
public const int WM_CAP_START = WM_USER;
public const int WM_CAP_SAVEDIB = WM_CAP_START + 25;
}
public class cVideo //視頻類
{
private IntPtr lwndC; //保存無符號句柄
private IntPtr mControlPtr; //保存管理指示器
private int mWidth;
private int mHeight;
public cVideo(IntPtr handle, int width, int height)
{
mControlPtr = handle; //顯示視頻控件的句柄
mWidth = width; //視頻寬度
mHeight = height; //視頻高度
}
/// <summary>
/// 打開視頻設備
/// </summary>
public void StartWebCam()
{
byte[] lpszName = new byte[100];
byte[] lpszVer = new byte[100];
VideoAPI.capGetDriverDescriptionA(0, lpszName, 100, lpszVer, 100);
this.lwndC = VideoAPI.capCreateCaptureWindowA(lpszName, VideoAPI.WS_CHILD | VideoAPI.WS_VISIBLE, 0, 0, mWidth, mHeight, mControlPtr, 0);
if (VideoAPI.SendMessage(lwndC, VideoAPI.WM_CAP_DRIVER_CONNECT, 0, 0))
{
VideoAPI.SendMessage(lwndC, VideoAPI.WM_CAP_SET_PREVIEWRATE, 100, 0);
VideoAPI.SendMessage(lwndC, VideoAPI.WM_CAP_SET_PREVIEW, true, 0);
}
}
/// <summary>
/// 關閉視頻設備
/// </summary>
public void CloseWebcam()
{
VideoAPI.SendMessage(lwndC, VideoAPI.WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
/// <summary>
/// 拍照
/// </summary>
/// <param name="path">要保存bmp文件的路徑</param>
public void GrabImage(IntPtr hWndC, string path)
{
IntPtr hBmp = Marshal.StringToHGlobalAnsi(path);
VideoAPI.SendMessage(lwndC, VideoAPI.WM_CAP_SAVEDIB, 0, hBmp.ToInt32());
}
}
Form1窗體中經過調用視頻類中的方法來實現相應的功能。
在【打開視頻】按鈕的Click事件中添加以下代碼:
private void button1_Click(object sender, EventArgs e)
{
btnPlay.Enabled = false;
btnStop.Enabled = true;
btnPz.Enabled = true;
video = new cVideo(pictureBox1.Handle, pictureBox1.Width, pictureBox1.Height);
video.StartWebCam();
}
在【關閉視頻】按鈕的Click事件中添加以下代碼:
private void b_stop_Click(object sender, EventArgs e)
{
btnPlay.Enabled = true;
btnStop.Enabled = false;
btnPz.Enabled = false;
video.CloseWebcam();
}
在【拍攝照片】按鈕的Click事件下添加以下代碼:
private void btnPz_Click(object sender, EventArgs e)
{
video.GrabImage(pictureBox1.Handle, "d:\\a.bmp");
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
無人值班視頻實時監控系統。
車庫安全實時監控系統。
實例426 攝像頭監控錄像
實例說明
本例是爲經過攝像頭來實現監控錄像的程序。運行本例後,單擊【開始監控】按鈕,程序將自動開始錄像,錄像文件(lx.avi)將保存在D盤根目錄下。運行程序,效果如圖13.10所示。
技術要點
在實例「簡易視頻程序」的技術要點中,使用的技術和相關函數已經介紹過。在這裏主要介紹如何將捕獲的視頻製做成 .AVI媒體文件。實現技術爲主要經過SendMessage函數發送Windows消息機制,消息值WM_CAP_FILE_SET_CAPTURE_FILEA和WM_CAP_SEQUENCE,分別用來設置視頻捕捉的文件名稱和初始化視頻流,捕捉視頻信息到文件:
private const int WM_USER = 0x400;
private const int WM_CAP_START = WM_USER;
private const int WM_CAP_SEQUENCE = WM_CAP_START + 62;
private const int WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20;
實現關鍵代碼以下:
IntPtr hBmp = Marshal.StringToHGlobalAnsi(path);
SendMessage(hWndC,WM_CAP_FILE_SET_CAPTURE_FILEA,0, hBmp.ToInt32());
SendMessage(hWndC, WM_CAP_SEQUENCE, 0, 0);
實現過程
(1)新建一個項目,命名爲Ex13_09,默認窗體爲Form1,添加一個類文件(.CS),用於編寫視頻類。
(2)在Form1窗體中,主要添加一個PictrueBox控件,用於顯示視頻;添加4個Button控件,用於開始監控、中止監控和監控程序。
(3)視頻類中主要程序代碼以下:
/// <summary>
/// 開始錄像
/// </summary>
/// <param name="path">要保存錄像的路徑</param>
public void StarKinescope(string path)
{
IntPtr hBmp = Marshal.StringToHGlobalAnsi(path);
SendMessage(hWndC,WM_CAP_FILE_SET_CAPTURE_FILEA,0, hBmp.ToInt32());
SendMessage(hWndC, WM_CAP_SEQUENCE, 0, 0);
}
/// <summary>
/// 中止錄像
/// </summary>
public void StopKinescope()
{
SendMessage(hWndC, WM_CAP_STOP, 0, 0);
}
(4)Form1窗體中主要程序代碼以下:
//開始錄像
private void button1_Click(object sender, EventArgs e)
{
btnStar.Enabled = false;
btnStop.Enabled = true;
video.StarKinescope(@"d:\lx.avi");
}
//中止錄像
private void button2_Click(object sender, EventArgs e)
{
btnStar.Enabled = true;
btnStop.Enabled = false;
video.StopKinescope();
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
小區視頻監控錄像系統。
公司財務室視頻監控系統。
實例427 超市攝像頭定時監控系統
實例說明
本實例實現超市攝像頭定時監控系統。運行本例後,在「定時監控設置」處設置監控的星期及時間,單擊【保存】按鈕,將「定時設置」參數數據保存到數據庫中。系統在運行到定時時間後,程序將自動進行監控。如圖13.11所示。另外,監控的錄像文件和圖片文件保存在D盤根目錄中,命名格式爲系統當前日期。
圖13.11 超市攝像頭定時監控
技術要點
相關技術要點請參見實例「攝像頭監控錄像」。另外,本實例利用Timer控件中的定時執行功能,進行數據的定時錄像工做。
實現過程
(1)新建一個項目,命名爲Ex13_10,默認窗體爲Form1,添加一個類文件(.CS),用於編寫視頻類。
(2)在Form1窗體中,主要添加一個PictrueBox控件,用於顯示視頻;其餘控件的添加如圖13.11所示。
(3)主要程序代碼。
private void timer1_Tick(object sender, EventArgs e)
{
string strTime="";
//星期一
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek)==1)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt1.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期二
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 2)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt2.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期三
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 3)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt3.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期四
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 4)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt4.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期五
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 5)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt5.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期六
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 6)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt6.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
//星期日
if (chk1.Checked && Convert.ToInt32(DateTime.Now.DayOfWeek) == 7)
{
strTime = DateTime.Now.ToString("HH:mm");
DateTime date = Convert.ToDateTime(mtxt7.Text);
if (strTime == date.ToString("HH:mm"))
video.StarKinescope(@"d:\" + DateTime.Today.Month.ToString() + DateTime.Today.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".avi");
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
車站定時監控系統。
公司定時安防系統。
13.5 語音卡控制
隨着語音技術的不斷髮展,語音卡在通訊行業應用很是普遍。本節經過幾個典型實例介紹語音卡程序的開發。
實例428 語音卡電話呼叫系統
實例說明
隨着科學技術的不斷髮展,語音卡被普遍地應用於商業軟件中。本例實現了利用語音卡實現電話呼叫的功能。實例運行結果如圖13.12所示。
技術要點
本例採用東進公司開發的8路模擬語音卡,該卡採用靈活的模式化設計,可按需配置外線、內線兩種模塊。該語音卡可實現坐席、會議、FSK數據收發、語音合成等多種功能,並提供SDK開發工具包。
在安裝完驅動程序後,相應的動態連接庫(NewSig.dll和Tc08a32.dll文件)會複製到Windows的系統目錄下。在語音卡的開發過程當中,主要經過調用NewSig.dll和Tc08a32.dll來實現相應的功能。下面介紹這兩個動態庫中的主要使用函數。
(1)LoadDRV函數
該函數用於加載動態連接庫。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern long LoadDRV();
返回值:返回值爲0表示成功;−1表示打開設備驅動程序錯誤。−2表示在讀取TC08A-V.INI文件時發生錯誤;−3表示INI文件的設置與實際的硬件不一致時發生錯誤。
(2)FreeDRV函數
該函數用於關閉驅動程序。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern long EnableCard(short wusedCh, short wFileBufLen);
(3)EnableCard函數
該函數用於初始化語音卡硬件,併爲每一個通道分配語音緩衝區。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern long EnableCard(short wusedCh, short wFileBufLen);
參數說明以下。
l wUsedCh:標識通道數量。
l WFileBufLen:標識分配的緩衝區大小。
(4)CheckValidCh函數
該函數檢測在當前機器內可用的通道總數。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern short CheckValidCh();
l 返回值:通道總數量。
(5)CheckChType函數
該函數用於測試某個通道的類型。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern short CheckChType(short wChnlNo);
參數說明以下。
l wChnlNo:標識通道號。
l 返回值:爲0表示內線;爲1表示外線;爲2表示懸空。
(6)PUSH_PLAY函數
該函數用於維持文件錄放音的持續進行,需在處理函數的大循環中調用。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern void PUSH_PLAY();
(7)SetBusyPara函數
該函數用於設置要檢測的掛機忙音的參數。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern void SetBusyPara(short BusyLen);
參數說明:
l BusyLen:標識忙音的時間長度,單位爲毫秒。
(8)RingDetect函數
該函數用於測試外線是否振鈴或內線是否提機。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern bool RingDetect(short wChnlNo);
參數說明以下。
l wChnlNo:標識通道號。
返回值:若是爲1,對於外線表示有振鈴信息;對於內線,表示提機。若是爲0,對於外線,表示無振鈴信息;對於內線,表示掛機。
(9)OffHook函數
該函數用於外線提機。對於內線,不起做用。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern void OffHook(short wChnlNo);
參數說明以下。
l wChnlNo:標識外線通道。
(10)HangUp函數
該函數用於外線掛機。對於內線,不起做用。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern void HangUp(short wChnlNo);
參數說明以下。
l wChnlNo:標識外線通道。
(11)Sig_Init函數
該函數用於完成信號音檢測的初始化工做。語法以下:
[DllImport("NewSig.dll", CharSet = CharSet.Auto)]
public static extern void Sig_Init(int Times);
參數說明以下。
l wPara:缺省值爲0,不起做用。
(12)Sig_CheckBusy函數
清空忙音檢測的緩衝區以及內部計數。當檢測對方掛機的忙音後,必須調用本函數。語法以下:
[DllImport("NewSig.DLL", CharSet = CharSet.Auto)]
public static extern void Sig_ResetCheck(short wChlNo);
參數說明以下。
l wChNo:標識通道號。
l 返回值:爲1表示檢測到忙音;爲0,表示沒有檢測到忙音。
(13)Sig_ResetCheck函數
該函數用於清空忙音檢測的緩衝區以及內部計數。當檢測對方掛機的忙音後,必須調用本函數。語法以下:
[DllImport("NewSig.DLL", CharSet = CharSet.Auto)]
public static extern void Sig_ResetCheck(short wChlNo);
參數說明以下。
l wChNo:標識通道號。
(14)Sig_StartDial函數
該函數用於撥打電話號碼。開始某通道的呼出過程。該函數只是設置通道的呼出緩衝區,真正的呼出過程須要循環調用Sig_CheckDial函數來逐步完成。語法以下:
[DllImport("NewSig.dll", CharSet = CharSet.Auto)]
public static extern int Sig_StartDial(short wChNo, [MarshalAs(UnmanagedType.LPArray)] byte[] DialNum, [MarshalAs(UnmanagedType.LPArray)] byte[] PreDialNum, short wMode);
參數說明以下。
l wChNo:標識通道號。
l DialNum:標識呼出號碼。
l PreDialNum:標識前導號碼。
l wMode:呼出檢測的模式。
(15)Sig_CheckDial函數
該函數用於檢測呼出結果。
在調用函數Sig_StartDial啓動撥號過程後,就能夠循環調用Sig_CheckDial函數維持撥號過程,並檢測呼出的結果,直至獲得結果爲止。
撥號的通常過程以下。
1.若是參數PreDialNum不爲空,則延遲1秒後撥出PreDialNum,若是參數PreDialNum爲空,則直接進入步驟3。
2.檢測PreDialNum是否已發完。如已發完轉至步驟3。
3.檢測是否有撥號音,如撥號音長度達到配置項DialToneAfterOffHook的數值,則發送DialNum碼串,並轉至步驟4。如在此步驟已等待配置項NoDialToneAfterOffHook定義的時間長度仍未檢測到撥號音,則返回0x10。
4.檢測DialNum碼串是否發完,如已發完則延遲StartDelay配置項的時間長度後進入步驟5。
5.若是從進入此步驟起已通過配置項RingLen定義的時間長度,撥號音仍未中止則返回0x10;若是在此步驟已等待配置項NoRingLen定義的時間長度仍未檢測到回鈴音則返回0x10;若是檢測到佔線忙音數達到配置項BusySigCount定義的數字,則返回0x21;若是檢測到對方摘機,則返回0x14;若是進入此步驟已通過配置項Ringback_NoAnswerTime定義的時間長度,而且已檢測到回鈴音,則返回0x13;其餘狀況返回0x10。
注意:在進行呼出結果檢測以前必須調用函數StartSigCheck啓動信號音採集過程,而且在進行呼出結果檢測時,要循環調用FeedSigFunc函數維持信號音採集過程。
語法以下:
[DllImport("NewSig.dll", CharSet = CharSet.Auto)]
public static extern int Sig_CheckDial(short wChNo);
參數說明以下。
l wChNo:標識通道號。
l 返回值包括如下幾種狀況。
l 16(0x10):還沒有得出結果。
l 15(0x0F):沒有撥號音。
l 33(0x21):檢測到對方佔線的忙音。
l 20(0x14):對方摘機,能夠進行通話。
l 19(0x13):振鈴若干次,無人接聽電話。
l 21(0x15):沒有信號音。
注意:關於語音卡其餘函數語法請參見光盤中的本實例文件D161A.CS,該文件給出大部分語音卡的函數語法。
實現過程
(1)新建一個項目,命名爲Ex13_11,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個Button控件,用於執行電話撥號和電話掛機,添加一個DataGridView控件,顯示語音卡各通道及通道狀態,添加Timer組件實現電話的呼出過程,添加一個TextBox控件,用於輸入呼出電話號碼。
(3)主要程序代碼。
在窗體裝載事件中,主要進行初始化語音卡驅動程序,而且檢測通道總數及狀態,爲每一條通道分配語音緩衝區。代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
//初始化驅動程序
long load = DJ160API.LoadDRV();
//檢測通道總數,併爲每一個通道分配語音緩衝區
short wuseCh = DJ160API.CheckValidCh();
short wFileBufLen = 16 * 1024;
long card = DJ160API.EnableCard(wuseCh, wFileBufLen);
//設置表格通道的行數
dataGridView1.RowCount = wuseCh;
//檢測每一個通道類型
short chanelTpye = 0; //定義通道類型變量
string strType = "";
for (short i = 0; i < wuseCh; i++)
{
chanelTpye = DJ160API.CheckChType(i);
dataGridView1[0, i].Value = i;
switch (chanelTpye)
{
case 0:
strType = "內線";
break;
case 1:
strType = "外線";
break;
case 2:
strType = "懸空";
break;
}
dataGridView1[1, i].Value = strType;
dataGridView1[2, i].Value = "空閒";
}
}
在DataGridView控件中選擇一個外線空閒通道,單擊【撥號】按鈕,進行電話撥號,而且將撥號過程當中的狀態顯示在相應的DataGirdView表格中。代碼以下:
private void button1_Click(object sender, EventArgs e)
{
short wuseCh = DJ160API.CheckValidCh();
short wFileBufLen = 16 * 1024;
long card = DJ160API.EnableCard(wuseCh, wFileBufLen);
DJ160API.Sig_Init(chanel);
//檢查(外線)是否有振鈴信號或(內線)是否有提機
bool ring = DJ160API.RingDetect(chanel);
//外線提機
DJ160API.OffHook(chanel);
byte[] ss =new byte[textBox1.Text.Length];
byte[] s ={ 0 };
for (int i = 0; i < textBox1.Text.Length; i++)
{
ss[i] = Convert.ToByte(textBox1.Text.Substring(i, 1));
}
DJ160API.Sig_StartDial(chanel, ss, s, 0);
timer1.Enabled = true;
dataGridView1[2, chanel].Value = "撥號中...";
dataGridView1[3, chanel].Value = textBox1.Text;
}
單擊【掛機】按鈕,實現電話掛機功能。代碼以下:
private void button2_Click(object sender, EventArgs e)
{
DJ160API.HangUp(chanel);
DJ160API.Sig_ResetCheck(chanel);
DJ160API.StartSigCheck(chanel);
timer1.Enabled = false;
dataGridView1[2, chanel].Value = "空閒";
dataGridView1[3, chanel].Value = "";
}
Sig_StartDial函數用於撥打電話號碼。開始某通道的呼出過程。該函數只是設置通道的呼出緩衝區,真正的呼出過程需循環調用Sig_CheckDial函數來逐步完成。代碼以下:
private void timer1_Tick(object sender, EventArgs e)
{
DJ160API.Sig_CheckDial(chanel);
}
單擊DataGridView控件的相應行記錄相應的通道號,代碼以下:
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
chanel = (short)e.RowIndex;
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
實現電話自助服務系統。
實現電話自動報警系統。
實例429 客戶來電查詢系統
實例說明
隨着市場競爭的加重,企業愈來愈重視客戶服務和市場反饋。本例實現了電話來電的顯示支持功能。當有客戶打入電話時,會讀取客戶的電話號碼,根據電話號碼能夠提取客戶的相關信息,方便客服人員有針對性地進行服務。實例運行結果如圖13.13所示。
圖13.13 客戶來電查詢
技術要點
其餘相關函數介紹請參見實例「語音卡電話呼叫系統」,本實例主要介紹GetCallerIDStr函數,該函數用於獲取主叫號碼。語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern short GetCallerIDStr(short wChnlNo, byte[] IDStr);
參數說明以下。
l wChnlNo:標識通道號。
l IDStr:用於接收讀取的號碼。
l 返回值:爲0,表示未收到任何信息;爲1,表示正在接收頭信息;爲2表示正在接收ID號碼;爲3表示接收完畢,校驗正確;爲4表示接收完畢,校驗錯誤。
在調用GetCallerIDStr函數時,只有返回值爲3或4才表示已經正確接收了主機號碼。
實現過程
(1)新建一個項目,命名爲Ex13_12,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個DataGridView控件,顯示語音卡各通道和通道狀態,並在來電時顯示來電號碼;添加一個Timer控件用於時刻檢測來電信息;添加其餘控件及用途如圖13.13所示。
(3)主要程序代碼。
private void timer1_Tick(object sender, EventArgs e)
{
byte[] ss = new byte[100];
for (short i = 0; i < 8; i++)
{
DJ160API.StartSigCheck(i);
if(open_close==false)
DJ160API.ResetCallerIDBuffer(i);
if (DJ160API.RingDetect(i))
{
open_close = true;
//獲取來電號碼
result = DJ160API.GetCallerIDStr(i, ss);
if (result == 3 || result == 4)
{
string str = Encoding.UTF8.GetString(ss);
txtTel.Text = str;
txtTel.Text = txtTel.Text.Substring(txtTel.Text.Length - 8, 8);
dataGridView1[2, i].Value = txtTel.Text;
//查詢客戶資料
this.getMessage(txtTel.Text);
}
}
}
}
private void getMessage(string str)
{
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "db_csell.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("SELECT * FROM 我的名錄表 WHERE 電話='" + str + "'", con);
DataSet ds = new DataSet();
dap.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
txtName.Text = ds.Tables[0].Rows[0]["姓名"].ToString();
txtDuty.Text = ds.Tables[0].Rows[0]["職務"].ToString();
txtAddress.Text = ds.Tables[0].Rows[0]["地址"].ToString();
txtMobile.Text = ds.Tables[0].Rows[0]["手機"].ToString();
txtCompany.Text = ds.Tables[0].Rows[0]["公司名稱"].ToString();
txtPostId.Text = ds.Tables[0].Rows[0]["郵編"].ToString();
}
else
{
labStatus.Text = "非本單位會員客戶。。。。";
}
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
用語音卡實現客戶某項費用的查詢。
用語音卡實現客戶在某個時間的留言信息。
用語音卡實現客戶購買物品的查詢。
實現客戶反饋電話錄音系統。
實例430 語音卡實現電話錄音
實例說明
現在的許多電話都具備電話錄音的功能。本例實現了該功能,當有電話打入時,即刻將雙方的對話信息進行錄音。實例運行結果如圖13.14所示。
技術要點
其餘相關函數介紹請參見實例「語音卡電話呼叫系統」,本實例主要介紹StartRecordFile函數和StopRecordFile函數。
(1)StartRecordFile函數用於開始文件錄音。中止該方式的錄音必定要用StopRecordFile函數。檢查錄音是否結束,用CheckRecordEnd函數。StartRecordFile函數語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern bool StartRecordFile(short wChnlNo, byte[] FileName, long dwRecordLen);
參數說明以下。
l wChnINo:標識錄音的通道號。
l FileName:標識錄音的文件名。
l dwRecordLen:標識文件大小。
(2)StopRecordFile函數用於中止錄音。該函數語法以下:
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern void StopRecordFile(short wChnlNo);
參數說明以下。
l wChnINo:標識要中止的錄音通道。
(3)CheckRecordEnd函數檢查指定通道錄音是否結束(緩衝區已滿)。
[DllImport("Tc08a32.dll", CharSet = CharSet.Auto)]
public static extern int CheckRecordEnd(int ChannelNo);
參數說明以下。
l wChnINo:標識錄音的通道號。
l 返回值:0表示未結束;1表明結束。
實現過程
(1)新建一個項目,命名爲Ex13_13,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個DataGridView控件,顯示語音卡各通道和通道狀態,並在來電時顯示來電號碼;添加一個Timer控件用於實時檢測來電信息,若是來電,程序將自動摘機而且實現錄音;添加其餘控件及用途如圖13.14所示。
(3)主要程序代碼。
private void timer1_Tick(object sender, EventArgs e)
{
//維持文件錄音持續執行
DJ160API.PUSH_PLAY();
for (short i = 0; i < 8; i++)
{
DJ160API.StartSigCheck(i);
if (open_close == false)
DJ160API.ResetCallerIDBuffer(i);
if (DJ160API.RingDetect(i))
{
open_close = true;
//摘機
DJ160API.OffHook(i);
DJ160API.StartSigCheck(i);
//是否掛機
if (DJ160API.ReadCheckResult(i, 2) != 33)
{
bool bl = DJ160API.StartRecordFile(i, Encoding.UTF8.GetBytes(@"D:\ly.001"), 600 * 1024);
dataGridView1[2, i].Value = "已接來電,開始錄音";
}
else
{
DJ160API.StopRecordFile(i);
open_close = false;
DJ160API.Sig_ResetCheck(i);
dataGridView1[2, i].Value = "";
}
if (DJ160API.CheckRecordEnd(i)==1)
{
DJ160API.StopRecordFile(i);
open_close = false;
dataGridView1[2, i].Value = "";
}
}
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
用語音卡實現電話點播歌曲,經過按鍵的選擇點播本身喜好的歌曲。
用語音卡實現產品報價,經過按鍵實現某些產品的報價。
利用語音卡實現產品報價。
13.6 手機程序開發
現在手機已成爲大衆的交流工具。有關手機的程序開發愈來愈普遍,本節經過幾個典型實例介紹如何利用短信貓發送、接收短信、遠程控制計算機、業務員銷售數據採集和短信息娛樂互動平臺。
實例431 利用短信貓收發短信息
實例說明
短信貓是利用SIM卡發送短信的硬件設備,經過串口或USB接口(根據設備型號而定)與計算機相連。在程序中能夠利用短信貓發送或接收短信。本例實現了利用短信貓收發短信息的功能。實例運行結果如圖13.15所示。
技術要點
本例使用的是北京人大金倉信息技術有限公司的串口短信貓。在購買短信貓時會附帶包括SDK的開發包,其中提供了操做短信貓的函數(封裝在dllforvc.dll動態庫中)。下面介紹操做短信貓的主要函數。
(1)GSMModemGetSnInfoNew函數
該函數獲取短信貓註冊須要的信息,代碼以下:
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetSnInfoNew",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetSnInfoNew(string device, string baudrate);
參數說明以下。
l device:通訊端口,爲null時系統會自動檢測。
l baudrate:通信波特率,爲null時系統會自動檢測。
(2)GSMModemGetDevice函數
該函數獲取當前的通信端口,代碼以下:
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetDevice",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetDevice();
(3)GSMModemGetBaudrate函數
該函數獲取當前的通信波特率,代碼以下:
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetBaudrate",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetBaudrate();
(4)GSMModemInitNew函數
該函數用於初始化短信貓。語法以下:
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemInitNew",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern bool GSMModemInitNew(
string device,
string baudrate,
string initstring,
string charset,
bool swHandshake,
string sn);
參數說明以下。
l device:標識通訊端口,若是爲NULL,系統會自動檢測。
l baudrate:標識通信波特率,若是爲NULL,系統會自動檢測。
l initstring:標識初始化命令,爲NULL便可。
l charset:標識通信字符集,爲NULL便可。
l swHandshake:標識是否進行軟件握手,爲False便可。
l sn:標識短信貓的受權號,須要根據實際狀況填寫。
(5)GSMModemSMSsend函數
該函數用於發送手機短信。語法以下:
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemSMSsend",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern bool GSMModemSMSsend(
string serviceCenterAddress,
int encodeval,
string text,
int textlen,
string phonenumber,
bool requestStatusReport);
參數說明以下。
l serviceCenterAddress:標識短信中心號碼,爲NULL便可。
l encodeval:標識短信息編碼格式,若是爲8,表示中文短信編碼。
l text:標識短信內容。
l textlen:標識短信內容的長度。
l phonenumber:標識接收短信的電話號碼。
l requestStatusReport:標識狀態報告。
(6)GSMModemSMSReadAll函數
該函數取得全部短信息,包括SIM卡和手機中的短信息。返回的短信內容格式爲電話號碼1|短信內容1||電話號碼2|短信內容2||:
//接收短信息返回字符串格式爲:手機號碼|短信內容||手機號碼|短信內容||
//RD_opt爲1表示接收短信息後不作任何處理,爲0表示接收後刪除信息
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemSMSReadAll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemSMSReadAll(int RD_opt);
參數說明以下。
l RD_opt:對讀取後的短信息進行處理,0表示刪除,1表示不作處理。
實現過程
(1)新建一個項目,命名爲Ex13_14,默認窗體爲Form1。
(2)在Form1窗體中,主要添加TextBox控件和Label控件,控件的數量及用途如圖13.15所示,添加兩個Button控件,分別用於發送短信息和接收短信息。
(3)主要程序代碼。
將所使用的函數封裝在GMS類中。代碼以下:
class GMS
{
//初始化gsm modem,並鏈接gsm modem
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemInitNew",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern bool GSMModemInitNew(
string device,
string baudrate,
string initstring,
string charset,
bool swHandshake,
string sn);
//獲取短信貓新的標識號碼
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetSnInfoNew",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetSnInfoNew(string device, string baudrate);
//獲取當前通信端口
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetDevice",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetDevice();
//獲取當前通信波特率
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetBaudrate",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetBaudrate();
//斷開鏈接並釋放內存空間
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemRelease",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern void GSMModemRelease();
//取得錯誤信息
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemGetErrorMsg",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemGetErrorMsg();
//發送短信息
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemSMSsend",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern bool GSMModemSMSsend(
string serviceCenterAddress,
int encodeval,
string text,
int textlen,
string phonenumber,
bool requestStatusReport);
//接收短信息返回字符串格式爲:手機號碼|短信內容||手機號碼|短信內容||
//RD_opt爲1接收短信息後不作任何處理,0爲接收後刪除信息
[DllImport("dllforvc.dll",
EntryPoint = "GSMModemSMSReadAll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern string GSMModemSMSReadAll(int RD_opt);
}
在裝載Form1窗體時,獲取設備信息。代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
//機器號碼,當參數爲空時,函數自動獲取設備信息
txtJQHM.Text = GMS.GSMModemGetSnInfoNew(txtCOM.Text, txtBTL.Text);
txtCOM.Text = GMS.GSMModemGetDevice(); //COM1
txtBTL.Text= GMS.GSMModemGetBaudrate(); //波特率
}
發送短信息。代碼以下:
private void btnSend_Click(object sender, EventArgs e)
{
if(txtSJHM.Text == "")
{
MessageBox.Show("手機號碼不能爲空!","提示", MessageBoxButtons.OK);
txtSJHM.Focus();
return;
}
if(txtContent.Text=="")
{
MessageBox.Show("短信內容不能爲空!", "提示", MessageBoxButtons.OK);
txtContent.Focus();
return;
}
//鏈接設備
if(GMS.GSMModemInitNew(txtCOM.Text, txtBTL.Text, null, null, false, txtPower.Text)==false)
{
MessageBox.Show("設備鏈接失敗!" + GMS.GSMModemGetErrorMsg(),"提示", MessageBoxButtons.OK);
return;
}
// 發送短信
if (GMS.GSMModemSMSsend(null, 8, txtContent.Text, Encoding.Default.GetByteCount(txtContent.Text),txtSJHM.Text, false) == true)
MessageBox.Show("短信發送成功!", "提示", MessageBoxButtons.OK);
else
MessageBox.Show("短信發送失敗!" + GMS.GSMModemGetErrorMsg(), "提示", MessageBoxButtons.OK);
}
接收短信息。代碼以下:
private void btnResvice_Click(object sender, EventArgs e)
{
//1)鏈接設備
if (GMS.GSMModemInitNew(txtCOM.Text, txtBTL.Text, null, null, false, txtPower.Text) == false)
{
MessageBox.Show("鏈接失敗!" + GMS.GSMModemGetErrorMsg(), "提示", MessageBoxButtons.OK);
return;
}
//2)接收短信
txtContent.Text = GMS.GSMModemSMSReadAll(1);
txtSJHM.Text = txtContent.Text.Substring(0, 13);
txtContent.Text = txtContent.Text.Substring(13, txtContent.Text.Length-13);
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
利用短信貓羣發短信。
辦公自動化系統,辦公短信通知、短信日程提醒、應急信息短信發佈和短信審批等。
實例432 利用短信遠程關閉計算機
實例說明
本例實現了利用短信遠程關閉計算機的功能。運行程序,首先,進行關機信息設置;而後,開啓服務;最後,經過手機向短信貓發送「關機」數據。片刻以後,指定的計算機將會自動關機。程序如圖13.16所示。
技術要點
相關函數請參見實例「利用短信貓收發短信息」中的技術要點。
實現過程
(1)新建一個項目,命名爲Ex13_15,默認窗體爲Form1。
(2)在Form1窗體中,主要添加TextBox控件和Label控件,控件的數量及用途如圖13.16所示,添加一個Button控件,用於開啓或中止遠程關閉計算機服務。
(3)主要程序代碼。
private void Form1_Load(object sender, EventArgs e)
{
//機器號碼
txtJQHM.Text = GMS.GSMModemGetSnInfoNew(txtCOM.Text, txtBTL.Text);
txtCOM.Text = GMS.GSMModemGetDevice(); //COM1
txtBTL.Text = GMS.GSMModemGetBaudrate(); //波特率
labStatus.Text = "服務關閉中。。。。。";
}
private void Close_Windows()
{
try
{
//指定生成 WMI 鏈接所需的全部設置
ConnectionOptions op = new ConnectionOptions();
op.Username = txtUser.Text; //遠程計算機用戶名稱
op.Password = txtPWD.Text; //遠程計算機用戶密碼
//設置操做管理範圍
ManagementScope scope = new ManagementScope("\\\\" + txtIP.Text + "\\root\\cimv2", op);
scope.Connect(); //將此 ManagementScope 鏈接到實際的 WMI 範圍。
ObjectQuery oq = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query = new ManagementObjectSearcher(scope, oq);
//獲得WMI控制
ManagementObjectCollection queryCollection = query.Get();
foreach (ManagementObject obj in queryCollection)
{
obj.InvokeMethod("ShutDown", null); //執行關閉遠程計算機
}
}
catch(Exception ex)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.WriteLine("shutdown /s");
p.StandardInput.WriteLine("exit");
}
}
private void timer1_Tick(object sender, EventArgs e)
{
//鏈接設備
if (GMS.GSMModemInitNew(txtCOM.Text, txtBTL.Text, null, null, false, txtPower.Text) == false)
{
MessageBox.Show("鏈接失敗!" + GMS.GSMModemGetErrorMsg(), "提示", MessageBoxButtons.OK);
return;
}
//接收短信
string str = GMS.GSMModemSMSReadAll(1);
if (str==null)
return;
if (str.Substring(str.IndexOf("|")+1, 2) == "關機")
{
this.Close_Windows();
}
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "開啓服務")
{
timer1.Enabled = true;
labStatus.Text = "關機命令採集中。。。。。";
button1.Text = "中止服務";
}
else
{
timer1.Enabled = false;
button1.Text = "開啓服務";
labStatus.Text = "服務關閉中。。。。。";
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
利用短信實現客戶資料查詢。
保險行業中:保單查詢、續費提醒、客戶生日提醒和保費計算等。
實例433 短信息採集菸草銷售數據
實例說明
在各種銷售行業中,產品銷售數據量是企業不可缺乏的一項數據。當銷售人員在外地出差而且在沒有計算機的狀況下,如何及時的將銷售數據彙報到公司中呢?
本例實現利用短信息採集菸草銷售數據的功能。銷售人員根據規定的格式編輯短信發送到短信息貓中便可。運行程序,單擊【菸草銷售信息採集】按鈕採集菸草銷售數據,而後單擊【統計】按鈕,將銷售數據整理出來。如圖13.17所示。
技術要點
相關函數請參見實例「利用短信貓收發短信息」中的技術要點。
另外,程序規定的編輯短信息格式爲,以冒號「:」分隔並結束。例如「張三:業務員:12:長春:3400:」。其中,主要使用String.Split( )方法將信息數據拆分。
圖13.17 短信息採集菸草銷售數據
String.Split( )方法:返回包含此實例中的子字符串(由指定 Char 數組的元素分隔)的 String 數組。
語法:
public string[] Split (
params char[] separator
)
參數說明以下。
l separator:分隔此實例中子字符串的 Unicode 字符數組,不包含分隔符的空數組或空引用。
l 返回值:一個數組,其元素包含此實例中的子字符串,這些子字符串由 separator 中的一個或多個字符分隔。
實現過程
(1)新建一個項目,命名爲Ex13_16,默認窗體爲Form1。
(2)在Form1窗體中,主要添加TextBox控件和Label控件,控件的數量及用途如圖13.17所示,添加3個Button控件,分別用於發送短信息、採集銷售數據和整理採集數據,添加兩個DataGridView表格,分別用於顯示短信息內容和整理後的銷售數據。
(3)主要程序代碼。
單擊【菸草銷售信息採集】按鈕,接受短信息並保存到數據庫中。代碼以下:
private void btnResvice_Click(object sender, EventArgs e)
{
//鏈接設備
if (GMS.GSMModemInitNew(txtCOM.Text, txtBTL.Text, null, null, false, txtPower.Text) == false)
{
MessageBox.Show("鏈接失敗!" + GMS.GSMModemGetErrorMsg(), "提示", MessageBoxButtons.OK);
return;
}
//接收短信
string content = GMS.GSMModemSMSReadAll(0);
if (content ==null)
{
this.getMessage();
return;
}
content= content.Replace("||", "|"); // 替換||爲|
string[] str_sp = content.Split('|');// 進行分離
int k=0;
dataGridView1.ColumnCount = 2;
dataGridView1.RowCount = str_sp.Length / 2;
dataGridView1.Columns[0].HeaderText = "手機號碼";
dataGridView1.Columns[1].HeaderText = "短信息";
for (int i = 0; i < str_sp.Length / 2; i++)
{
for (int j = 0; j < 2; j++)
{
dataGridView1[j, i].Value = str_sp[k];
if (k % 2 != 0)
this.InsertMessage("insert into RecivedBox (Mobile,Content,reciveTime)values('" + Convert.ToString(dataGridView1[0, i].Value) + "','" + Convert.ToString(dataGridView1[1, i].Value) + "','" + DateTime.Now.ToString() + "') ");
k++;
}
}
this.getMessage();
}
自定義方法getSplitMessage()用來拆分短信息而且整理爲正規數據。代碼以下:
private void getSplitMessage()
{
string content = "";
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
content = content + Convert.ToString(dataGridView1["短信息", i].Value);
}
string[] str_sp = content.Split(':');// 進行分離
int k = 0;
dataGridView2.ColumnCount = 5;
dataGridView2.RowCount = str_sp.Length/5 ;
dataGridView2.Columns[0].HeaderText = "姓名";
dataGridView2.Columns[1].HeaderText = "職務";
dataGridView2.Columns[2].HeaderText = "月份";
dataGridView2.Columns[3].HeaderText = "銷售地區";
dataGridView2.Columns[4].HeaderText = "銷售數量";
for (int i = 0; i < str_sp.Length / 5; i++)
{
for (int j = 0; j < 5; j++)
{
dataGridView2[j, i].Value = str_sp[k];
k++;
}
}
}
自定義InsertMessage()方法,將接受的短信息保存到數據庫中。代碼以下:
private void InsertMessage(string strSql)
{
//將短信息內容添加到數據庫中
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "message.mdb" + ";Persist Security Info=False");
con.Open();
OleDbCommand cmd = new OleDbCommand(strSql, con);
cmd.ExecuteNonQuery();
con.Close();
}
自定義getMessage()方法,獲取數據庫中全部的短信息數據。代碼以下:
private void getMessage()
{
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "message.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("select mobile as 手機號碼,content as 短信息,reciveTime as 日期 from RecivedBox", con);
DataSet ds = new DataSet();
dap.Fill(ds);
dataGridView1.DataSource = ds.Tables[0].DefaultView;
}
調用自定義getSplitMessage()方法,將整理後的銷售數據顯示在DataGridView表格中。代碼以下:
private void btnFind_Click(object sender, EventArgs e)
{
if(dataGridView1.Rows.Count>0)
this.getSplitMessage();
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
利用短信實現銷售業績查詢。
利用短信實現訂購商品。
實例434 「春晚」節目評比短信息互動平臺
實例說明
在觀看娛樂電視節目中,主持人常常帶動場外的電視觀衆參與到活動當中。例如,在春節聯歡晚會中,經過編輯短信來選取觀衆最喜歡的春晚節目,那麼本實例將實現爲「春晚」節目評比的短信息互動平臺。電視觀衆根據規定的格式編輯短信發送到短信息互動平臺進行節目評比。運行程序,單擊【獲取參與觀衆短信】按鈕,便可獲得觀衆的投票,如圖13.18所示。
技術要點
相關函數請參見實例「利用短信貓收發短信息」中的技術要點。
實現過程
(1)新建一個項目,命名爲Ex13_17,默認窗體爲Form1。
(2)在Form1窗體中,主要添加TextBox控件和Label控件,控件的數量及用途如圖13.18所示,添加一個Button控件,用於獲取參與的觀衆短信息,添加一個DataGridView控件,用於顯示觀衆的投票信息。
(3)主要程序代碼。
private void btnResvice_Click(object sender, EventArgs e)
{
//鏈接設備
if (GMS.GSMModemInitNew(txtCOM.Text, txtBTL.Text, null, null, false, txtPower.Text) == false)
{
MessageBox.Show("鏈接失敗!" + GMS.GSMModemGetErrorMsg(), "提示", MessageBoxButtons.OK);
return;
}
//接收短信
string content = GMS.GSMModemSMSReadAll(0);
if (content == null)
{
this.getMessage();
return;
}
content = content.Replace("||", "|"); // 替換||爲|
string[] str_sp = content.Split('|');// 進行分離
int k = 0;
string[,] str_Sp = new string[2, str_sp.Length / 2];
for (int i = 0; i < str_sp.Length / 2; i++)
{
for (int j = 0; j < 2; j++)
{
str_Sp[j, i] = str_sp[k];
if (k % 2 != 0)
this.InsertMessage("insert into RecivedBox (Mobile,Content,reciveTime)values('" + Convert.ToString(str_Sp[0, i]) + "','" + Convert.ToString(str_Sp[1, i]) + "','" + DateTime.Now.ToString() + "') ");
k++;
}
}
this.getMessage();
}
private void getMessage()
{
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "message.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("select mobile as 手機號碼,content as 短信息,reciveTime as 日期 from RecivedBox", con);
DataSet ds = new DataSet();
dap.Fill(ds);
dataGridView1.DataSource = ds.Tables[0].DefaultView;
}
private void InsertMessage(string strSql)
{
//將短信息內容添加到數據庫中
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "message.mdb" + ";Persist Security Info=False");
con.Open();
OleDbCommand cmd = new OleDbCommand(strSql, con);
cmd.ExecuteNonQuery();
con.Close();
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
手機短信息平臺訂購商品。
定時向手機發送天氣預報
13.7 其他程序
實例435 條形碼掃描器銷售商品
實例說明
現在,許多超市都利用條形碼銷售商品。微機操做員利用掃描器在商品的條形碼處進行掃描,商品的詳細信息就會顯示在屏幕中。本例實現了利用條形碼銷售商品的功能。效果如圖13.19所示。
技術要點
當利用掃描器掃描條形碼時,條形碼數據會顯示在當前得到焦點的窗口控件中。例如,若是當前編輯框得到焦點,那麼條形碼數據會顯示在TextBox文本框中。而後會向TextBox文本框發送回車鍵按下時的消息。
在程序中只要觸發TextBox文本框的KeyDown事件,判斷當前按鍵是不是回車鍵,若是是,讀取TextBox文本框中的條形碼數據,並從數據表中根據條形碼查詢商品信息,將其顯示在DataGridView列表中。
實現過程
(1)新建一個項目,命名爲Ex13_18,默認窗體爲Form1。
(2)在Form1窗體中,主要添加TextBox控件,用於接收條形碼;添加一個DataGridView控件,用於顯示掃描器掃描條形碼的商品銷售信息。
(3)主要程序代碼。
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyValue == 13)
{
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "price.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("select * from MerchandiseInfo where barcode='" + textBox1.Text + "'", con);
DataSet ds = new DataSet();
dap.Fill(ds);
if (ds.Tables[0].Rows.Count == 0)
{
MessageBox.Show("該商品不存在!", "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
for (int i = 0; i < dataGridView1.RowCount; i++)
{
if (Convert.ToString(dataGridView1[0, i].Value) == "")
{
dataGridView1[0, i].Value = ds.Tables[0].Rows[0][0].ToString();
dataGridView1[1, i].Value = ds.Tables[0].Rows[0][1].ToString();
dataGridView1[2, i].Value = ds.Tables[0].Rows[0][2].ToString();
dataGridView1[3, i].Value = ds.Tables[0].Rows[0][3].ToString();
return;
}
}
}
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
超市條形碼掃描系統。
公司工具條形碼掃描系統。
實例436 利用神龍卡製做練歌房程序
實例說明
在開發酒店、賓館的點歌系統時,使用神龍DVD解壓卡能夠方便地進行媒體控制。神龍DVD解壓卡(如下簡稱神龍卡)是一款專門針對中國大陸市場而開發出來的DVD硬解壓卡,神龍卡與好萊塢卡的基本功能相近,兩卡比較具備如下幾個區別。
l 神龍卡可播放中國大陸全區碼DVD碟,好萊塢卡可播放全球1~6區碼影碟,可無數次解區碼。
l 神龍卡支持1~5M碼流播放,好萊塢卡可支持1~15M的視頻流播放。
l 神龍卡支持全屏播放,好萊塢卡支持全屏及窗口(便可縮放窗口)播放。
l 神龍卡支持Win 9x下工做環境,好萊塢卡可支持Win 9x及WinNT下工做環境。
本例利用神龍卡實現了音樂的控制功能。運行程序,結果如圖13.20所示。
圖13.20 利用神龍卡製做練歌房程序
技術要點
本程序主要經過一個第三方NNSREALmagicCtrl.ocx控件實現。在.NET下使用第三方控件,首先,須要進行Windows註冊,註冊命令爲「REgsvr32 路徑\NNSREALmagicCtrl.ocx」;其次,將註冊成功的控件添加到Microsoft Visual Studio 2005開發環境中,實現步驟爲:選擇菜單「工具」/「選擇工具箱」,彈出「選擇工具箱」窗口,在該窗口中選擇「COM組件」選項卡,在列表中選擇註冊的第三方控件,單擊【肯定】按鈕便可,如圖13.21所示。
圖13.21 添加第三方控件
實現過程
(1)新建一個項目,命名爲Ex13_19,默認窗體爲Form1。
(2)在Form1窗體中,主要添加DataGridView控件,用於選擇播放影音;添加其餘控件及用途如圖13.20所示。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
if (strFileName == "")
{
MessageBox.Show("請在列表中選擇播放文件!","系統提示");
return;
}
axREALmagicCtrl1.Filename = strFileName; //指定播放文件
axREALmagicCtrl1.Play(); //播放
}
private void btnPause_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.Pause(); //暫停播放
}
private void btnStop_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.Stop(); //中止播放
}
private void btnSpeed_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.CurrentFrame = axREALmagicCtrl1.CurrentFrame + 125; //快進
}
private void btnRecede_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.CurrentFrame = axREALmagicCtrl1.CurrentFrame - 125; //快退
}
private void rdoLeftTrack_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.AudioChannel = NNSREALmagicCtrl.TAudChannel.acLEFT; //左聲道
}
private void rdoRightTrack_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.AudioChannel = NNSREALmagicCtrl.TAudChannel.acRIGHT; //右聲道
}
private void rdoStereo_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.AudioChannel = NNSREALmagicCtrl.TAudChannel.acSTEREO; //立體聲
}
private void tbVolume_Scroll(object sender, EventArgs e)
{
axREALmagicCtrl1.Volume = tbVolume.Value; //音量
}
private void rdoTV_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.DisplayDevice = NNSREALmagicCtrl.TDisDev.ddTV; //TV輸出模式
}
private void rdoVGA_Click(object sender, EventArgs e)
{
axREALmagicCtrl1.DisplayDevice = NNSREALmagicCtrl.TDisDev.ddVGA; //VGA輸出模式
}
private void Form1_Load(object sender, EventArgs e)
{
if (!axREALmagicCtrl1.OpenDriver()) //打開驅動
{
MessageBox.Show("打開驅動失敗!!", "系統提示");
this.groupBox1.Enabled = false;
this.groupBox2.Enabled = false;
this.groupBox3.Enabled = false;
this.groupBox4.Enabled = false;
return;
}
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "DataBase.mdb" + ";Persist Security Info=False");
OleDbDataAdapter dap = new OleDbDataAdapter("select G_name as 影音名稱,G_YC as 原唱,G_wjlx as 文件格式 from g_music_name", con);
DataSet ds = new DataSet();
dap.Fill(ds); //顯示影音文件的相關屬性
dataGridView1.DataSource = ds.Tables[0].DefaultView;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{ //選擇播放的影音
strFileName = @"\vod\" + dataGridView1[0, e.RowIndex].Value.ToString() + "." + dataGridView1[2, e.RowIndex].Value.ToString();
}
觸類旁通
根據本實例,讀者能夠開發如下程序。
練歌房卡拉OK系統。
單機卡拉OK系統。
第16章 加密、安全與軟件註冊
16.1 數據加密與解密
在企業的計算機中,每每存有大量的機密文件,這些機密文件與企業的發展有緊密聯繫,若是這些機密文件保管不善,將會使企業遭受巨大的損失。本節經過幾個典型實例詳細介紹一下C#中的加密與解密技術。
實例463 數據加密技術
實例說明
本實例實現對文件的機密數據進行加密的功能。運行程序,在文本框中輸入要加密的數據,單擊【加密】按鈕,對數據進行加密,並將加密後的數據顯示在「加密後的字符」文本框中。實例運行結果如圖16.1所示。
技術要點
實現本實例功能主要用到了System.Security.Cryptography命名空間下的MD5Crypto- ServiceProvider類的ComputeHash( )方法、System.Text命名空間下的ASCIIEncoding類的ASCII屬性、GetBytes( )方法和GetString( )方法。下面分別進行介紹。
(1)System.Security.Cryptography命名空間
System.Security.Cryptography 命名空間提供加密服務(包括安全的數據編碼和解碼)以及許多其餘操做,例如散列法、隨機數字生成和消息身份驗證。
(2)MD5CryptoServiceProvider類
此類使用加密服務提供程序(CSP)提供的實現,計算輸入數據的MD5哈希值。
語法格式爲:
public sealed class MD5CryptoServiceProvider : MD5
注意:MD5CryptoServiceProvider類的哈希大小爲128位。
(3)ComputeHash( )方法
此方法計算指定字節數組的哈希值。
語法格式爲:
public byte[] ComputeHash (byte[] buffer)
參數說明以下。
l buffer:要計算其哈希代碼的字節數組。
l 返回值:計算所得的哈希代碼。
(4)System.Text命名空間
表示 ASCII、Unicode、UTF-7和UTF-8字符編碼的類;是用於將字符塊轉換爲字節塊和將字節塊轉換爲字符塊的抽象基類。
(5)ASCIIEncoding類
此類表示Unicode字符的ASCII字符編碼。
語法格式爲:
public class ASCIIEncoding : Encoding
(6)ASCII屬性
此屬性獲取 ASCII(7位)字符集的編碼。
語法格式爲:
public static Encoding ASCII { get; }
l 屬性值:ASCII(7 位)字符集的Encoding。
(7)GetBytes( )方法
此方法將指定的String中的全部字符編碼爲一個字節序列。
語法格式爲:
public virtual byte[] GetBytes (string s)
參數說明以下。
l s:包含要編碼的字符的String。
(8)GetString( )方法
此方法將指定字節數組中的全部字節解碼爲一個字符串。
語法格式爲:
public virtual string GetString (byte[] bytes)
參數說明以下。
l bytes:包含要解碼的字節序列的字節數組。
l 返回值:包含指定字節序列解碼結果的String。
注意:使用MD5CryptoServiceProvider類必須引用System.Security.Cryptography命名空間。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_01,默認窗體爲Form1。
(2)在Form1窗體中,主要添加兩個TextBox控件,用來輸入和顯示字符串;添加一個Button控件,用來執行加密操做。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{ MessageBox.Show("請輸入加密數據"); return; }
MD5CryptoServiceProvider M5 = new MD5CryptoServiceProvider();
textBox2.Text = ASCIIEncoding.ASCII.GetString(M5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(textBox1.Text)));
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
對機密的文件夾進行加密與解密。
在數據傳輸中使用,以便保證傳輸信息不被泄露。
實例464 文本文件加密與解密
實例說明
本實例實現對計算機中的一些很是機密的文本文件進行加密與解密操做。運行程序,單擊【選擇文件】按鈕,選擇要進行加密或解密的文本文件(.txt格式的文件),單擊【加密】或【解密】按鈕,便可完成對文本文件的加密或解密操做。實例運行結果如圖16.2所示。
技術要點
實現本實例功能主要用到了System.Security.Cryptography命名空間下的RijndaelManaged類的CreateDecryptor( )方法、CreateEncryptor( )方法、CryptoStream類的Write( )方法、FlushFinalBlock( )方法、Close( )方法、System.IO命名空間下的FileStream類、StreamReader類的ReadToEnd( )方法、StreamWriter類的Write( )方法、Flush( )方法和BinaryReader類的ReadBytes( )方法。System.Security.Cryptography命名空間在第16章實例463中已經作過詳細介紹,這裏再也不詳細描述,下面對本實例中用到的其餘知識進行詳細介紹。
(1)RijndaelManaged類
此類表示Rijndael對稱加密算法的全部實現必須從其繼承的基類中得到。
語法格式爲:
public abstract class Rijndael SymmetricAlgorithm
注意:此算法支持12八、192或256位的密鑰長度。
(2)CreateDecryptor( )方法
此方法使用指定的Key和初始化向量(IV)建立對稱的Rijndael解密器對象。
語法格式爲:
public override IcryptoTransform CreateDecryptor (byte[] rgbKey,byte[] rgbIV)
參數說明以下。
l rgbKey:用於對稱算法的機密密鑰。
l rgbIV:用於對稱算法的IV。
l 返回值:對稱的Rijndael解密器對象。
(3)CreateEncryptor( )方法
此方法使用指定的Key和初始化向量(IV)建立對稱的Rijndael加密器對象。
語法格式爲:
public override ICryptoTransform CreateEncryptor (byte[] rgbKey,byte[] rgbIV)
參數說明以下。
l rgbKe:用於對稱算法的機密密鑰。
l rgbIV:用於對稱算法的IV。
l 返回值:對稱的Rijndael加密器對象。
(4)CryptoStream類
此類定義將數據流連接到加密轉換的流。
語法格式爲:
public CryptoStream (Stream stream,ICryptoTransform transform,CryptoStreamMode mode)
參數說明以下。
l stream:對其執行加密轉換的流。
l Transform:要對流執行的加密轉換。
l Mode:CryptoStreamMode值之一。CryptoStreamMode值及明說如表16.1所示。
表16.1 CryptoStreamMode值及說明
值 |
說 明 |
Read |
對加密流的讀訪問 |
Write |
對加密流的寫訪問 |
(5)CryptoStream類的Write( )方法
此方法將一個字節序列寫入當前CryptoStream類中,並從當前位置寫入指定的字節數。
語法格式爲:
public override void Write (byte[] buffer,int offset,int count)
參數說明以下。
l buffer:字節數組。此方法將count個字節從buffer複製到當前流。
l offset:buffer中的字節偏移量,今後偏移量開始將字節複製到當前流。
l count:要寫入當前流的字節數。
(6)FlushFinalBlock( )方法
此方法用緩衝區的當前狀態更新基礎數據源或儲存庫,隨後清除緩衝區。
語法格式爲:
public void FlushFinalBlock ()
(7)Close( )方法
關閉當前流並釋放與之關聯的全部資源(如套接字和文件句柄)。
語法格式爲:
public virtual void Close ()
(8)System.IO命名空間
System.IO命名空間包含容許讀寫文件和數據流的類型以及提供基本文件和目錄支持的類型。
(9)FileStream類
此類公開以文件爲主的Stream,既支持同步讀寫操做,也支持異步讀寫操做。
語法格式爲:
public FileStream (string path,FileModemode,FileAccessaccess)
參數說明以下。
l path:當前FileStream類對象封裝文件的相對路徑或絕對路徑。
l Mode:FileMode常數,肯定如何打開或建立文件。FileMode常數的值及說明如表16.2所示。
l Access:FileAccess常數,肯定FileStream對象訪問文件的方式。這將獲取FileStream對象的CanRead和CanWrite屬性。若是path指定磁盤文件,則CanSeek爲True。FileAccess常數的值及說明如表16.3所示。
表16.2 FileMode常數的值及說明
常 數 值 |
說 明 |
Append |
打開現有文件並查找到文件尾,或建立新文件。FileMode.Append只能同FileAccess.Write一塊兒使用。任何讀嘗試都將失敗並引起ArgumentException |
Create |
指定操做系統應建立新文件。若是文件已存在,它將被改寫。這要求FileIOPermissionAccess.Write和System.IO.FileMode.Create等效於這樣的請求:若是文件不存在,則使用CreateNew;不然使用Truncate |
CreateNew |
指定操做系統應建立新文件。此操做須要FileIOPermissionAccess.Write。若是文件已存在,則將引起IOException |
Open |
指定操做系統應打開現有文件。打開文件的能力取決於FileAccess所指定的值。若是該文件不存在,則引起System.IO.FileNotFoundException |
OpenOrCreate |
指定操做系統應打開文件(若是文件存在);不然,應建立新文件。若是用FileAccess.Read打開文件,則須要FileIOPermissionAccess.Read。若是文件訪問爲FileAccess.Write或FileAccess.ReadWrite,則須要FileIOPermissionAccess.Write。若是文件訪問爲FileAccess.Append,則須要FileIOPermissionAccess.Append |
Truncate |
指定操做系統應打開現有文件。文件一旦打開,就將被截斷爲零字節大小。此操做須要FileIOPermissionAccessWrite。試圖從使用Truncate打開的文件中進行讀取將致使異常 |
表16.3 FileAccess常數的值及說明
常 數 值 |
說 明 |
Read |
對文件的讀訪問。可從文件中讀取數據。同Write組合即構成讀寫訪問權 |
ReadWrite |
對文件的讀訪問和寫訪問。可從文件讀取數據和將數據寫入文件 |
Write |
文件的寫訪問。可將數據寫入文件。同Read組合即構成讀/寫訪問權 |
(10)StreamReader類
此類實現一個TextReader,使其以一種特定的編碼從字節流中讀取字符。
語法格式爲:
public StreamReader (Stream stream)
參數說明以下。
l stream:要讀取的流。
(11)ReadToEnd( )方法
此方法從流的當前位置到末尾讀取流。
public override string ReadToEnd ()
l 返回值:字符串形式的流的其他部分(從當前位置到末尾)。若是當前位置位於流的末尾,則返回空字符串(「」)。
(12)StreamWriter類
此類實現一個TextWriter,使其以一種特定的編碼向流中寫入字符。
語法格式爲:
public StreamWriter (Stream stream)
參數說明以下。
l stream:要寫入的流。
(13)StreamWriter類的Write( )方法
此方法將字符寫入流。
語法格式爲:
public override void Write (char value)
參數說明以下。
l value:要寫入文本流中的字符。
(14)Flush( )方法
此方法清理當前編寫器的全部緩衝區,並使全部緩衝數據寫入基礎流。
語法格式爲:
public override void Flush ()
(15)BinaryReader類
此類用特定的編碼將基元數據類型讀做二進制值。
語法格式爲:
public BinaryReader (Stream input)
參數說明以下。
l Input:流。
(16)ReadBytes( )方法
此方法從當前流中將count個字節讀入字節數組,並使當前位置提高count個字節。
語法格式爲:
public virtual byte[] ReadBytes (int count)
參數說明以下。
l count:要讀取的字節數。
l 返回值:包含從基礎流中讀取的數據的字節數組。若是到達了流的末尾,則該字節數組可能小於所請求的字節數。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_02,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個TextBox控件,用來顯示文件路徑;添加一個OpenFileDialog控件,用來選擇要加密或解密的文件;添加3個Button控件,用來執行加密、解密和選擇文件操做。
(3)主要程序代碼。
加密文本文件的實現代碼以下:
private void button5_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);
csDecrypt.FlushFinalBlock();
csDecrypt.Close();
fsIn.Close();
fsOut.Close();
if (MessageBox.Show(strOutName, "提示:加密成功!加密後的文件名及路徑爲:\n"+"是否刪除源文件", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
File.Delete(strPath);
textBox1.Text = "";
}else
{ textBox1.Text = ""; }
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
}
解密文本文件的實現代碼以下:
private void button4_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();
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
{
textBox1.Text = "";
}
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
對重要公文進行加密。
對網絡中傳輸的文件進行加密與解密。
實例465 利用圖片加密文件
實例說明
本實例中,利用圖片生成密鑰,而後對文本文件進行加密和解密操做。運行程序,單擊【打開圖片】按鈕,選擇密鑰圖片,而後單擊【打開文本文件】按鈕,選擇要加密或解密的文件,單擊【加密】或【解密】按鈕完成文本文件的加密或解密操做。解密時的密鑰圖片要與加密時的密鑰圖片相同,不然解密不能成功。實例運行結果如圖16.3所示。
技術要點
實現本實例功能主要用到了System.Security.Cryptography命名空間下的RC2CryptoServiceProvider類的CreateDecryptor( )方法、CreateEncryptor( )方法、CryptoStream類的Write( )方法、FlushFinalBlock( )方法、Close( )方法、System.IO命名空間下的FileStream類、BinaryReader類的ReadBytes( )方法、BinaryWriter類的Write( )方法、File類的Delete( )方法和Copy( )方法。以上大部分知識在第16章實例464中已經作過詳細介紹,這裏再也不詳細講解。下面主要對RC2CryptoServiceProvider類、BinaryWriter類的Write( )方法、File類的Delete( )方法和Copy( )方法進行介紹。
(1)RC2CryptoServiceProvider類
此類定義訪問RC2算法的加密服務提供程序(CSP)實現的包裝對象。
(2)BinaryWriter類
此類以二進制形式將基元類型寫入流,並支持用特定的編碼寫入字符串。
語法格式爲:
public BinaryWriter (Streamoutput)
參數說明以下。
l output:輸出流。
(3)BinaryWriter類的Write( )方法
此方法將一個無符號字節寫入當前流,並將流的位置提高1個字節。
語法格式爲:
public virtual void Write (byte value)
參數說明以下。
l value:要寫入的無符號字節。
(4)File類
此類提供用於建立、複製、刪除、移動和打開文件的靜態方法,並協助建立FileStream對象。
(5)Delete( )方法
此方法刪除指定的文件。若是指定的文件不存在,則引起異常。
語法格式爲:
public static void Delete (string path)
參數說明以下。
l path:要刪除的文件的名稱。
(6)Copy( )方法
此方法將現有文件複製到新文件,不容許改寫同名的文件。
語法格式爲:
public static void Copy (string sourceFileName,string destFileName)
參數說明以下。
l sourceFileName:要複製的文件。
l destFileName:目標文件的名稱,不能是一個目錄或現有文件。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_03,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個TextBox控件,用來顯示加密或解密文件的路徑;添加一個OpenFileDialog控件,用來選擇要加密或解密的文件和打開密鑰的圖片;添加4個Button控件,用來執行加密、解密、打開文件和打開圖片操做;添加一個PictureBox控件,用於顯示密鑰圖片。
(3)主要程序代碼。
利用圖片加密文本文件的實現代碼以下:
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);
//初始化Key IV
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 desc = new RC2CryptoServiceProvider();//desc進行加密
BinaryReader br = new BinaryReader(fsText);//從要加密的文件中讀出文件內容
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);
}
}
利用圖片解密文本文件的實現代碼以下:
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);
//初始化Key IV
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 desc = new RC2CryptoServiceProvider();//desc進行解密
CryptoStream csDecrypt = new CryptoStream(fsOut, desc.CreateDecryptor(bykey, byIv), CryptoStreamMode.Read);//讀出加密文件
BinaryReader sr = new BinaryReader(csDecrypt);//從要加密流中讀出文件內容
BinaryWriter sw = new BinaryWriter(fs);//寫入解密流
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);
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
利用各類圖片加密文件。
使用圖片批量解密文件。
16.2 Access數據庫安全
因爲Access數據庫操做簡單,使用方便,因此一些中小型的應用軟件常常採用Access數據庫。爲了保證數據庫的安全,常常要加密或鎖定數據庫,若是數據庫因爲某種緣由遭到破壞,就須要對數據庫進行修復。下面的幾個實例分別實現了加密、鎖定和修復Access數據庫的功能。
實例466 如何編程修復Access數據庫
實例說明
Access數據庫操做簡單,使用方便,是中小型企業常常採用的數據庫,但Access數據庫容易遭到破壞,並隨着時間的增長,數據庫文件會變得很是大,該如何解決這些問題呢?本實例經過壓縮數據庫的方法從新組織修復數據庫,減小了數據庫佔用的空間。運行程序,單擊【打開】按鈕,找到要修復的數據庫,單擊【開始修復】按鈕,便可完成修復數據庫操做。實例運行結果如圖16.4所示。
技術要點
實現本實例功能主要用到了JRO命名空間下JetEngineClass對象的CompactDatabase( )方法、System.IO命名空間下File類的Copy( )方法和Delete( )方法。下面分別進行介紹。
(1)CompactDatabase( )方法
CompactDatabase( )方法壓縮並回收本地數據庫中的浪費空間。
語法格式爲:
CompactDatabase(strng SourceConnection, string DestConnection)
參數說明以下。
l SourceConnection:字符串值,指定與要壓縮的源數據庫的鏈接。
l DestConnection:字符串值,指定與要經過壓縮建立的目標數據庫的鏈接。
注意:必須引用C:\Program Files\Common Files\System\ado\msjro.dll,該DLL包含JRO命名空間。
(2)Copy( )方法
此方法將現有文件複製到新文件,不容許改寫同名的文件。
語法格式爲:
public static void Copy (string sourceFileName,string destFileName)
參數說明以下。
l sourceFileName:要複製的文件。
l destFileName:目標文件的名稱,不能是一個目錄或現有文件。
(3)Delete( )方法
此方法刪除指定的文件。
語法格式爲:
public static void Delete (string path)
參數說明以下。
l path:要刪除的文件的名稱。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_04,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個TextBox控件,用來顯示修復數據庫文件的路徑;添加一個OpenFileDialog控件,用來選擇要修復的數據庫文件;添加3個Button控件,用來執行修復、退出和打開數據庫文件操做。
(3)主要程序代碼。
開始壓縮數據庫的實現代碼以下:
string strPathMdb = null;//數據庫路徑
private void button2_Click(object sender, EventArgs e)
{
if (!File.Exists(strPathMdb)) //檢查數據庫是否已存在
{
MessageBox.Show("目標數據庫不存在,沒法壓縮","操做提示");
return;
}
//聲明臨時數據庫的名稱
string temp = DateTime.Now.Year.ToString();
temp += DateTime.Now.Month.ToString();
temp += DateTime.Now.Day.ToString();
temp += DateTime.Now.Hour.ToString();
temp += DateTime.Now.Minute.ToString();
temp += DateTime.Now.Second.ToString() + ".bak";
temp = strPathMdb.Substring(0, strPathMdb.LastIndexOf("\\") + 1) + temp;
//定義臨時數據庫的鏈接字符串
string temp2 = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + temp;
//定義目標數據庫的鏈接字符串
string strPathMdb2 = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strPathMdb;
//建立一個JetEngineClass對象的實例
JRO.JetEngineClass jt = new JRO.JetEngineClass();
//使用JetEngineClass對象的CompactDatabase方法壓縮修復數據庫
jt.CompactDatabase(strPathMdb2, temp2);
//複製臨時數據庫到目標數據庫(覆蓋)
File.Copy(temp, strPathMdb, true);
//最後刪除臨時數據庫
File.Delete(temp);
MessageBox.Show("修復完成");
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
定時數據庫壓縮。
定時數據庫備份。
實例467 訪問帶驗證模式的Sqlserver 2000數據庫
實例說明
本實例實現了訪問帶驗證模式的SQL Server 2000數據庫的功能。用戶登陸數據庫時,必須輸入用戶名和密碼。運行程序,輸入計算機的名稱或地址、訪問數據庫的用戶名、密碼和數據庫名稱,單擊【登陸】按鈕,便可登陸數據庫。實例運行結果如圖16.5所示。
技術要點
實現本實例功能主要用到了ADO.NET的SqlConnection對象的Open( )方法、Close( )方法和ConnectionState枚舉。下面分別進行介紹。
(1)SqlConnection對象
此對象表示SQL Server數據庫的一個打開的鏈接。
語法格式爲:
public SqlConnection (string connectionString)
參數說明以下。
l connectionString:用於打開SQL Server數據庫的鏈接。
(2)Open( )方法
此方法使用ConnectionString所指定的屬性設置打開的數據庫鏈接。
語法格式爲:
public override void Open ()
(3)Close( )方法
此方法關閉與數據庫的鏈接。這是關閉任何打開鏈接的首選方法。
語法格式爲:
public override void Close ()
(4)ConnectionState枚舉
此枚舉描述與數據源鏈接的當前狀態。
語法格式爲:
public enum ConnectionState
ConnectionState枚舉值及說明如表16.4所示。
表16.4 ConnectionState枚舉值及說明
枚 舉 值 |
說 明 |
Broken |
與數據源的鏈接中斷。只有在鏈接打開以後纔可能發生這種狀況。能夠關閉處於這種狀態的鏈接,而後從新打開 |
Closed |
鏈接處於關閉狀態 |
Connecting |
鏈接對象正在與數據源鏈接 |
Executing |
鏈接對象正在執行命令 |
Fetching |
鏈接對象正在檢索數據 |
Open |
鏈接處於打開狀態 |
注意:使用SqlConnection對象必須引用System.Data.SqlClient命名空間。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_05,默認窗體爲Form1。
(2)在Form1窗體中,主要添加4個TextBox控件,用於輸入登陸信息;添加3個Button控件,用來執行登陸、斷開鏈接和退出操做。
(3)主要程序代碼。
登陸數據庫的實現代碼以下:
public SqlConnection con = null;//實義數據庫鏈接對象
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
MessageBox.Show(textBox1.Tag.ToString()+"不能爲空","提示");
textBox1.Focus();
return;
}
if (textBox2.Text == "")
{
MessageBox.Show(textBox2.Tag.ToString() + "不能爲空", "提示");
textBox2.Focus();
return;
}
if (textBox4.Text == "")
{
MessageBox.Show(textBox4.Tag.ToString() + "不能爲空", "提示");
textBox4.Focus();
return;
}
try
{
string strcon = "server='" + textBox1.Text.Trim() + "';uid='" + textBox2.Text.Trim() + "';pwd='" + textBox3.Text + "';database='" + textBox4.Text.Trim() + "'";
con = new SqlConnection(strcon);//實例SqlConnect對象
con.Open();
MessageBox.Show("登陸成功");
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
斷開鏈接退出數據庫登陸的實現代碼以下:
private void button2_Click(object sender, EventArgs e)
{
if (con.State == ConnectionState.Open)
{
con.Close();
MessageBox.Show("鏈接已斷開");
}
else
{
MessageBox.Show("尚未鏈接數據庫");
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
對數據庫添加用戶。
對數據庫添加用戶權限。
16.3 軟件註冊與加密
爲了使開發的軟件能被更普遍地使用,開發者但願更多的用戶能試用軟件,而另外一方面,又不想讓用戶長時間無償使用未經受權的軟件,這就須要設計軟件註冊程序。下面經過幾個典型實例介紹保護軟件安全的方法。
實例468 利用INI文件對軟件進行註冊
實例說明
本實例實現使用INI文件對軟件的用戶信息進行註冊的功能。運行程序,輸入登陸名稱、登陸口令和註冊碼,單擊【註冊】按鈕進行註冊,若是註冊成功,則給出提示;若是信息已註冊,系統給出提示信息。實例運行結果如圖16.6所示。
技術要點
實現本實例功能主要用到API函數WritePrivateProfileString和GetPrivateProfileString函數。下面分別進行介紹。
(1)WritePrivateProfileString函數
此函數實現對INI文件的寫操做。
函數聲明以下。
[ DllImport ( "kernel32" ) ]
private static extern long WritePrivateProfileString ( string section ,string key , string val , string filePath ) ;
參數說明以下。
l section:INI文件中的段落。
l key:INI文件中的關鍵字。
l val:INI文件中關鍵字的數值。
l filePath:INI文件完整的路徑和名稱。
(2)GetPrivateProfileString函數
此函數實現對INI文件的讀操做。
函數聲明以下。
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
參數說明如表16.5所示。
表16.5 參數說明
參 數 值 |
說 明 |
section |
INI文件中的段落名稱 |
key |
INI文件中的關鍵字 |
def |
沒法讀取時候的缺省數值 |
retVal |
讀取數值 |
size |
數值的大小 |
filePath |
INI文件的完整路徑和名稱 |
注意:使用API函數必須引用System.Runtime.InteropServices命名空間。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_07,默認窗體爲Form1。
(2)在Form1窗體中,主要添加3個TextBox控件,用於輸入註冊信息;添加兩個Button控件,用來執行註冊和退出操做。
(3)主要程序代碼。
註冊用戶信息的實現代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
FileStream c = new FileStream("C:\\desck.ini",FileMode.OpenOrCreate,FileAccess.Write);
}
[ DllImport ( "kernel32" ) ]
private static extern long WritePrivateProfileString ( string section ,string key , string val , string filePath ) ;
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
private void button1_Click(object sender, EventArgs e)
{
StringBuilder temp = new StringBuilder(200);
string FileName = "C:\\desck.ini";//NI文件的完整的路徑和名稱。
foreach (object ct in Controls)
{
if (ct.GetType().ToString() == "System.Windows.Forms.TextBox")
{
TextBox tx = (TextBox)ct;
if (tx.Text == "")
{
MessageBox.Show(tx.Tag.ToString()+"不能爲空");
}
}
}
string section = textBox3.Text;//INI文件中的段落
string key = textBox1.Text;//INI文件中的關鍵字
string keyValue = textBox2.Text;//INI文件中的關鍵字
int i = GetPrivateProfileString(section, key, "沒法讀取對應數值!", temp, 200, FileName);//判斷是否註冊過
if (temp.ToString() == "沒法讀取對應數值!")
{
WritePrivateProfileString(section, key, keyValue, FileName);
MessageBox.Show("註冊成功寫入INI文件!", "信息");
}
else
{
MessageBox.Show("此信息已註冊過了");
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
對INI文件加密保存註冊信息。
對組合INI文件加密保存註冊信息。
實例469 利用註冊表設計軟件註冊程序
實例說明
大多數應用軟件會將用戶輸入的註冊信息寫進註冊表中,程序運行過程當中,能夠將這些信息從註冊表中讀出。本實例主要實如今程序中對註冊表進行操做的功能,運行程序,單擊【註冊】按鈕,會將用戶輸入的信息寫入註冊表中。實例運行結果如圖16.7所示。
技術要點
實現本實例功能主要用到了Microsoft.Win32命名空間下的Registry類的CurrentUser屬性、RegistryKey類的OpenSubKey( )方法、GetSubKeyNames( )方法、SetValue( )方法和CreateSubKey( )方法。下面分別進行介紹。
(1)Microsoft.Win32命名空間
Microsoft.Win32命名空間提供兩種類型的類:處理由操做系統引起的事件的類和操做系統註冊表的類。
(2)RegistryKey類
此類表示Windows註冊表中的項級節點,此類是註冊表封裝。
語法格式爲:
public sealed class RegistryKey : MarshalByRefObject, IDisposable
注意:要獲取RegistryKey實例,須要使用Registry類的靜態成員之一。
(3)Registry類
此類提供表示Windows註冊表中的根項的RegistryKey對象,並提供訪問項/值對的static( )方法。
語法格式爲:
public static class Registry
(4)CurrentUser屬性
此屬性包含有關當前用戶首選項的信息,該字段讀取Windows 註冊表中的HKEY_ CURRENT_USER註冊表項。
語法格式爲:
public static readonly RegistryKey CurrentUser
注意:存儲在此項中的信息包括環境變量的設置和有關程序組、顏色、打印機、網絡鏈接和應用程序首選項的數據,此項使創建當前用戶的設置更容易。在此項中,軟件供應商存儲要在其應用程序中使用的當前用戶特定的首選項。
(5)OpenSubKey( )方法
此方法檢索指定的子項。
語法格式爲:
public RegistryKey OpenSubKey (string name,bool writable)
參數說明以下。
l name:要打開的子項的名稱或路徑。
l writable:若是須要項的寫訪問權限,則設置爲True。
l 返回值:請求的子項;若是操做失敗,則爲空引用。
(6)CreateSubKey( )方法
此方法建立一個新子項或打開一個現有子項以進行寫訪問。字符串subkey不區分大小寫。
語法格式爲:
public RegistryKey CreateSubKey (string subkey)
參數說明以下。
l subkey:要建立或打開的子項的名稱或路徑。
l 返回值:RegistryKey對象,表示新建的子項或空引用。若是爲subkey指定了零長度字符串,則返回當前的RegistryKey對象。
(7)GetSubKeyNames( )方法
此方法檢索包含全部子項名稱的字符串數組。
語法格式爲:
public string[] GetSubKeyNames ()
l 返回值:包含當前項的子項名稱的字符串數組。
(8)SetValue( )方法
此方法設置指定的名稱/值對。
語法格式爲:
public void SetValue (string name,Object value)
參數說明以下。
l name:要存儲的值的名稱。
l value:要存儲的數據。
注意:對註冊表操做使用RegistryKey類和Registry類時,必須引用Microsoft.Win32 命名空間。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_07,默認窗體爲Form1。
(2)在Form1窗體中,主要添加3個TextBox控件,用於輸入註冊信息;添加兩個Button控件,用來執行註冊和退出操做。
(3)主要程序代碼。
private void button1_Click(object sender, EventArgs e)
{
if(textBox1.Text=="")
{
MessageBox.Show("公司名稱不能爲空"); return;
}
if(textBox2.Text=="")
{ MessageBox.Show("用戶名稱不能爲空"); return; }
if (textBox3.Text == "")
{ MessageBox.Show("註冊碼不能爲空"); return; }
//實例RegistryKey 類對象
Microsoft.Win32.RegistryKey retkey1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("software").OpenSubKey("ZHY").OpenSubKey("ZHY.INI", true);
foreach (string strName in retkey1.GetSubKeyNames())//判斷註冊碼是否過時
{
if (strName == textBox3.Text)
{
MessageBox.Show("此註冊碼已通過期");
return;
}
}//開始註冊信息
Microsoft.Win32.RegistryKey retkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("software", true).CreateSubKey("ZHY").CreateSubKey("ZHY.INI").CreateSubKey(textBox3.Text.TrimEnd());
retkey.SetValue("UserName", textBox2.Text);
retkey.SetValue("capataz", textBox1.Text);
retkey.SetValue("Code", textBox3.Text);
MessageBox.Show("註冊成功,您可使用本軟件");
Application.Exit();
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
註冊信息加密後存入註冊表。
記錄用戶試用次數的註冊程序。
實例470 利用網卡序列號設計軟件註冊程序
實例說明
本實例實現了利用本機網卡序列號生成軟件註冊碼的功能。運行程序,自動得到本機網卡序列號,單擊【生成註冊碼】按鈕,生成軟件註冊碼,將註冊碼依次輸入下面的文本框,單擊【註冊】按鈕實現軟件註冊功能。實例運行結果如圖16.8所示。
技術要點
實現本實例功能主要用到了Microsoft.Win32命名空間下的Registry類的CurrentUser屬性、RegistryKey類的OpenSubKey( )方法、GetSubKeyNames( )方法、SetValue( )方法、CreateSubKey( )方法、System.Management命名空間下的ManagementClass類的GetInstances( )方法、ManagementObjectCollection類和ManagementObject類。Microsoft.Win32命名空間下的類和方法在第16章實例469中已經作過介紹,這裏再也不詳細說明,下面主要對System.Management命名空間及該命名空間下的類進行詳細介紹。
(1)System.Management命名空間
提供對大量管理信息和管理事件集合的訪問,這些信息和事件是與根據Windows管理規範 (WMI)結構對系統、設備和應用程序設置檢測點有關的。
(2)ManagementClass類
表示公共信息模型(CIM)管理類。管理類是一個WMI類,如Win32_LogicalDisk和Win32_Process,前者表示磁盤驅動器,後者表示進程(如Notepad.exe)。
語法格式爲:
public class ManagementClass : ManagementObject
(3)GetInstances( )方法
返回該類的全部實例的集合。
語法格式爲:
public ManagementObjectCollection GetInstances ()
l 返回值:表示該類實例的ManagementObject對象的集合。
(4)ManagementObjectCollection類
基於指定的查詢檢索管理對象的集合。此類是用於檢索管理信息的較爲經常使用的入口點之一。例如,能夠用於枚舉系統中的全部磁盤驅動器、網絡適配器、進程及更多管理對象,或者用於查詢全部處於活動狀態的網絡鏈接以及暫停的服務等。
(5)ManagementObject類
表示 WMI 實例。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_08,默認窗體爲Form1。
(2)在Form1窗體中添加4個TextBox控件、3個Button控件和6個Label控件。其中,TextBox控件用輸入註冊碼,Button控件用來執行註冊、退出和生成註冊碼操做,Label控件用於顯示計算機名稱、網卡序列號、軟件註冊碼和一些提示信息等。
(3)主要程序代碼。
得到網卡序列號和計算機名稱的實現代碼以下:
private void Form1_Load(object sender, EventArgs e)
{
label2.Text = Environment.MachineName.ToString();//獲得計算機名
label4.Text = GetNetCardMacAddress();//獲得網卡信息
}
//得到網卡信息函數
public string GetNetCardMacAddress()
{
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection moc = mc.GetInstances();
string str = "";
foreach (ManagementObject mo in moc)
{
if ((bool)mo["IPEnabled"] == true)
str = mo["MacAddress"].ToString();
}
return str;
}
生成註冊碼的實現代碼以下:
string[] strLanCode = new string[12];// 網卡信息存儲
string[] strkey ={ "Q", "W", "7", "E", "D", "F", "2", "G", "R", "T", "Y", "8", "P", "N", "B", "V", "C", "X", "Z", "0", "9", "I", "8", "6", "U", "O", "P", "M", "5", "4", "3", "1", "A", "S", "H", "J", "K", "L" };
//生成註冊碼
public int intRand = 0;//判斷隨機生成次數
private void button1_Click(object sender, EventArgs e)
{
//把網卡信息轉換成字符串
string strCode = GetNetCardMacAddress();//調用函數獲取網卡信息
strCode = strCode.Substring(0, 2) + strCode.Substring(3, 2) + strCode.Substring(6, 2) + strCode.Substring(9, 2) + strCode.Substring(12, 2) +strCode.Substring(15, 2);
string strb = strCode.Substring(0, 4) + strCode.Substring(4, 4) + strCode.Substring(8, 4);//網卡信息存儲
for (int i = 0; i < strLanCode.Length; i++)//把網卡信息存入數組
{
strLanCode[i] = strb.Substring(i, 1);
}
Random ra = new Random();
switch (intRand)//隨機生成註冊碼的順序
{
case 0:
label5.Text = strCode.Substring(0, 4) + "-" + strCode.Substring(4, 4) + "-" + strCode.Substring(8, 4) + "-" + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString();
intRand = 1;
break;
case 1:
label5.Text = strCode.Substring(0, 4) + "-" + strCode.Substring(4, 4) + "-" + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString();
intRand = 2;
break;
case 2:
label5.Text = strCode.Substring(0, 4) + "-" + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString();
intRand = 3;
break;
case 3:
label5.Text = strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + strLanCode[ra.Next(0, 11)] + "-" + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString() + strkey[ra.Next(0, 37)].ToString();
intRand = 0;
break;
}
}
註冊軟件的實現代碼以下。
private void button2_Click(object sender, EventArgs e)
{
if (label5.Text == "")
{ MessageBox.Show("請生成註冊碼"); }
else
{
string strNameKey = textBox1.Text.TrimEnd() + textBox2.Text.TrimEnd() + textBox3.Text.TrimEnd() + textBox4.Text.TrimEnd();
string strNumber = label5.Text.Substring(0, 4) + label5.Text.Substring(5, 4) + label5.Text.Substring(10, 4) + label5.Text.Substring(15, 4);
if (strNameKey == strNumber)
{
Microsoft.Win32.RegistryKey retkey1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("software").OpenSubKey("ZHY").OpenSubKey("ZHY.INI", true);
foreach (string strName in retkey1.GetSubKeyNames())//判斷註冊碼是否過時
{
if (strName == strNameKey)
{
MessageBox.Show("此註冊碼已通過期");
return;
}
}//開始註冊信息
Microsoft.Win32.RegistryKey retkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("software",true).CreateSubKey("ZHY").CreateSubKey("ZHY.INI").CreateSubKey(strNumber.TrimEnd());
retkey.SetValue("UserName", "明日科技");
MessageBox.Show("註冊成功!", "提示");
Application.Exit();
}
else
{ MessageBox.Show("註冊碼輸入錯誤"); }
}
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
應用組件的註冊使用。
銷售的軟件產品進行受權。
實例471 根據cpu序列號、磁盤序列號設計軟件註冊程序
實例說明
本實例根據計算機的cpu號和硬盤序列號通過簡單的計算自動生成一組無規律的註冊碼來實現應用程序的註冊。運行程序,單擊【生成機器碼】按鈕,生成24位的機器碼,單擊【生成註冊碼】按鈕,根據生成的機器碼自動轉換出24位註冊碼,將註冊碼輸入文本框中,單擊【註冊】按扭,完成軟件註冊功能。實例運行對果如圖16.9所示。
技術要點
實現本實例功能主要用到了Microsoft.Win32命名空間下的Registry類的CurrentUser屬性、RegistryKey類的OpenSubKey( )方法、GetSubKeyNames( )方法、SetValue( )方法、CreateSubKey( )方法、System.Management命名空間下的ManagementClass類的GetInstances( )方法、ManagementObjectCollection類和ManagementObject類、Char字符、Random類的Next( )方法。Microsoft.Win32和System.Management命名空間下的類和方法在第16章實例469和470中已經作過介紹,這裏再也不詳細講解。下面對本實例中用到的其餘知識進行詳細介紹。
(1)Char字符
Char類型的常數能夠寫成字符、十六進制換碼序列或Unicode表示形式,用戶也能夠顯式轉換整數字符代碼。
(2)Random類
表示僞隨機數生成器,一種可以產生知足某些隨機性統計要求的數字序列的設備。
(3)Next方法
返回一個指定範圍內的隨機數。
語法格式爲:
public virtual int Next (int minValue,int maxValue)
參數說明以下。
l minValue:返回的隨機數的下界(隨機數可取該下界值)。
l maxValue:返回的隨機數的上界(隨機數不能取該上界值)。maxValue必須大於或等於minValue。
l 返回值:一個大於或等於minValue且小於maxValue的32位帶符號整數,即返回的值範圍包括minValue但不包括maxValue。若是minValue等於maxValue,則返回minValue。
實現過程
(1)新建一個Windows應用程序,將其命名爲Ex16_08,默認窗體爲Form1。
(2)在Form1窗體中,主要添加一個TextBox控件,用來輸入註冊碼;添加4個Button控件,用來執行註冊、退出、生成註冊碼和生成機器碼操做;添加3個Label控件,用於顯示軟件註冊碼和機器碼等信息。
(3)主要程序代碼。
得到CPU序列號和硬盤序列號的實現代碼以下:
public string GetDiskVolumeSerialNumber()取得設備硬盤的卷標號
{
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObject disk = new ManagementObject("win32_logicaldisk.deviceid=\"d:\"");
disk.Get();
return disk.GetPropertyValue("VolumeSerialNumber").ToString();
}
public string getCpu()得到CPU的序列號
{
string strCpu = null;
ManagementClass myCpu = new ManagementClass("win32_Processor");
ManagementObjectCollection myCpuConnection = myCpu.GetInstances();
foreach( ManagementObject myObject in myCpuConnection)
{
strCpu = myObject.Properties["Processorid"].Value.ToString();
break;
}
return strCpu;
}
生成機器碼的實現代碼以下:
private void button1_Click(object sender, EventArgs e)
{
label2.Text = getCpu() + GetDiskVolumeSerialNumber();//得到24位CPU和硬盤序列號
string[] strid = new string[24];
for (int i = 0; i < 24; i++)//把字符賦給數組
{
strid[i] = label2.Text.Substring(i, 1);
}
label2.Text = "";
Random rdid = new Random();
for (int i = 0; i < 24; i++)//從數組隨機抽取24個字符組成新的字符生成機器碼
{
label2.Text += strid[rdid.Next(0, 24)];
}
}
生成註冊碼的實現代碼以下:
public int[] intCode = new int[127];//用於存密鑰
public void setIntCode()//給數組賦值小於10個的隨機數
{
Random ra = new Random();
for (int i = 1; i < intCode.Length;i++ )
{
intCode[i] = ra.Next(0, 9);
}
}
public int[] intNumber = new int[25];//用於存機器碼的AscII值
public char[] Charcode = new char[25];//存儲機器碼字
//生成註冊碼
private void button2_Click(object sender, EventArgs e)
{
if (label2.Text != "")
{
//把機器碼存入數組中
setIntCode();//初始化127位數組
for (int i = 1; i < Charcode.Length; i++)//把機器碼存入數組中
{
Charcode[i] = Convert.ToChar(label2.Text.Substring(i - 1, 1));
}
for (int j = 1; j < intNumber.Length; j++)//把字符的ASCII值存入一個整數組中
{
intNumber[j] = intCode[Convert.ToInt32(Charcode[j])] + Convert.ToInt32(Charcode[j]);
}
string strAsciiName = null;//用於存儲機器碼
for (int j = 1; j < intNumber.Length; j++)
{
//MessageBox.Show((Convert.ToChar(intNumber[j])).ToString());
if (intNumber[j] >= 48 && intNumber[j] <= 57)//判斷字符ASCII值是否在0~9之間
{
strAsciiName += Convert.ToChar(intNumber[j]).ToString();
}
else if (intNumber[j] >= 65 && intNumber[j] <= 90)//判斷字符ASCII值是否在A~Z之間
{
strAsciiName += Convert.ToChar(intNumber[j]).ToString();
}
else if (intNumber[j] >= 97 && intNumber[j] <= 122)//判斷字符ASCII值是否在a~z之間
{
strAsciiName += Convert.ToChar(intNumber[j]).ToString();
}
else//判斷字符ASCII值不在以上範圍內
{
if (intNumber[j] > 122)//判斷字符ASCII值是否大於z
{ strAsciiName += Convert.ToChar(intNumber[j] - 10).ToString(); }
else
{
strAsciiName += Convert.ToChar(intNumber[j] - 9).ToString();
}
}
label3.Text = strAsciiName;//獲得註冊碼
}
}
else
{ MessageBox.Show("請選生成機器碼","註冊提示"); }
}
觸類旁通
根據本實例,讀者能夠實現如下功能。
獲取CPU信息。
進行遠程軟件產品的註冊。
---恢復內容結束---