找回密码
 入住遨海湾
搜索
网站解决方案专享优惠-3折上云
查看: 1207|回复: 1

[C#]C#实现软件的升级

[复制链接]
发表于 2004-12-24 14:17:00 | 显示全部楼层 |阅读模式

登录后查才能浏览下载更多咨询,有问题联系QQ:3283999

您需要 登录 才可以下载或查看,没有账号?入住遨海湾

×

丽水市汽车运输集团有限公司信息中心 苟安廷

2 B) l% R$ I8 T3 T, n3 e( p3 B

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 1 }# `, G7 o @+ y0 ~9 J 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 ! _/ `/ B( q7 ^/ M+ d" G创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: . q7 U4 r/ q) B* P3 O /// <summary> ) f' t: X; F6 X/ A1 J /// 应用程序的主入口点。 % Z& D3 ?) ~+ L0 @+ ~ /// </summary> ! g" O9 @& {% v# f; ]3 k [STAThread] 7 h, _0 Y2 `* P) W0 }" g static void Main() 7 z1 k! X5 O" L( D. w- h" } { 8 K3 e# [# ]) M6 b7 O" J Application.Run(new Form1()); ' a! Y c& F7 \) j. }8 f+ f0 X } 5 D6 V& E4 v& D+ j) v/ m7 Q1 W. u为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: # N6 b+ ?& o4 i6 K( j. e private static string m_UserName,m_Password; 0 N% u, v, f: K- I /// <summary> 6 e5 y$ A! O* K0 p /// 应用程序的主入口点。 7 [3 c6 |. o/ {! \8 Q; d) L /// </summary> ( `7 {6 d0 L" J4 s, p# h5 i& ]# O1 h [STAThread] p& V! a( E" u; Q9 ]# }, V static void Main(string[] args) ) V5 t) v7 o' C6 U# d ^3 X9 D { + b$ _" }3 t t if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 0 N6 k8 Q8 w% l _3 E; R! i { - @. [5 U: d. r1 C7 I //记录下用户名和密码,供软件使用 7 s; |6 r3 k P& l) e' x z m_UserName=args[0]; . P+ H2 E6 s5 S5 }7 d m_Password=args[1]; 7 F; | f) M o. {& K4 w Application.Run(new Form1()); 9 b! k& |( I) U9 `4 q } 4 p; q/ L T, r0 A else ( f3 [; B: y- v) J+ K { 4 n: G$ R' w! Q7 ] MessageBox.Show("不能从这里启动"); 2 _* _( x( Y2 A" j0 L" ]! g# g9 ^# P } ! {; D" z# o% f } ( l: a0 O4 i6 h- d- ` 为了显示登录是否正确,Load事件的代码修改为: 6 W1 U( U) F4 k" | private void Form1_Load(object sender, System.EventArgs e) : x Z0 O7 c4 y* T7 C8 ?' ^1 G { ) J) ?2 @7 t: G: P6 w g7 Y string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); . s9 p% ?6 i a% X. q3 g- i% O MessageBox.Show(msg); ( ?. i8 Y# K: q) x, l0 w; Q } 7 k4 Z U) \# p$ t* R6 o这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 9 ? @/ p: s) Q+ r: ?由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

7 S: J( Z, }: X- U

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 - \( D' ]5 _" k& D. Y3 w0 V 在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: 0 ^8 \, p1 n7 O+ E% [$ B3 Z 操作员表 版本表 ; v" o! b: k5 D, d5 w( a% p7 ? 字段名 类型 用途 字段名 类型 用途 " B' [- [1 h/ H# ?8 R( _序号 长整型 主键 序号 长整型 主键 ) C P* |8 ` j! X6 _( u2 K 姓名 字符 用户名 版本号 长整型 存放当前版本 " a2 E7 r3 _" H 文件名称 字符 本记录对应的文件名 7 c' ] R5 S3 y. E' ^& U) w密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 . B7 d( a1 K# n 我们手工输入一些用户名和密码,如下:

1 \" s8 p4 a$ A: K' i2 W

不要关闭刚才的主程序,直接选择菜单“文件”→“添加项目”→“新建项目”,输入项目名称为“UpLoad”,如下图:

' C( ?# _1 d; Y6 ?3 S8 L" i1 Y. n

点“确定”,同样,配置输出路径为D:\output。 - m O/ }. R" S5 y3 y- d 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

$ U6 W }6 O) x( V, w( g0 W

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 / g' b0 K2 ^ b/ D; ?2 P* ], m添加浏览按钮的响应代码如下: 6 h. D9 z! C3 S( p X8 q private void btnBrow_Click(object sender, System.EventArgs e) h2 Q- Y: Y) @3 T( ] Q7 D0 ] { 3 m( Z/ o. O" X' A, X# M, \5 d% |3 J OpenFileDialog myForm=new OpenFileDialog(); + k- P3 `: Z ^9 ^( g- I7 f myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; 4 i7 h( e. i# K. Y if(myForm.ShowDialog()==DialogResult.OK) 0 `1 A. Q$ R( u { . }9 |/ J* S8 Z! ~7 L. R* f this.txtFileName.Text=myForm.FileName; , x. r1 Q: R4 X; p" e } 7 u9 x; T$ w+ ]2 C! E } " @- N! \! A" v该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 8 B J+ U; z/ q) u; q1 s添加取消按钮响应代码,目的是关闭窗口: # W- U1 }' [$ Q! J private void btnCancel_Click(object sender, System.EventArgs e) - A% t; T2 c' G) y% `' Q) R% s { , r0 E4 W; z* R: E5 z this.Close(); 4 T& l) S1 N$ X U- L } . ^4 Y7 Z9 r, Z5 C; j添加两个引用: # \9 h- V' R" f using System.Data.OleDb; W |3 M0 R# z3 T# F- a$ H4 {4 N$ S g using System.IO; ) A+ b- ?# m) H 再添加两个变量: / p7 _2 g1 G: ?7 G- T8 Y7 Y% G+ V private DataSet m_DataSet=new DataSet(); $ p0 z2 V6 C/ G: i, z* k% w1 T private string m_TableName="版本"; , G6 f! w0 i+ N; f R 下面的函数去掉文件名中的路径: 8 A1 D; A& y2 ^* K) z. O /// <summary> + ]8 c1 ?: f1 R% J+ D /// 从一个含有路径的文件名中分离出文件名 " r5 P1 c3 W: H6 S- M }' _ /// </summary> ( G. Z6 p) M. ?; W. a( S. x4 ~ /// <param name="p_Path">包含路径的文件名</param> 8 j$ R% p" G x- y /// <returns>去掉路径的文件名</returns> & S: n* x2 ]7 q private string GetFileNameFromPath(string p_Path) + ^ \& a6 S; N3 S8 G: N) t { 0 P: Y% D$ F, j) ^) v0 z$ W: t' l string strResult=""; 2 x! F( x4 A# T6 m1 t$ W int nStart=p_Path.LastIndexOf("\\"); 2 j+ P) G% B/ T% Q if(nStart>0) 6 N8 J- Q& y4 W @ { : x! |( w/ ~+ \# G! G strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); ( q4 b" {. _" z } # k; N) |8 ` a8 ?9 E return strResult; , R' a3 P5 a" E3 d } ; Y+ M+ I6 L# R* L5 ?/ @ 添加确定按钮响应代码(含注释): & n Z% j; } M1 `. l- ]+ j8 B- Sprivate void btnOK_Click(object sender, System.EventArgs e) ' E' m2 C/ M0 K0 ] { 9 S) K: [8 V% Z8 H9 X# }; {' b! k //检查版本号是否合法 4 e; R2 ?# K* K% V9 m try % o; U8 S$ U% `- [& K( Q4 v { ( Y/ B! e0 k! l3 F, D4 R: F/ ] Decimal.Parse(this.txtVersion.Text); - R0 I& }4 S0 H* E2 V0 v' v0 S } 3 _ n6 f' [* r4 w# T& v* z( @ catch ' e. L: F6 i5 H/ H. p3 U; C { 1 W2 ~* X- X5 u, D MessageBox.Show("无效的版本号!"); - @, c$ m8 |' f this.txtVersion.Focus(); 7 Y8 w! t5 ~/ L& G% ?! p this.txtVersion.SelectAll(); $ z0 \( k& Q5 f* C/ W) K return; ! Y9 N; A% [2 \ J) }0 z! D# x- B }

5 M9 h- D, L- h$ m

if(this.txtFileName.Text.Trim().Length>0) ' q2 C3 u4 ~1 S% i/ ` { * r. K1 l' |8 c7 f* P w //检查文件是否存在 ! H' g- [: x) p2 ^ if(!File.Exists(this.txtFileName.Text.Trim())) 2 O8 y: t8 @4 s9 |' B6 d6 V* k { " A' J( s* `/ S/ h MessageBox.Show("文件不存在!"); 4 x: q$ m" \1 a return; / V, b+ o# _- j+ w; ?4 k9 E }

+ k- O0 B+ R4 a. @

//连接数据库 9 |6 C. S3 D# C; a6 ` string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ ; s( ?0 X7 l1 }& e9 T Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 2 L' k5 k- O! l9 r% q& w, c( h OleDbConnection myConnect=new OleDbConnection(strConnection); - f; t; {. T& s" X OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); : `, u" K$ S' e) m OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 1 x( L) n' o) b# E6 }8 ^% \! ?0 \ myDataAdapter.SelectCommand=myCommand; 6 C8 U2 ~0 F4 S/ H OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); , F* A. }/ U+ h myConnect.Open();

+ J5 P6 }* u/ p$ r1 x! n

//获取已有的数据 5 o! K' N2 x& K: E* y) C1 k C m_DataSet=new DataSet(); 4 @7 v( f! t# Q2 N T/ E try : Y2 a2 e6 \" \: j. P { 9 O2 p+ O; H4 y R9 H3 c! a/ _$ u myDataAdapter.Fill(m_DataSet,this.m_TableName); 6 Z0 {5 T* D. M( V1 q //如果是首次上传,则增加一条记录 % a7 y+ t$ u1 q1 X& o if(m_DataSet.Tables[m_TableName].Rows.Count==0) % A$ _2 p$ j7 h+ x { v9 C6 U9 J4 w J; ` DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); 0 [# V9 A0 y1 D, U" y newrow["序号"]="1"; + b$ h/ e, s$ d m_DataSet.Tables[m_TableName].Rows.Add(newrow); / K- {, r1 _2 N9 `0 u } $ n4 k" O# Y/ u# w: r$ P- P / g+ V* K, E) S# R DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; ) c) {! N: y) j4 |1 u //填入去掉路径的文件名称 ; e4 z/ u% i, m- c2 Y+ M row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); . H, ?# J4 {4 i# P. j. d( `# m4 K. H //填入版本号 ; M" U' ]. R1 l( } V" K row["版本号"]=this.txtVersion.Text.Trim();

0 ~8 \* u$ q( |9 M. o5 E( S* B

//将实际文件存入记录中 " B' _" J5 ?2 g4 S- |$ } FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); 0 F* A) ~# p% A! b4 r byte [] myData = new Byte [fs.Length ]; 9 ~1 G* U% \! l+ [5 H8 r fs.Position = 0; 6 ^$ V4 {% B& S; K fs.Read (myData,0,Convert.ToInt32 (fs.Length )); : m$ o; L9 ^0 c row["文件内容"] = myData; # `$ I! h3 `& ~* ]: W fs.Close();//关闭文件

$ F; V* w8 J4 j- F7 m+ L& c2 j

//更新数据库 / P# n1 C' S8 l$ }: ` myDataAdapter.Update(this.m_DataSet,this.m_TableName); 6 i# K+ C% u$ @( c% [ K4 ~ myConnect.Close(); : B7 v2 }$ |* K' E MessageBox.Show("文件更新成功!"); 8 h7 H& O8 g" d3 Y$ ~ } , n& \6 Q1 Q5 N% |3 Z+ c6 c3 i8 B catch(Exception ee) 6 k8 @, s- O3 e _1 O { - G/ O l" ~% w: D. n' Q/ R* M MessageBox.Show(ee.Message); 4 i- H) |9 I7 A } - N8 U2 |' N- U: Z : j* n. {+ i* t } % Q8 j$ ]! b3 ]4 U+ ~ else ( u! ], P+ l7 J { " A; ]9 }5 a, F% c. E% a j7 _ MessageBox.Show("请输入文件名"); ! H, Z; l/ ]5 U N } 4 x$ c6 u7 z7 B% P% _ } # f3 ?9 W$ g& m0 T" O1 D3 W% I至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 & d4 l0 a8 O1 `/ r' x% _ 最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 + B3 k( e! E: \& l添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

7 A; o! s$ Z$ E9 E

切换到代码窗口,添加引用: # H. E/ o ^/ \2 ]using System.Data.OleDb; - ^$ ^" O$ @: `' kusing System.Threading; & o$ A/ H1 ]& {4 m! L using System.IO; . w. Q' v# L J Busing Microsoft.Win32;

! j( n" Y( L* N+ |! W

再添加如下变量: * `9 S/ D' ]0 h /// <summary> x3 G% V" b7 R /// 存放操作员及密码的DataSet & ?, K/ P W/ r; W6 K# a* |- m7 Y /// </summary> 4 J8 V" A' Q' j+ h private DataSet m_DataSet; 3 [) G7 L5 O n/ x# Q8 y/ R- }4 J4 F /// <summary> c2 Q' X1 E, u5 X2 a /// 本功能用到的数据库表 : T: @0 Y% d! E /// </summary> 8 @* w4 ?$ D, ]$ e* r( h private string m_TableName="操作员"; 0 m& w9 S x" _ private DataTable m_Table; 1 ^; e$ M! ?$ b) P7 R+ ~/ I- z- ^ 为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: ; l4 S8 N- D. s/ x( a /// <summary> n. t, ~: L6 t% } /// 定义本软件在注册表中software下的公司名和软件名称 & k/ ?% N, P# C$ M /// </summary> # f8 c* F2 ~# `' R) p: I7 s* N private string m_companyname="lqjt",m_softwarename="autologin"; 0 X; E( x+ D8 P2 _% N6 | /// <summary> + k. @5 g1 O5 S$ a0 d/ n. ~- a0 U /// 从注册表中读信息; * j2 ^( l* H/ m2 @0 k* T /// </summary> 8 H, C+ ~" h$ n0 Y( F8 { /// <param name="p_KeyName">要读取的键值</param> 6 C' d& c+ y- f( I, w' u# a /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> 7 u3 H! \7 [% X' ~( `% k8 J( K( c" R private string ReadInfo(string p_KeyName) 1 x& ?+ {6 K1 F C& g. d$ z+ J* i { ! J4 w$ M. a3 X- d0 F% Q+ m T$ @ RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); 4 Y; I' ~7 s. |% b" S( Y" W RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); ( S' t0 M8 L: @% U& \% W string strvalue=""; 9 ^# f- g8 g+ ^% P & f# }6 x3 [3 l- c) [1 a8 w if(CompanyKey==null) $ R- _' h B+ `1 S) u return ""; # ~" e+ }, M1 Y: \ RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 % ?! ?* V8 q7 O1 q if(SoftwareNameKey==null) - T' n4 p/ p4 S' |+ i% } return "";

, b* r1 h! Z2 H- l) l, [

try ' }1 F2 h U( x8 X8 T" v { 4 `9 [8 k. D6 K! m* s strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); & q3 g/ |! _% [+ M, ]0 t" X7 ? } 8 X9 k+ o, H. @8 P+ ] catch ; @7 w! C! i. R" [' O1 ] {}

& t1 s7 Z, S, W# x

if(strvalue==null) - S0 I) r# R7 j+ D9 Z strvalue=""; . D2 k r: q5 m+ d% l# g return strvalue; 4 ^& t, p- y' ~" m2 M( D } - A& { G3 C" z' B, z /// <summary> 4 h" J7 ]6 C( @% L /// 将信息写入注册表 " A2 K4 Y$ f$ J' ]4 g& ]! Y+ T, }! R /// </summary> + I2 S& I% p' S1 F /// <param name="p_keyname">键名</param> . |2 S3 a3 V) z: D, H/ X9 C /// <param name="p_keyvalue">键值</param> 0 Q4 @- D* k7 z& `& d" K, t private void WriteInfo(string p_keyname,string p_keyvalue) - h; c4 s$ | _: g/ P { . H1 L( f1 a# @( Q7 x( v# L, f RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); : p5 J# ?7 G+ o9 o RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); 2 I W9 L+ u& A4 a/ [; Z5 k; j+ Z, r RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); 5 d. G9 X! g5 O/ P9 z1 ] //写入相应信息 7 i# O) ?! _) W SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); * N! K0 | G5 a/ o2 r6 T) Q& } } 7 O$ F1 n0 Q/ p1 I) U再写一个函数,用户来获取用户名/密码和更新主程序版本: 5 P0 ^4 E n( b0 u /// <summary> ; O R1 [* l" U+ W- z /// 获取操作员情况,同时更新主程序版本 2 [: p" [ a6 q" M' U9 e$ a; ? /// </summary> " q; [/ E0 Q/ V8 O; e! [1 z private void GetInfo() 7 N2 L* z8 Z% x g, x2 o" q { 1 ~- f- r$ K: X) {( \( C$ P this.m_DataSet=new DataSet(); 3 J( I) k: j1 X9 x this.combUsers.Items.Clear(); 7 a; Z8 {. h- x1 u" L0 D1 d string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

/ V; @% V6 ?& S8 N- B

//连接数据库 " N; |5 W0 B6 d. b1 g string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ : |0 D7 \ r- L1 G, j, p1 o Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 7 k1 C" K% p. ?2 | OleDbConnection myConnect=new OleDbConnection(strConnection); # K* g7 { \& D4 B OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); 4 z4 I/ V& G- O0 q, ~" [ OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); % a; W2 G+ |; H( G" z, Y: H myDataAdapter.SelectCommand=myCommand; * _! L( J: i, z! N" X0 U try 7 ?/ W% V& T; ^. G- {/ O7 y2 w { ( S7 H' O2 T: H F" r( O2 i myConnect.Open();

2 J1 k) T( y- e# y

//获取操作员信息 . S) h4 [+ }6 K- r. U* s2 G1 C' o myDataAdapter.Fill(this.m_DataSet,this.m_TableName); " p# H& i: Z# [/ E //将查询到的用户名填充到组合框供用户选择 . @" M& D" W5 k this.m_Table=this.m_DataSet.Tables[this.m_TableName]; 1 K* q2 x4 m6 _- ?% c$ `8 v foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) # S5 U6 B. Z$ E8 A. F { " O& H' L8 H2 i) | this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); * T! t/ }$ b& p$ q# @6 s% J& n }

8 }; W+ p( a7 O* F3 Z9 o/ s

//检查是否有新的版本 ' p( q5 k r3 d* s9 d6 Z8 M9 a DataSet dataset=new DataSet(); 9 F+ w$ i8 o- ~/ ` string tablename="tablename"; 1 T$ g, [1 v3 {5 Y# j/ f/ ] //为减少数据传送时间,不获取文件内容 & Z: ]) F! x. q strSql="select 文件名称,版本号 from 版本"; ) w6 y- J7 L: H, p! W" |7 n myCommand=new OleDbCommand(strSql,myConnect); 7 @( J5 ^" ` t6 H4 D9 } myDataAdapter=new OleDbDataAdapter(); , J. b3 i" x1 h9 J2 g5 [! F myDataAdapter.SelectCommand=myCommand; * J E( s) j$ _ b; X3 ` myDataAdapter.Fill(dataset,tablename); ( O9 I7 p {7 |- q) i. Q if(dataset.Tables[tablename].Rows.Count==1)//有文件 5 V6 `$ _' R" x { 5 ?, R4 Q6 Z D' } string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); ; J% o {* Y( @1 r) i string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); % j1 C/ o n' A) f% |2 y //读入本机主程序的版本号 3 L' I) O2 ^9 D& j string oldversion=this.ReadInfo(filename); 0 p4 A/ f2 R! D! w! m1 ? if(oldversion.Length==0)//不存在 & a3 E$ s* Q% w6 ~+ V7 K oldversion="0"; 3 h( z. P' z* d6 C- i7 G$ h# t if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 % X3 v+ v: F9 O/ J9 U6 ?* a { P* W6 ?; J/ R* h6 r* t //取回文件内容 " e7 q- A, X8 S3 H$ | C dataset=new DataSet(); + i$ w) }; Q. q% ~4 P( ]! [ strSql="select * from 版本"; 4 X3 b# ` f* e; { myCommand=new OleDbCommand(strSql,myConnect); * b. F& \8 _/ T9 L) {" W myDataAdapter=new OleDbDataAdapter(); 7 u6 |5 W, ?; e" J: A/ v5 e" t myDataAdapter.SelectCommand=myCommand; $ [$ g# Z4 t9 W3 U4 P$ y q: Z myDataAdapter.Fill(dataset,tablename); ) }2 z6 |; n y. q( `& z% ^" |: c& C //将文件下载到本地 # T; p, z; A* i. @/ V7 C# ?; V DataRow row=dataset.Tables[tablename].Rows[0]; - d& A2 [1 R) k9 T if(row["文件内容"]!=DBNull.value) . K; T/ q [& _* R2 O {

& K0 Z) f8 Z l

Byte[] byteBLOBData = new Byte[0]; % O- V6 s: P! k! ^. f byteBLOBData = (Byte[])row["文件内容"]; + ^5 U$ W5 j: x try 3 `& f( Z' r( }; p { & U* J0 @1 G0 @ w FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); 9 K# v5 p7 j4 @! t. _, f fs.Write(byteBLOBData,0,byteBLOBData.Length); 2 y4 z/ a* M# l" U: p9 p fs.Close(); 6 t$ E' [$ `/ _% S6 p //写入当前版本号,供下次使用 7 I9 N+ `; q" E; U7 K this.WriteInfo(filename,version); - ^& c/ \/ }( V9 J) D. W' Z! b } + b G' V! |' }) {5 k1 W catch(Exception ee) : g) |* w1 T2 P$ ~1 B( c { f) `; a/ I' Q, @ MessageBox.Show(ee.Message); * Q7 B0 [/ c$ L } ; y" s* F2 K% a `( o }

8 C8 c9 C b+ ~5 k& t" D# p

}//有新版本 9 m) f2 i- _0 E6 a }//有文件

( M( p' p- x5 R& }; L$ i

//关闭连接 , \" ?6 G. Q! c myConnect.Close(); % N3 H6 u2 }. M$ ^5 P; F } 2 O( o/ M% Z# Z% Y catch(Exception ee) 6 u$ _$ @: t/ h7 k& J { ; P* K' a; ?4 r1 V% @) @1 ^ MessageBox.Show(ee.Message); ; k& g5 r* c" o. X2 B return; " _* n2 r4 J2 x0 E, f6 M } ) F3 ?/ ~$ }0 s. M6 o //允许登录 , q4 p, B8 \. G+ D) e this.btnOK.Enabled=true; & D" g' L+ A. N D) k } - Z {8 a+ ^2 i( M$ p8 E1 ?5 v2 l

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: * q9 J, I0 N9 w: @1 @: u5 R9 [! h private void Form1_Load(object sender, System.EventArgs e) + c, u- D% ? W, B' f/ E { ' Q l% A \0 E( {/ e5 [2 W //为加快显示速度,将数据库连接等放到另外一个线程中去 z3 v* d: ~. t/ g- l* e Thread thread=new Thread(new ThreadStart(GetInfo)); # i* \- C. {5 { thread.Start(); 2 R8 @% W; p$ d8 L% R1 i0 P } 1 }* ~8 Y. `, w3 G5 P/ h- N t' u 有了上述准备,我们来编写确定按钮的响应代码如下: ! b( G! j) o0 Z8 R- ] private void btnOK_Click(object sender, System.EventArgs e) _1 U1 y4 v$ i1 x { 4 t' I7 z/ z6 S# l' f+ M9 b0 F //根据组合框的选择,得到当前用户在DataSet中具体物理位置 0 D6 d/ y7 [8 k& c if(this.combUsers.SelectedIndex<0)//没有选择 ! v+ `6 L/ a+ {' M8 f( \, w return; 6 m1 {+ `" t D$ I DataRow rowNow=null; 4 _: s, S0 W" U7 |* G! {; H' P foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) " c" C) w+ U% | { + O8 e2 B6 E) y: X if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) % s4 W9 J }8 X! u' @* j# \ { " u+ [; x7 z. k- x1 F) Z. L4 x rowNow=row; & u$ M5 F* a- x" V4 w6 t V break; + B6 t( F6 X! [9 C2 T- ~( y } 7 }; U; i3 M' b1 T) D# `7 h1 T } 9 k& D# t+ W }; |) m: T if(rowNow==null) , a1 i4 h; |( y3 U ^ return;

//获取当前正确密码 , Q7 h0 s6 \! ^( { string strPassword=rowNow["密码"].ToString().Trim(); 4 H( u- y: Y. O$ _' O this.txtPassword.Text=this.txtPassword.Text.Trim(); / Y& L; S) \2 x: e4 B if(this.txtPassword.Text==strPassword)//密码正确 " Y" s0 A+ Q/ P& \ {

//主程序名称 7 x. h( H, K; R string filename=Application.StartupPath+"\\"+"MainPro.exe"; $ V Q$ r9 _6 s. z7 ] //参数名称 0 \1 {+ C- j- A- [; S) Z string arg=this.combUsers.Text+" "+this.txtPassword.Text; 8 x* K- _1 p) i* r* I( P //运行主程序 $ G9 i0 V9 W4 n% s$ k. d, u% s System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

//关闭登录框 : q# Q( _# T8 h8 n- w this.Close(); 7 y! S6 n+ J9 J2 B. E } , k5 |: X0 Q6 k9 S* R else ) H" T" E& }) I9 A' `; w6 I { 1 v8 w; l: I6 o; O; e! \9 x8 L MessageBox.Show(" 密码错误!如果你确信密码输入正确,\n可以试着检查一下大写字母键是否按下去了。", $ h; T( L0 C# F& k/ L. B& S "警告",MessageBoxButtons.OK,MessageBoxIcon.Warning); $ I/ L0 e8 r$ n2 b: c5 G& y this.txtPassword.Focus(); - v2 K% z# [7 ^! ] this.txtPassword.SelectAll(); 4 \, \+ c, E/ r' g$ l! F% e } & G6 ]( p# K7 |3 t! b4 O } 7 V! I$ J% U- U0 ?# l取消按钮的代码非常简单,就是关闭登录窗口: 3 p q: f; g! Q3 G private void btnCancel_Click(object sender, System.EventArgs e) # N; ]! `. i3 g5 e" X { 0 u1 P# j$ n4 v* b) {+ t* P$ `5 C this.Close(); ' P; Z1 u, @$ c1 u; L* E } & |+ j1 p1 U4 f8 T( x& T: J把Login和MainPro软件连同其他相关文件打包成安装程序,将Login以快捷方式放到桌面或开始菜单中供用户使用(当然,快捷方式名称可以随便取了),用户运行Login后,会自动更新软件。 9 @" r: J# N! i7 P& p9 c 本例中所有代码请ftp://qydn.vicp.net/ 下载,文件名为update.rar,解压缩后别忘了在D:\创建一个output文件夹,并将mydatabase.mdb复制到该文件夹中。 5 D- K: Q! H! } 说明:本文只起抛砖引玉的作用,通过该思路进行扩展可以完成许多功能,如通过修改上传/登录程序,不仅可以实现对主程序的更新,而且可以实现对任何要用到的资源文件进行更新,本例中不能实现对登录框本身的更新,我采用的办法是在主程序的Closing事件中更新登录窗口,因为此时登录窗口已经关闭了。在登录窗口中,可以放一个“保存密码”的复选框,如果用户选中该组合框,可以将用户名和密码保存到注册表中,下次登录时直接读入,用户只要点确定按钮即可,免去了每次都选用户名和输密码的烦恼, " |2 Y% U3 Z4 p4 Y在本例中,我们可以看到,数据库的连接、查询等工作是重复性劳动,且三个个项目中用到的数据库、公司名称等是一样的,在实际工作中,我们可以单独新建一个cs文件,不妨取名为MyTools.cs,将一些常用函数和变量(如数据库连接、公司名称等)做成静态的,各具体项目中链接本文件,然后直接使用,我们只需修改MyTools.cs中的相关变量或函数而不必在每个项目中都去改,既方便又不会遗漏,MyTools.cs参考如下: . _% u7 [( [! |; a///<summary> 0 l' ~+ _9 n, e9 D/ a; ]& g# ~ ///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 9 c+ i/ Q) x, y# ]///</summary>

//#define NETWORKVERSION

using System; ; N H! U2 E4 Yusing System.Drawing; ) E0 {2 O/ ^% U6 ~0 ~ using System.Collections; 4 }! T5 \ H- k! d; Tusing System.ComponentModel; 2 x! l2 l. W% b' D ~7 P using System.Windows.Forms; , D& t* M% W9 r3 d# o using System.Drawing.Imaging; 4 ~& P' d& ^7 Pusing System.IO; 2 r8 f8 [8 L, `7 P0 `3 e" |! Z, e using System.Data;

#if NETWORKVERSION 4 `' h7 t- j$ [: b: L* V) Cusing System.Data.SqlClient; ( i+ ^, k. F. J' ~( Q& W+ Z#else : N. C; v" E% F1 ?9 \ using System.Data.OleDb; ! C2 h! I, X- X* s' D1 e) c, x8 _ p#endif ; I/ X$ z/ J s3 N+ e. l" t3 g using System.Reflection; $ A* U5 _" D: ~using Microsoft.Win32;

namespace OA / |8 N* E. q* a5 A$ L{ + Q7 l2 W4 A2 v9 u$ {' a0 Q4 e+ j public class Tool ) O9 {' z3 d u { ( m- f: ^$ ~+ x* X public Tool() ' V: Y/ Z# v4 x0 { { $ m# O0 W0 t0 i/ F0 ~ } 9 H! x( \ \. v k3 k' D /// <summary> # |+ m+ i9 |! p4 N' v) H /// 主程序的文件名 " N @# G- X1 ?+ s /// </summary> z5 w5 Y3 @! w+ }( V. u public const string FileName="OA.exe"; 3 E, e9 V0 m" M ~) N0 ^& I4 I public const string g_TitleName="丽汽集团办公自动化系统"; $ g9 i, A- M0 |% R. H0 y8 W3 a$ F public static string g_UserName; / c/ S8 \5 {3 o3 ] public static void WriteInfo(string p_keyname,string p_keyvalue) 4 @, j2 ?0 e" A. U6 Y1 V9 t' s { 4 K( |* w: \$ ?. C5 P …… & @) M6 e. y. k; b8 n0 ` } 9 Z" J/ C# ~- b+ v //其他类似代码略……

} 1 |* b0 W6 X# r8 u4 c- |9 n% y} ! y: e: ?- A$ ?: v \. J" A( r 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: " J- R* N/ B( J1 N1 X8 L* D在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: , p9 y6 ?0 @) c2 ^4 j( \using OA; : \4 r" v1 r0 l: xprivate void myFun() & B4 q7 Y& t7 z) W4 A) l* t{ # _, f! N! z: ?, e& ^* ustring s=Tool.FileName; " c5 O5 i/ Z' U# E% O3 f/ T; \ } 3 ]& h+ r* R2 f如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 8 K; ^3 k6 l$ F; v1 ^- T) h我们还注意了一个细节: + c' U! ^8 V$ P3 u2 I, o+ s+ g u ///<summary> / j4 Q$ P9 u7 }3 r ///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 * \0 w0 z2 F& e6 L0 K6 ]) L- t ///</summary>

//#define NETWORKVERSION ) n$ y8 n* u7 D( y我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: , s+ f! }6 g `# j* k /// <summary> % Z; P! g( _- @$ a: h, `' W% F /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 ; J6 h2 v5 u+ U! n( @- H4 ?; z1 P( Y /// </summary> 3 M: d$ F% ^8 ^* N" W: T5 F /// <param name="p_Sql">查询用到的SQL语句</param> 6 I, p! Z( R5 V /// <returns>查询到的结果,没有时则返回空""</returns> + N& s7 u+ @6 h0 |) H public static string GetAvalue(string p_Sql) , i3 _( O, e9 ^/ z { # W3 ]7 P; `* i: q# v- Y" T* \( p4 J string strResult=""; 2 K0 Q0 }6 U; {1 q% W+ b! h Tool.OpenConn();

//设计所需要返回的数据集的内容 * y) s8 H5 c3 e* j' ]- [ try 5 R1 s, P; b/ T& Z) I* ^* G$ D { / y) b1 D6 ^: u+ G+ ` // 打开指向数据库连接 + |: C1 ^4 ?/ g9 E2 p4 W% Z/ ~#if NETWORKVERSION //网络版 * K; l* @5 c8 V: O SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; 0 r3 t2 q8 c8 { SqlDataReader aReader = aCommand.ExecuteReader ( ) ; 5 L2 m) |# \% G #else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 2 L' z& l# u" B+ t7 V/ L OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; ' J# W4 O9 z' Y1 m( |+ K OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; 6 Y# x* d6 ~( N#endif ( w7 f+ X& j; s, s8 g# T # m( ]. L, b! h( R" G: M9 ~ // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 2 \4 O) O% T5 W( E if(aReader.Read()) . U3 l$ v) } _ strResult=aReader[0].ToString(); 5 b7 t4 Z, ^: O/ x4 U% I; q aReader.Close () ;

} * X* A, a8 t) l( n9 r- [ catch(Exception ee) ' W- N& D* v$ O1 y { ) D r. g! _1 ~& a1 k. G MessageBox.Show(ee.Message); ' }# W6 d! V1 o) ~* Q } # I& |4 N: ?0 f3 s return strResult; 3 ?# S! `7 T0 H! |: X+ f1 | } 6 U+ D1 \( I4 ]. }- W以上类似的小技巧还很多,注意总结,定会收益多多。

遨海湾-心灵的港湾 www.aosea.com
您需要登录后才可以回帖 登录 | 入住遨海湾

本版积分规则

网站解决方案专享优惠-3折上云

QQ|手机版|小黑屋|遨海湾超级社区

GMT+8, 2025-4-25 01:48

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表