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

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

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

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

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

×

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

9 S5 D3 E/ D% P( _+ y6 A6 U2 F

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 - F' e% b. V" s. [. Q2 p 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 ) V+ ^% R* h& H0 R- V创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: 3 |* S1 A; s$ ?. D# d /// <summary> * _- ?) N: M3 q- ` /// 应用程序的主入口点。 : m4 w0 l" S, j) X# r /// </summary> & x7 P7 t& I0 g [STAThread] ( a7 j0 v7 c( s/ r, [5 U static void Main() # Q, x% S9 Q" R! q+ q { * J, Q) x" L' l Application.Run(new Form1()); / G" [2 R: e3 L. A- n* g: w4 `2 W } 8 h6 k' \% a! [2 q1 ^为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: 6 e4 `" X8 W) c6 k3 I private static string m_UserName,m_Password; 0 S5 E$ ~% m- m8 X4 i7 P /// <summary> - }' K! s: ?4 D /// 应用程序的主入口点。 ( u4 }+ X* Q2 |) o% t( ` /// </summary> 7 h7 q( t ~ F6 z [STAThread] 5 o4 W3 P5 `- g6 `+ M; p static void Main(string[] args) 4 O1 u- ? P: x' o( a3 M) s& n5 { { . l( d9 f0 V2 q2 q2 l9 q+ W0 v if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 2 t0 R3 I+ K6 O; C' | E, \& d { " U9 e W6 M$ ^/ F: [# z) @2 P //记录下用户名和密码,供软件使用 2 `4 H8 E" t; C2 ?& u, M& O* `* ~ m_UserName=args[0]; 2 [! F; u; g- [: k6 j1 \ m_Password=args[1]; 9 f0 @0 }- C1 a; h7 O8 J Application.Run(new Form1()); 4 C& ^: ]- F( j: y } + E% {& v6 `' w" W* H" X- P, u1 { else 8 J5 X' U1 P) s9 t0 y. [' T& i- ^ N { 9 o. `4 g; n$ ^/ I( c; Q X MessageBox.Show("不能从这里启动"); 8 \: U3 ?+ n* W# _2 C- _3 I. A } # ~% `! [ t% E1 Q( _! A; o: x8 e } $ F/ g& w) c* A( U$ ^ 为了显示登录是否正确,Load事件的代码修改为: 0 c3 h4 e J8 n! G9 X private void Form1_Load(object sender, System.EventArgs e) : v# @( B2 d% I1 S# U1 b { / l0 x8 S' S) l8 T) R4 ^5 f$ O* r, a string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); ; E+ \) w9 K: }, s3 o# U2 L. R MessageBox.Show(msg); : P) X+ |' k2 C } $ c6 u" [' E, {& ~# s; P! ~ 这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 & ]9 R% \1 ]3 m" L# I 由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

! A9 u1 } D4 N! J5 `1 } z

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 * w/ y9 S, p3 o$ |+ @8 E7 k* O 在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: ]4 z7 v6 `0 F# _: b操作员表 版本表 7 U. a* Z! p n k字段名 类型 用途 字段名 类型 用途 - e" U' \) Z3 o% K( M( d 序号 长整型 主键 序号 长整型 主键 6 G+ `$ g$ o& {姓名 字符 用户名 版本号 长整型 存放当前版本 2 E* h5 @! h# ~# r3 Z 文件名称 字符 本记录对应的文件名 " W x. ]( P, f密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 : G& X/ ^9 K; v- S 我们手工输入一些用户名和密码,如下:

5 W& a5 {6 T4 B; _

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

8 t# W& ]; P# f% A# j

点“确定”,同样,配置输出路径为D:\output。 . ^$ U; _2 T5 K( l$ Q0 O! G在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

) x5 P( a0 s3 D8 Y) o8 K% A

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 4 A( x9 \; N5 k1 j# K( Y 添加浏览按钮的响应代码如下: / `* q9 }# _0 W6 d private void btnBrow_Click(object sender, System.EventArgs e) * P! z; I9 ^! H+ l/ q5 Q { 6 u) {2 G. E f OpenFileDialog myForm=new OpenFileDialog(); 3 C0 `; f* t- u myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; % H# K& C9 M- K [) U- a if(myForm.ShowDialog()==DialogResult.OK) # i5 Z/ H2 Y7 N { ' T9 Z" s" r s! ^4 w9 h" D this.txtFileName.Text=myForm.FileName; & ^; s7 Y$ D, A, @! m1 ]$ Y } 2 ^& c. ]9 D" |. A( z7 G$ q* n$ Q } - J) K: Z8 l4 g1 U4 R; S 该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 2 `- B$ W, \6 y; c添加取消按钮响应代码,目的是关闭窗口: & a( Q/ y* h. Q private void btnCancel_Click(object sender, System.EventArgs e) % H: ]7 `' q1 u3 A2 |+ q6 x { & U6 @$ }& P, W: A" t' U# ] this.Close(); 8 W# B1 @# H1 P) A7 a } 0 g6 B' T1 v0 `% ~添加两个引用: 7 h. x" |3 ]) y2 v- A. O using System.Data.OleDb; / D( P' n- Q2 R% p& j2 M D0 e using System.IO; " ]( S, h5 N. k9 t% b& b再添加两个变量: 2 Y9 J- j- q) g- i+ T private DataSet m_DataSet=new DataSet(); ( P3 Q% F0 m( h! u7 R! X0 \ private string m_TableName="版本"; 7 {" K9 _* a4 q+ L* Q+ B 下面的函数去掉文件名中的路径: 6 H2 j7 t; H8 Q4 @0 c @$ G /// <summary> , T/ c3 @9 Z8 d. O; H /// 从一个含有路径的文件名中分离出文件名 3 H2 a' J9 X( n# r /// </summary> ( ?! _- ~4 H0 \ /// <param name="p_Path">包含路径的文件名</param> 1 M) ?8 q9 I& u; d. a5 u /// <returns>去掉路径的文件名</returns> ' D5 P0 \) I- ~4 n8 W; d6 a5 [ private string GetFileNameFromPath(string p_Path) ( P. k6 O. y# [# w/ H' h { ) l9 k5 d6 A( ?/ H' { string strResult=""; , c2 o2 ]$ B6 H0 t int nStart=p_Path.LastIndexOf("\\"); % r- V2 q- g( s. K) Q6 a, V# L& B( R if(nStart>0) 4 r1 `; m0 V( a! u( M4 d3 n { 2 [6 x5 X% a7 `$ J2 c/ a strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); 5 W9 Q) L+ c( f5 Q7 w0 t } 1 u( S3 ]7 ~' Y2 U) S: ] return strResult; ; I5 o2 G% [" h, k: X) W. P } % J' E {$ ^7 F4 S# w 添加确定按钮响应代码(含注释): + x% v/ f8 I n2 H7 vprivate void btnOK_Click(object sender, System.EventArgs e) & ?5 l, t! ~0 o0 G( | { / H. M/ F5 c' q$ a' A //检查版本号是否合法 4 y7 z) A$ ^1 I) ~. }% h try . S) J8 e, j; \ b- w1 Q& ^6 Q { ; A- D* p7 T) M& O |9 ~6 s r Decimal.Parse(this.txtVersion.Text); + D/ @# m& m' U, o1 k, x } 6 J8 u+ A/ q- Y3 L5 G, {4 q catch # J" Q8 Y! `) p7 m" V/ A { 6 }7 {5 k: i6 b9 Y9 C5 c MessageBox.Show("无效的版本号!"); 1 A. S" i9 p) o% j9 [9 Z# n3 H this.txtVersion.Focus(); - V* W7 R% N7 U- P, u this.txtVersion.SelectAll(); . F- r& ?1 w G. R/ \% i" x return; ! I0 t3 m- R9 l$ ~1 R) T) a }

$ v6 V" s# T2 i+ N

if(this.txtFileName.Text.Trim().Length>0) # }0 D5 k/ N: b4 t { 1 e" c+ v* p4 x) C$ K6 Z- I //检查文件是否存在 * Q1 G \- F/ U+ S+ u. G: C if(!File.Exists(this.txtFileName.Text.Trim())) / `+ m2 |1 ?9 d* I7 Z* ?7 a { n8 I/ n m) k a1 r5 x* W( g6 { MessageBox.Show("文件不存在!"); 9 j* H! c) g) W) X4 | return; + S# c% ?8 J" b, X }

7 w( Z, w2 @( @) v

//连接数据库 ' i/ p, L7 W- J5 t8 ~ string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ - Q1 _. K4 U6 w% b: p- P, U& m Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 8 |- C0 U9 a, L OleDbConnection myConnect=new OleDbConnection(strConnection); ) D' G8 J; D/ M- [# f4 w OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); ) I! D% R6 J9 f. `. c OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); , l7 ]. r3 y5 d" l myDataAdapter.SelectCommand=myCommand; 6 A% A% c! K8 L8 F: b OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); ) g& \+ ?- H0 }6 ~, w0 g8 ?. q myConnect.Open();

q8 m! I3 s E( c6 l5 Z! d. O* j

//获取已有的数据 9 w1 B" r0 { a$ a, U9 N8 C m_DataSet=new DataSet(); ' T; E4 Z& U" F( \' w try , I1 O; _6 ]9 R; V( M* B9 G6 H; ?$ b { ' M3 `& t- I- |, P$ P/ e7 R myDataAdapter.Fill(m_DataSet,this.m_TableName); 4 U* m0 [0 [9 s- Z% _ //如果是首次上传,则增加一条记录 ( w6 l7 F/ }3 g4 C; L' L8 } if(m_DataSet.Tables[m_TableName].Rows.Count==0) 8 }9 Z: u* a+ U& Q& q4 I3 Y+ ~ { % M' T5 ^) A: N0 M2 o& P DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); " R! v5 L1 A( O, H0 | newrow["序号"]="1"; + u" ]7 E/ B( | {( E' i m_DataSet.Tables[m_TableName].Rows.Add(newrow); 8 D+ r( Q3 ^( [. g! a7 r } 5 F) H4 v; }$ ^" s$ T: O : t( w7 S, C. q2 I DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; ( ~+ ~7 Q$ d% Y$ l' M- @ //填入去掉路径的文件名称 % V5 c- _7 F+ j row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); : K% ^& d! m+ _: M$ n* u% d! } //填入版本号 ' W; q# j3 @2 `+ \ row["版本号"]=this.txtVersion.Text.Trim();

: `( p) J- n0 k$ g0 ]

//将实际文件存入记录中 2 J. |$ y5 ]: o, K/ F7 G FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); 6 T; X7 K# ^1 a; E: b6 E byte [] myData = new Byte [fs.Length ]; ( |1 H1 j3 ?1 R1 m( p fs.Position = 0; j2 |( V9 d+ d fs.Read (myData,0,Convert.ToInt32 (fs.Length )); 0 A3 D1 j4 l- s) v row["文件内容"] = myData; ! v$ X* Y! j, p! I& ^4 x& Q fs.Close();//关闭文件

. V( U6 S6 s7 \0 D0 Q5 W

//更新数据库 + k0 W7 \ }% k& }/ q myDataAdapter.Update(this.m_DataSet,this.m_TableName); ( P1 m& ^) h2 M/ l9 E myConnect.Close(); : z+ o% ]3 D+ p# {, t5 b MessageBox.Show("文件更新成功!"); - m* X. J+ G/ F! y2 w } 3 E% R1 v- V( ^$ W! V+ t catch(Exception ee) . V, x1 w( Z7 {7 J9 t- n7 r; b& l: _ { * g% I, t: a5 N7 L. { MessageBox.Show(ee.Message); # s. n1 ]/ L) R" y. p6 d# e } & Z, H7 f2 M( N, z0 H! c) _: ~' I ; x' `$ L" f$ E. q, i, b! O& n } ; C* r% I8 u" W ]9 `/ Q; s else 2 x5 v, q) s0 v: P" c { : X% _4 s; t4 I. s4 A0 j' | MessageBox.Show("请输入文件名"); $ v5 T# l6 J+ D0 Q/ s8 i4 g8 J } 1 H1 e3 D A/ F" a' w } 2 A( p3 _* h! F/ y# X至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 / U- X1 \/ N: O% k5 U/ _' B最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 , d9 |7 z" p& H: H; y; e. `4 q- j添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

- q/ Z) I" }% |8 z; }" J$ M

切换到代码窗口,添加引用: * E9 m% l. b" Z; y8 k2 Uusing System.Data.OleDb; 4 ^9 Z. e0 Y1 W7 \using System.Threading; ) s0 f& q; t/ P; P: m; x% @ E using System.IO; & }9 I& c6 }( t* W0 u( u1 R using Microsoft.Win32;

3 Y" m0 u( v1 U

再添加如下变量: $ x; n. m) Z( e+ V. p9 v3 r /// <summary> 1 G" K/ F; ]1 O% r8 L( H4 j /// 存放操作员及密码的DataSet & z! ]1 Y: D; h; e /// </summary> & }3 E, [( ?) v1 [, ~+ ? private DataSet m_DataSet; ! L# n; n3 p+ S! e /// <summary> ' Z5 t2 N, i1 e X7 ` /// 本功能用到的数据库表 + H6 Z2 [3 s0 O5 H& R4 x) [ /// </summary> `$ C; a# Z: L- t+ _* I" M private string m_TableName="操作员"; - ~( s+ \6 Q* V; `6 u6 O6 n, d private DataTable m_Table; 9 f6 E ]7 N: T" ]5 H为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: / G6 N' k2 b& T /// <summary> " H; ~; G& g, N4 h /// 定义本软件在注册表中software下的公司名和软件名称 ) z$ E4 c c6 g6 a3 k0 Q4 Y* R /// </summary> ( j0 v1 J9 B7 W& `% H5 z private string m_companyname="lqjt",m_softwarename="autologin"; % d3 J5 V2 L- d; s# l: p: }8 F /// <summary> - W, z; z& e. ^+ L4 P6 M /// 从注册表中读信息; 5 E2 r* b6 d2 Q' M+ V /// </summary> , h* ]% W% E! C6 d; N /// <param name="p_KeyName">要读取的键值</param> 1 M0 `' | {. n# @& y9 @+ e /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> 1 f+ z5 J8 n0 u* _ private string ReadInfo(string p_KeyName) 8 X/ u1 h, S2 {0 i { & a' c: B; Q2 \" j# } A; t RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); 7 B0 [0 ~+ i9 z* J3 \) S RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); , u8 e. t$ v* O d string strvalue=""; 8 c( b# I T- U7 J & q' a: X" E V$ l0 p# m3 Y if(CompanyKey==null) " F" G, p# V9 f5 G" o return ""; ' a: f5 Q' b0 K4 I+ [) \/ @ RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 & |& X# Q. L8 ~. I8 A. J if(SoftwareNameKey==null) + i/ }( B, i9 n7 R+ c return "";

+ y5 w+ N, }4 I# a8 P; u: v

try 3 j5 p: M6 z. _ { E* y. k4 @* Q) L- P3 |6 W strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); 2 z# M- Y5 ]4 |5 N. {9 t( O } ' t$ T: z5 I2 y/ {5 K9 T7 X% m catch 2 j1 x1 V- u1 P1 Z3 O% F {}

; |" p2 |9 L7 F$ v: q

if(strvalue==null) / m0 C. `4 I" M. y b4 q9 P( E strvalue=""; . b2 }# \& t: d+ z2 O- R return strvalue; & d+ [: R7 F( d9 s8 B, ]1 ^. m' D/ H } / Q. P, {, h5 W) ?; M$ Q" d /// <summary> ( Z8 ~+ g, Z m8 l' W5 s! E /// 将信息写入注册表 ! w9 b' y- w4 S. b# N- A `8 { /// </summary> # l; Q$ u, d w1 ] /// <param name="p_keyname">键名</param> ! \- |: P* k5 g0 O. i: x /// <param name="p_keyvalue">键值</param> : m! H( q% W7 n3 i private void WriteInfo(string p_keyname,string p_keyvalue) ( l5 _' a! H& b6 T4 q0 H# o' P { 9 D( K$ w3 R! _6 h! v4 p RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); / z; v6 F- L8 u3 \: o. f7 B3 X2 h RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); 0 Y. n. a2 i7 J( n/ }! A RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); % L7 U( P. G+ J2 W //写入相应信息 * k" C- i: D$ L SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); : U+ o4 x3 J8 k8 G4 V6 f9 |& u7 s2 p } ( M7 Y" S* W# g6 k6 v 再写一个函数,用户来获取用户名/密码和更新主程序版本: * g1 Y) n8 x% n/// <summary> / ^9 E! f! U% b5 M7 E! B /// 获取操作员情况,同时更新主程序版本 % D- A2 _4 g7 o5 }4 ~$ r, i5 F4 n /// </summary> & {4 T1 i$ D$ S6 r private void GetInfo() $ K" i4 ]% g1 {* } l8 B/ X { 7 `" K. i2 v0 P! s" W1 A this.m_DataSet=new DataSet(); 2 q7 I ?/ S- |# r* X this.combUsers.Items.Clear(); - R- D& W. j7 ^ string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

9 e2 i' S" k' R+ N- p7 r4 w# u

//连接数据库 3 ^: {( t& q7 y2 d8 g# a+ C8 q* I' n string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ ! I7 |. O- Q& |5 ~ Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; " E% p8 R+ v7 k5 } OleDbConnection myConnect=new OleDbConnection(strConnection); 2 f2 z& ^ {. q( K! p OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); 8 o- I* I1 s' P7 \! |' `$ E6 _2 I OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); $ o! y, _$ Y$ [6 C# s; r* D7 F8 u myDataAdapter.SelectCommand=myCommand; ; R9 q; x7 K; i; o4 d& L) `6 n try 4 y4 c8 b; M" J6 C5 `' J { 6 P0 [' [- u1 t7 } myConnect.Open();

4 D. E+ a$ X* W9 n6 C

//获取操作员信息 ( U, _* i4 z9 P9 h. L myDataAdapter.Fill(this.m_DataSet,this.m_TableName); 3 p5 j6 \& J; ]1 X //将查询到的用户名填充到组合框供用户选择 6 D9 a! k0 \7 y! Q% p% l% Q this.m_Table=this.m_DataSet.Tables[this.m_TableName]; : S5 w4 P3 X% ^2 I, p' e/ g foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) 5 ]& r. v+ i3 j' A. j. @ { 4 C6 O9 |. T" |+ a% ~! x v3 e7 G this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); & h- w$ W1 q' Z) d9 i* z }

& Z& D5 C% t# k+ O

//检查是否有新的版本 2 Y& ?1 _; w0 R/ t) L3 f' O9 p DataSet dataset=new DataSet(); ; t0 d, N d# s7 W6 k string tablename="tablename"; 8 D. ^/ [# i* i5 F0 g, a& a //为减少数据传送时间,不获取文件内容 5 w, R" U1 M8 E6 @( ` strSql="select 文件名称,版本号 from 版本"; 0 u1 T8 a) Q3 d" |2 C, Z9 q8 b myCommand=new OleDbCommand(strSql,myConnect); 4 `% z6 x H! t( G M9 u myDataAdapter=new OleDbDataAdapter(); , _. I* h2 p+ i- P7 D myDataAdapter.SelectCommand=myCommand; 4 y2 p k3 ~2 y6 n* l' E. K6 |& L myDataAdapter.Fill(dataset,tablename); 5 S1 L! |. \ K, @6 e0 C, r | if(dataset.Tables[tablename].Rows.Count==1)//有文件 : f6 b2 O% {) y. d, B4 u9 @% e- S { 5 K% L: M# c# D2 z* U, @; n6 b6 j string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); : G, h3 P7 d1 ~ string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); # p+ O, R0 E+ {7 H# n //读入本机主程序的版本号 9 U! J5 F6 x( O3 S. Z string oldversion=this.ReadInfo(filename); T2 `: Z- Y) e6 w$ N l- Q C if(oldversion.Length==0)//不存在 2 t% `6 E8 v8 `- I3 ^ w+ [ oldversion="0"; - ?% }" s7 {5 `; e @) `, V/ F if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 - \" X' l3 X' t { 8 g4 n m- b. S //取回文件内容 ' _' n8 J5 ~& u$ `9 @; T dataset=new DataSet(); . O; f1 D- s' o3 m9 ]9 x& g strSql="select * from 版本"; 7 I K: r# U' M* x myCommand=new OleDbCommand(strSql,myConnect); " q# N' Y! @( @* P7 q$ ~, ` myDataAdapter=new OleDbDataAdapter(); 8 N, f) O; W+ T. R# E myDataAdapter.SelectCommand=myCommand; / b6 ]6 w* c9 q3 d& b4 Q myDataAdapter.Fill(dataset,tablename); & h4 g9 ?! \5 p1 O) d+ ?; \5 ~- K //将文件下载到本地 9 q: z" R3 e# y. i# x v* ~ s DataRow row=dataset.Tables[tablename].Rows[0]; $ ?. E9 k" f, Z) J# a; ?) x4 M if(row["文件内容"]!=DBNull.value) . M$ W; s: K# V4 S {

* W0 [( \7 o" q: j; x

Byte[] byteBLOBData = new Byte[0]; ( h. H# e6 ] F! e byteBLOBData = (Byte[])row["文件内容"]; # k. [: y( T( B" ~$ Q4 m try 0 e- d) n# D2 n R! `' D) ]7 W4 ` { 6 O- _& F( Y3 h: w FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); % `+ Z4 }* G) N fs.Write(byteBLOBData,0,byteBLOBData.Length); " j9 F! F7 ]9 L7 W: E fs.Close(); ; O$ o2 ~# N$ a5 G; y //写入当前版本号,供下次使用 ( j9 P! v3 X8 s- q this.WriteInfo(filename,version); * I( z7 Z. n) v } / G8 }6 k, p/ p' Y catch(Exception ee) 8 Z: |8 ?- `" _( g7 O" s$ n0 x { : x' \ K L: `3 L3 d% g MessageBox.Show(ee.Message); : @% Z, A+ h) ^# C6 [ } - A) c( L( l+ B* l }

- v) m2 Z' R. c2 {3 I

}//有新版本 4 A( Q% ^( H$ R }//有文件

. V7 y4 n) x5 b: p% H$ d$ i* X

//关闭连接 6 k( K2 \8 ?( Z5 |4 P6 D myConnect.Close(); 8 D& d5 x$ M6 i' q* e7 Q; ^8 J } 1 z5 m6 Y7 v: u$ I catch(Exception ee) % w( m# ?/ \, x$ U- b T$ s3 B { . }. H- D' l" ^8 b4 _ MessageBox.Show(ee.Message); ( W$ L& K2 n7 d return; $ c% X& x& ^; L5 ]* G } % z! S$ ^0 u( i/ G //允许登录 + h) K! _/ }- j this.btnOK.Enabled=true; + i$ ]1 H. j" ]+ R, h; X* N } 8 O& S5 m& F1 M" ~

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: c! `' L/ c. n1 E3 m private void Form1_Load(object sender, System.EventArgs e) + b+ \) E3 C0 I; |7 o" L$ q { 7 U+ u# v1 d0 y+ I, u z# } //为加快显示速度,将数据库连接等放到另外一个线程中去 & w9 x+ M `2 K. O& F; K Thread thread=new Thread(new ThreadStart(GetInfo)); ( a: c4 x+ Y* [# `4 m thread.Start(); # Y. b) Q! r5 I |0 A } * o3 b. W# Z# K: o有了上述准备,我们来编写确定按钮的响应代码如下: 0 _) |4 ~, T# N7 H" C private void btnOK_Click(object sender, System.EventArgs e) 0 K7 t" b$ J) V+ G4 b { ( Q6 t# L2 n& Y) x3 I+ l //根据组合框的选择,得到当前用户在DataSet中具体物理位置 / I, o! g/ D- H+ c4 @ if(this.combUsers.SelectedIndex<0)//没有选择 5 Q8 i' e& y) a8 e2 o. v7 V6 b return; % i6 ~1 p3 ^3 h+ ] DataRow rowNow=null; % a' x0 u. l# T+ K% s% I' U foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) d9 O( r9 r! w7 N# G3 H { ?3 D# O$ c* H e% L9 R1 k if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) 6 c9 H* N$ g4 t! f" w6 ]4 o1 \ { 0 G& a: `+ p4 p$ Y# N- [ rowNow=row; 0 i" Q. J' {$ C7 j" Z break; C1 v1 X. C; ]; J D } 8 L8 B" }5 X- W- S9 v9 U/ E } ! z* f3 W9 O' y4 h4 } if(rowNow==null) 8 a: h- W( M0 d, ^7 n5 a return;

//获取当前正确密码 : M3 b9 _5 ?2 d/ }% E# | string strPassword=rowNow["密码"].ToString().Trim(); ; y$ Z; s( W, u* Q5 Y! \- K W this.txtPassword.Text=this.txtPassword.Text.Trim(); 7 q( z* S* u( f" ^ if(this.txtPassword.Text==strPassword)//密码正确 ! E- {( _) J: Y# ~ {

//主程序名称 " Y1 U, Y" C& \6 c string filename=Application.StartupPath+"\\"+"MainPro.exe"; 6 K" l9 q: Q1 E& }% o+ ]" d9 L //参数名称 6 \3 n3 z; b0 L( g* B string arg=this.combUsers.Text+" "+this.txtPassword.Text; ( @) }+ m( Y$ R( i& i' k //运行主程序 $ Z9 ?: u5 W/ e! p& W System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

//关闭登录框 + h; Z5 X' u; U$ J) \1 G% G" Z% w this.Close(); 0 s6 z# n' L9 t! d+ l- R: } } - w0 u8 R) O5 {4 ^2 o else 6 S! H" S: l) ~' j+ v9 a { 6 |/ [2 [5 t: @- X5 u2 f$ A& U MessageBox.Show(" 密码错误!如果你确信密码输入正确,\n可以试着检查一下大写字母键是否按下去了。", , K e+ e+ X$ K, ~, C( S8 l8 e2 o9 \ "警告",MessageBoxButtons.OK,MessageBoxIcon.Warning); 1 C. S" t6 o! S this.txtPassword.Focus(); : f8 [3 ?1 Y6 j0 F) { this.txtPassword.SelectAll(); " @1 Z8 h% d7 H8 x% p1 K } 7 e* ]* K6 e* c# s } 9 ~& \: ?( P! G" h7 ]' V 取消按钮的代码非常简单,就是关闭登录窗口: 4 L7 D3 S( f* U& _6 ? w private void btnCancel_Click(object sender, System.EventArgs e) : ]4 {4 U$ l# a2 B6 } { : i$ Y" d, X) N; [/ {8 j/ L this.Close(); ) y- U- [$ q& u8 F } % z7 |6 L, v/ M( W, d9 o 把Login和MainPro软件连同其他相关文件打包成安装程序,将Login以快捷方式放到桌面或开始菜单中供用户使用(当然,快捷方式名称可以随便取了),用户运行Login后,会自动更新软件。 / N- s7 ?. m5 I4 W! l 本例中所有代码请ftp://qydn.vicp.net/ 下载,文件名为update.rar,解压缩后别忘了在D:\创建一个output文件夹,并将mydatabase.mdb复制到该文件夹中。 4 O; Y+ H4 k0 S3 g 说明:本文只起抛砖引玉的作用,通过该思路进行扩展可以完成许多功能,如通过修改上传/登录程序,不仅可以实现对主程序的更新,而且可以实现对任何要用到的资源文件进行更新,本例中不能实现对登录框本身的更新,我采用的办法是在主程序的Closing事件中更新登录窗口,因为此时登录窗口已经关闭了。在登录窗口中,可以放一个“保存密码”的复选框,如果用户选中该组合框,可以将用户名和密码保存到注册表中,下次登录时直接读入,用户只要点确定按钮即可,免去了每次都选用户名和输密码的烦恼, $ r6 G& Z0 L( T) @在本例中,我们可以看到,数据库的连接、查询等工作是重复性劳动,且三个个项目中用到的数据库、公司名称等是一样的,在实际工作中,我们可以单独新建一个cs文件,不妨取名为MyTools.cs,将一些常用函数和变量(如数据库连接、公司名称等)做成静态的,各具体项目中链接本文件,然后直接使用,我们只需修改MyTools.cs中的相关变量或函数而不必在每个项目中都去改,既方便又不会遗漏,MyTools.cs参考如下: V5 G- o& [1 x; E; a! d" }' \ ///<summary> # r* }# D- Z( V% h, ]$ m) W! o///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 - v; N. T1 G/ Y# b$ d, h( n: C4 T///</summary>

//#define NETWORKVERSION

using System; : e D4 P% |0 ]using System.Drawing; . W3 g9 S6 p& m using System.Collections; / `8 L9 K+ x. d i, q* [ using System.ComponentModel; ) _; u4 X' l1 t( a8 y+ ?% g" husing System.Windows.Forms; * J5 B; p5 j m5 Z* gusing System.Drawing.Imaging; , F) {0 f. k+ Tusing System.IO; 8 `- f$ V V6 U' `+ ? using System.Data;

#if NETWORKVERSION ; z+ g- `* g0 h! p) Q( c* C3 B' p$ d0 P using System.Data.SqlClient; E# M" g! m' k#else 2 V8 d- I6 T. y& u using System.Data.OleDb; ! ~/ N& A& h/ S- s3 G j! ~ #endif " w8 d1 `, C: F+ |: i4 ousing System.Reflection; 8 H. ~! W: Q) T( u+ l using Microsoft.Win32;

namespace OA % f9 l$ R o) M) s{ % ~0 W- C0 H; p public class Tool 5 T) @6 c4 c: w k { 9 `5 I' z* M( F6 V) F public Tool() $ Y: G. ?, d, l* n+ y4 U2 L. l { $ O8 M( h7 @% P- W } 0 J o9 d; @! Z2 q& b /// <summary> 6 p& l8 p1 r9 ~; |7 c; c /// 主程序的文件名 0 q$ Y8 |. A5 g$ ]( ^) R /// </summary> $ B1 B! C. R/ a; K public const string FileName="OA.exe"; , m+ m+ [$ z, c7 S2 Q# [ public const string g_TitleName="丽汽集团办公自动化系统"; T% V$ Z2 k) n H2 K4 K) o8 S, i# a public static string g_UserName; , o& T3 v5 V: n( Q+ e public static void WriteInfo(string p_keyname,string p_keyvalue) 2 ?2 D& @8 ?+ e" E7 W2 d- @ h% o' M- t { # S# k. x1 Z1 |* I …… % t! @0 U% x( X* d7 ^) i } 9 F8 O1 n+ V% y( l& `; ]//其他类似代码略……

} : e9 ^) W$ a8 d d6 y' v } ' B3 V( ^! }3 f- R8 D. |+ a' ^ 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: - F& E8 f" S# s# ~. h$ T. A% i" y 在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: - G8 G$ u* l5 \! J |+ y8 d ~7 O* xusing OA; ' h1 v8 K. K3 k6 k4 o# }private void myFun() - X; U3 B3 K, @' v2 V; L! v& W1 |{ + r% W/ S/ i& _ string s=Tool.FileName; : Q% ~3 N1 i) S# u% h, w7 l } 2 x1 M; Q s/ F2 {+ i3 D! ~如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 8 U0 ^2 j! ^( |& }3 _/ Y" V W我们还注意了一个细节: 4 {8 K0 Z$ b4 N9 \, q, A+ U, t ///<summary> , ~: L8 D0 P' y* ^* g h ///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 ( Q& z) ^' d- P' m* r/ z ///</summary>

//#define NETWORKVERSION 6 U; h8 @( m4 v6 ~, O0 g+ ~我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: ; t+ y/ `0 f6 j /// <summary> $ @, a: m4 u6 g: e, b6 n! [ /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 8 P" j% q" I# S( n( ~! P /// </summary> " ?" Z$ _2 k% b' a5 Q. y /// <param name="p_Sql">查询用到的SQL语句</param> # n" l# K% W; q0 ~* P3 s0 c ^ /// <returns>查询到的结果,没有时则返回空""</returns> / S2 a6 s1 D9 x0 |" j# b& ^ public static string GetAvalue(string p_Sql) , }+ _0 l# j! f. F# Z { + X2 r5 \3 _! Q4 q string strResult=""; - P; Q: I; v) @9 g Tool.OpenConn();

//设计所需要返回的数据集的内容 & A4 D$ }: W( E5 ]$ y* o try : `$ j8 I4 { f+ \ { 9 N2 Y. R, y' x1 k/ Z5 w. \3 u // 打开指向数据库连接 9 r8 R' {( ^2 I #if NETWORKVERSION //网络版 8 x- L! w$ L* h6 A SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; ! a j$ x6 ]( A0 G( @, f1 I SqlDataReader aReader = aCommand.ExecuteReader ( ) ; * R# w( e* w# v5 x #else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 % H$ P- C4 s* b, {. S OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; * ~- Z7 r; S* t/ W! e OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; 3 D: E ]. p2 {) P: r: P9 | #endif ! @' k3 i- J) q . _4 B h" a- b( h& g6 N# h, M // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 : v# n5 l% X3 u R& s* | if(aReader.Read()) $ a: W+ s5 _, N) t# [6 ~ strResult=aReader[0].ToString(); & q# G4 c& b4 S aReader.Close () ;

} 0 K( [0 u* o3 I! m3 U catch(Exception ee) # `4 i0 J( q+ R H6 j% X$ u { ' b t/ m& G/ I+ N" z MessageBox.Show(ee.Message); * F M5 \- h/ l1 }) P5 Q, a4 p } 3 e7 E% v& F+ i( ? return strResult; - V8 b7 ?' U7 S } + i- |7 ?6 O, S5 D( W+ g& x( e* \以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2024-11-22 07:39

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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