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

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

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

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

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

×

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

# G1 Q* V9 [/ S* ^' ^, h5 K6 Z

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 9 Z" z- l7 o2 ]) S/ s! T0 M J 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 5 n0 p5 a: k1 l7 I 创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: / g. X4 t% g3 R; U ^6 ^ H. X. Q. R /// <summary> 1 Q7 _4 C1 U u* [7 l3 _1 l /// 应用程序的主入口点。 ; K/ }1 s5 ]% v7 x+ w /// </summary> 5 f& a' m& _' R# b c) E [ [STAThread] 0 _8 Z; ^9 {; S7 r1 q6 z M static void Main() 2 n" S1 D1 Z8 `- n, y( [4 p# p& s { l P( G4 l% O7 O, { Application.Run(new Form1()); / r, Z6 J3 i6 X3 u9 | j } " D# Y/ I0 O) r9 N' f5 i为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: 8 L7 x1 V* ^& G- N4 X2 C* d8 s private static string m_UserName,m_Password; 5 W8 T! n; f% u: Z; f1 O /// <summary> + B( S* J4 p6 R7 q /// 应用程序的主入口点。 / r( R. Y& t8 ]& J /// </summary> " v9 K+ u8 ]0 T) a. C5 O [STAThread] ( S+ T* ]) g9 y% { static void Main(string[] args) # `/ E5 B7 y) M { 6 Z/ `: e4 F) T6 n/ b& @ if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 / f7 q' F* e# V- h { % P! ]: X$ ?3 P' p) E //记录下用户名和密码,供软件使用 5 B- P, V* O B5 t m_UserName=args[0]; 1 |- |8 e1 X3 ]. `7 F m_Password=args[1]; + ~, r9 N, B' |, X. Z9 F7 l Application.Run(new Form1()); . a+ }! W/ q( Q- h( j& h } * o- ]- P3 G$ @* F2 I else ; A% X) ~5 w2 `' G; s8 T { / ^ C" A1 j8 | MessageBox.Show("不能从这里启动"); - N5 h! H ^; { } - g! Q g) v. x1 D! N7 s; j } o' C' ^4 E$ R0 ^9 m h 为了显示登录是否正确,Load事件的代码修改为: , @8 y0 Q& @6 y$ x private void Form1_Load(object sender, System.EventArgs e) 1 }1 A* ?' T* N1 j1 ?. G { - {+ u. z* ~+ \4 N2 Q5 L9 C string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); , J; L3 e. a2 o MessageBox.Show(msg); 0 d6 N- S+ G5 k) J% j2 R: h- c" N } # b% G; y: |4 \# H这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 4 j; C6 h4 Z( [" A( y: R 由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

4 D+ X) R2 G0 Q- N3 w: f

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 1 i$ b2 p8 ]& m2 R9 n1 H 在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: 7 N, L. \* |, b" v; f2 S, k6 a操作员表 版本表 5 l$ \5 |; s" o 字段名 类型 用途 字段名 类型 用途 - H( m( C& H; ^+ | O6 b 序号 长整型 主键 序号 长整型 主键 ) b$ H# x% d [, R `/ H 姓名 字符 用户名 版本号 长整型 存放当前版本 ( m1 k: H, j6 k4 T 文件名称 字符 本记录对应的文件名 0 ?; r0 U. \0 E 密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 ) _) U8 Y$ j1 P, l6 w8 F: x我们手工输入一些用户名和密码,如下:

2 L4 o; X( c9 }

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

9 u- S! H( K7 R. m0 z

点“确定”,同样,配置输出路径为D:\output。 % A" W: f* k3 _5 I0 Z 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

5 J8 i" E7 t4 A3 k; a' a# R

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 ( ?9 I9 {0 q, T) W 添加浏览按钮的响应代码如下: ! o' k" S- `" m( y private void btnBrow_Click(object sender, System.EventArgs e) |- J4 l6 C" G. j" D$ i' v { 3 w) h( r5 \ ]5 }7 L OpenFileDialog myForm=new OpenFileDialog(); : n% K0 R6 V. [% G- {1 | myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; ' Z( K' p0 V7 y3 H) l2 i if(myForm.ShowDialog()==DialogResult.OK) . m7 J$ v6 }5 |. r* i { 6 T5 n N8 A$ y' | this.txtFileName.Text=myForm.FileName; h* U4 w: f/ w+ v } 7 E& Y0 b8 a7 u5 w# }# x- S } ; p& q) L9 I( p该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 , f2 C8 t) _, R, x$ c, S9 M 添加取消按钮响应代码,目的是关闭窗口: 4 A6 p# m x, l. c private void btnCancel_Click(object sender, System.EventArgs e) 6 _# H. W# h) x) v' w0 |' }8 v. A { . X2 l. X+ r8 P. g! r this.Close(); M( w0 s& u& j9 R } 7 l b4 z, U- M) i6 q7 e, ~添加两个引用: : c6 n3 _; S. R( w using System.Data.OleDb; 5 O( i$ e* c" ^' L4 F using System.IO; 2 Q1 `( ]& ~: \. a* P+ _1 u 再添加两个变量: 0 n: t/ q, [9 X" L" u8 Z private DataSet m_DataSet=new DataSet(); 1 y! Y0 q9 d3 j! X: ^0 s$ Z private string m_TableName="版本"; 1 j& v3 k& [0 N7 A, u/ P( R& D 下面的函数去掉文件名中的路径: * f6 ]; b ?# K* | /// <summary> 6 a# n) o/ T' ?% K8 [ /// 从一个含有路径的文件名中分离出文件名 , V: o2 S! T, y0 T- n- E /// </summary> 2 A4 E+ c' g! p, P /// <param name="p_Path">包含路径的文件名</param> ' u" S$ }3 B$ ` /// <returns>去掉路径的文件名</returns> ! [; ^3 P* H9 V4 [3 _. ^ private string GetFileNameFromPath(string p_Path) 8 h! @% p& W( o9 q0 z3 i* h { " q5 C" @" T) e2 D$ ]9 G string strResult=""; 9 `4 P3 T O3 ~/ b/ n# J, |: r, x$ j% V int nStart=p_Path.LastIndexOf("\\"); " x# G' J( [) r if(nStart>0) " x' F% p0 K, m+ A { ; p! l' C3 h" r& B( e; `* i strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); 1 T, W C2 [- Z q* ~% K' r$ H' z } 5 W3 `9 t/ t X+ W return strResult; - r) [; H+ J9 h$ R/ L3 c7 I } 1 K0 w7 C8 I' q' g. P9 O3 u添加确定按钮响应代码(含注释): ) Z" R* C/ E0 Q4 @8 g1 T/ [private void btnOK_Click(object sender, System.EventArgs e) " F3 O5 x4 J- H& ^& M5 L& b& j" R { 3 Z2 |. S$ p7 q: g //检查版本号是否合法 5 u6 r4 _, y% X# J; Z try ' J2 \1 H7 [; C: I* y { ' s0 ]' X: d& s; e5 @ Decimal.Parse(this.txtVersion.Text); ! Y8 v! K, U* E; }& x } % |1 g( {- Z% d4 F( V catch / x5 c/ @' |- l! q/ { { 2 ?; U+ l3 c8 H: z$ y$ [( S MessageBox.Show("无效的版本号!"); 4 N4 a. \* p9 y: E$ e6 J% m this.txtVersion.Focus(); 3 P% }+ [: b( B: E this.txtVersion.SelectAll(); 9 X0 ^ A d1 ~7 j8 m return; # _4 u/ o* V% B: X! D }

& B* @! x$ ]( B# m2 m5 g( [6 N

if(this.txtFileName.Text.Trim().Length>0) 3 o0 j( B& ~6 k. Z' x$ g6 x { 8 |9 P5 V/ b0 }) e- A' V$ x7 w //检查文件是否存在 ( ~4 y5 a, ~& D7 D4 N if(!File.Exists(this.txtFileName.Text.Trim())) 8 }8 U* `; f) r( y& @: Q { 9 r6 m2 ]( C: p( R MessageBox.Show("文件不存在!"); 3 C' Q, V) G! q- k. l) l5 ^8 X" j1 u return; ; P/ Y0 c& U7 c' `# E3 y4 P5 ] }

" r6 A: R5 T0 i! H) E

//连接数据库 $ x/ K* N1 c" O7 x& f7 x string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ , }/ a+ i' Q* @, y* ]- R Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 1 U: W5 k$ @) N# `3 k3 k OleDbConnection myConnect=new OleDbConnection(strConnection); - g% {: o% b: \6 \ OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); ! h' s: s. Q& y3 g9 G! W6 ~! N OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 0 p% h* s, }! o+ c7 o) p myDataAdapter.SelectCommand=myCommand; 3 D& P* N8 N. \4 f OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); 6 c- l% l( ?7 C' s# g myConnect.Open();

* `6 R, X8 r) c% f/ w0 h3 `! T7 d

//获取已有的数据 ) C: G$ K$ m8 _- ? m_DataSet=new DataSet(); 2 K% Q. q8 b4 Z try $ P( F3 i5 e& \ { 6 ~( o; x; e1 ^7 Y* m7 B myDataAdapter.Fill(m_DataSet,this.m_TableName); $ n8 h1 T; {* F, a //如果是首次上传,则增加一条记录 ! {1 S- ~2 h) z6 | if(m_DataSet.Tables[m_TableName].Rows.Count==0) . N- H( X$ d4 h% f; M' R* I { 5 |! l1 ^" m4 T DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); 6 _! H" ]2 a3 f* C% M newrow["序号"]="1"; 0 a5 Y* ]3 z& p- T" O3 F, \ m_DataSet.Tables[m_TableName].Rows.Add(newrow); % J1 B% h8 ]8 n4 P5 E5 S* F } 2 C; n) Q8 A% K* o+ R ' H f2 R4 p6 T DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; 1 V: ^& ?- Z5 s3 C: n+ u //填入去掉路径的文件名称 6 A, W- `1 `9 P; {$ m6 L row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); 4 U* F7 S2 S* t9 r //填入版本号 , Z2 b! l. K/ ^. o row["版本号"]=this.txtVersion.Text.Trim();

5 s& L6 _* w$ v+ w! v8 [! q) t

//将实际文件存入记录中 $ Y/ Z' Q# U6 O% l, J FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); 4 b, ?1 [2 }) w' |! Q7 {; M byte [] myData = new Byte [fs.Length ]; # t }4 v# D, g/ V fs.Position = 0; 6 X) L$ o3 n7 \( X/ d& K fs.Read (myData,0,Convert.ToInt32 (fs.Length )); $ R. @/ J) l: Y& L" @2 T" `! k row["文件内容"] = myData; % o$ R5 m% `: h: o+ ? fs.Close();//关闭文件

) r& P) y5 C" D$ t6 J6 G+ u, H. m

//更新数据库 1 e4 L3 f9 b m. A; e% q myDataAdapter.Update(this.m_DataSet,this.m_TableName); ; G1 S d- H; M6 G4 p myConnect.Close(); 6 i+ S- `, r" C/ o6 m) l& J MessageBox.Show("文件更新成功!"); 6 I3 ^) C( b0 S2 K# C } 5 c+ x+ X. B7 t& ^; ?7 d, h catch(Exception ee) 9 I) d( S. z0 C) J { 7 w/ O l* q2 ]4 [. m8 z0 m MessageBox.Show(ee.Message); 3 Z! P( ]' P) t/ r } 8 V! E5 q" S$ l* O; z* s - C5 `* U, _& M, ?. J } 5 n( ]" o& | V- u. W! K, s else 7 E9 ~ i, x2 p7 ^! f { 2 c! E# A+ D2 m" T MessageBox.Show("请输入文件名"); 1 U% a0 B7 z8 |- Z" q* D% R7 s. ^& y } 7 Z; u- ~: b. K \* ^ } : s9 w6 @3 H6 S 至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 7 o% v5 w t4 j T0 d最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 5 D. c2 x8 A+ v- n添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

8 U" T6 x$ ?( R5 A5 U2 A7 s. R

切换到代码窗口,添加引用: ! }1 Y4 R0 @9 E& L- |0 Nusing System.Data.OleDb; . Z# W- |! p4 E* dusing System.Threading; ( \' u6 d% _1 j7 N+ [ using System.IO; ' K! r3 ^# K/ J2 G: y7 rusing Microsoft.Win32;

3 H6 i e8 }$ X

再添加如下变量: ! T# ?- h/ [, N( d3 _; Q /// <summary> * m+ m7 o$ `/ E$ L, |. { /// 存放操作员及密码的DataSet ; s- J9 ^6 o; p' y: Q2 C /// </summary> + t# u2 l9 f! @! x! F; ~ private DataSet m_DataSet; " f% c& t( u f/ ^" h) C /// <summary> 3 w8 c9 ` t) z+ s /// 本功能用到的数据库表 + n/ {, y6 O |# T% J /// </summary> 2 I* w. e. j( x1 ] private string m_TableName="操作员"; ! Q4 O4 b4 ]: O! k" @ private DataTable m_Table; : L! Q' {# C: E# J, R. d8 U: W为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: 5 [, F8 ^$ W {: M- _2 m /// <summary> 9 A" H0 a' \2 K# V /// 定义本软件在注册表中software下的公司名和软件名称 % e T- x8 ]" p' ] /// </summary> % Z6 ]- S* n$ z* ]0 Y9 F$ Z* ~/ } private string m_companyname="lqjt",m_softwarename="autologin"; , z( c& r- S. c9 D; n4 D& R! g2 { /// <summary> & `/ T. {% @ l /// 从注册表中读信息; 6 T5 |& O9 p$ N+ T' I /// </summary> ! c H" f. o& z3 Z5 o6 ]* ^ /// <param name="p_KeyName">要读取的键值</param> 3 Z( I+ h) s( }2 I /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> ]0 B* L4 W2 l, _, b private string ReadInfo(string p_KeyName) & ~, y7 k% R2 M* J { u1 l0 F& g: \ RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); ; H8 V4 l- y0 K4 p% T5 t RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); 1 U+ N% r( m8 y5 N string strvalue=""; " V4 c% c& K9 ? 9 D, o# X. z4 M1 ]1 i: p$ u/ J if(CompanyKey==null) , ?1 i: h9 E: { T; B$ X5 g- s return ""; + Z- d% t1 H8 @5 ^7 l' D) K RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 3 n' X6 s. Z. Z8 D if(SoftwareNameKey==null) ( A( }& ~; Y% @+ N. d( Z return "";

8 v2 q, z( O, ?2 V$ m2 [ E

try * D1 x# k1 [3 ^) |5 E { 7 T( h( b3 }/ j# B strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); 7 ~8 F* M; F8 L3 o6 J# F/ x5 F. E( x } : \$ @( x5 X" K catch ) V/ h: ~" l! V* B! C- B {}

' |, b$ P Q4 x: o* [

if(strvalue==null) % a2 z# Q* c1 [7 ` strvalue=""; - J) s8 W( P8 N return strvalue; 6 L' h# \6 y3 p9 ~4 Z } : _8 O$ {3 ]- Q- f /// <summary> 8 l" d% @ x% s4 { /// 将信息写入注册表 5 _1 }# T7 d8 ` /// </summary> 6 j: X! q T( e) k /// <param name="p_keyname">键名</param> # Q8 \4 P3 g4 d# U9 s% H /// <param name="p_keyvalue">键值</param> # a# Y+ ^" p6 g/ F9 k private void WriteInfo(string p_keyname,string p_keyvalue) 8 q' S1 o2 U$ T' b# F& l# D { 8 l x$ Q0 q6 l# P RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); * k" E: a v. Q# V9 G' d1 g( B$ ] RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); G! _; o# ^# S1 Z6 b$ c, e RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); : s$ z- P% f% X- D7 r //写入相应信息 + u" ?9 n7 ^% I8 ?% U, n7 A4 F SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); 1 E1 M/ M8 O: U; z5 I } 5 |( F' ~" V8 {, o5 t ` 再写一个函数,用户来获取用户名/密码和更新主程序版本: , [) E8 s$ m, \8 u( d9 j/ A/// <summary> , c! H5 r0 y' E& ~7 [ /// 获取操作员情况,同时更新主程序版本 1 W1 G) k7 Y& a3 _2 z3 j/ U /// </summary> / S# K* w- g" q: M5 z3 R private void GetInfo() # K- v; f6 e' Y& m% }: p& C4 S& v1 p { 0 m" i) K5 Y5 e this.m_DataSet=new DataSet(); 3 r! O$ h a7 {. T/ d this.combUsers.Items.Clear(); ; }* G1 N( \3 Y& y9 l& a9 P: M string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

+ F" Q* Q3 Q1 j+ F( o+ w0 I

//连接数据库 0 K2 l: f+ n1 B8 t- f3 q. \7 w3 U: T string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ 1 a! ~6 @/ K% Y! A, q Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 5 H" d s& F3 u7 p6 G7 Z OleDbConnection myConnect=new OleDbConnection(strConnection); & B1 @7 _; v! |/ S OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); $ Z2 ]3 S8 Q3 |1 r! B4 | OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); - F+ {* P6 U# m: H1 r+ K L myDataAdapter.SelectCommand=myCommand; 1 d! {# s; x! ^ try 4 v9 F2 g( f# x1 }' p/ a { 2 J1 }1 o4 z( S/ y9 C. d7 t myConnect.Open();

& l n& ]. I# H4 \" Z

//获取操作员信息 ! {+ ^( q8 q& ~9 k& ~3 k myDataAdapter.Fill(this.m_DataSet,this.m_TableName); / ~6 T" {9 i9 }0 x: ], d- j //将查询到的用户名填充到组合框供用户选择 * e- l2 i- Y; ?- C0 ` this.m_Table=this.m_DataSet.Tables[this.m_TableName]; / T/ W1 Y3 H3 H n0 s% W( f foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) 8 m" L$ E# E' I* P! d/ x9 u { 4 \/ T; q8 I" }; \) x& Q( d4 v this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); - z, H9 f7 M5 r( ^. f, V1 _ }

* F' F0 n8 Z1 J* n, U b

//检查是否有新的版本 9 c, f$ @& ?) X' a) A DataSet dataset=new DataSet(); / y, O$ M* h! v. ^% p4 e5 @ string tablename="tablename"; ! D7 X; {, c! }2 W: p6 _ b1 P //为减少数据传送时间,不获取文件内容 & H2 o9 t X% I6 r5 Q$ z. n6 M strSql="select 文件名称,版本号 from 版本"; ) [% J8 P7 A$ M* S myCommand=new OleDbCommand(strSql,myConnect); 7 a) S0 `! B) w* l& T myDataAdapter=new OleDbDataAdapter(); * I7 \. c$ g+ Y( a myDataAdapter.SelectCommand=myCommand; 2 p! H" W3 |3 w' a. G) t! z myDataAdapter.Fill(dataset,tablename); & O- R. R" m2 D* e& k& D8 n% b/ _ if(dataset.Tables[tablename].Rows.Count==1)//有文件 1 W2 }* N3 i- o" g { 8 f2 |" B/ f/ r2 H* e7 L; { string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); & n9 i3 m( W: {( I) [ string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); / _6 ]! R7 a" R //读入本机主程序的版本号 3 L! M3 o6 t4 I$ L string oldversion=this.ReadInfo(filename); 2 d8 I. Y- |4 v+ E2 e( q if(oldversion.Length==0)//不存在 ' B8 E5 N5 a9 Q! N4 E2 S; \' _' p oldversion="0"; 2 } J3 x! P, {+ y$ J, ` if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 2 y$ W0 j3 e! L { ; P: R0 N& V+ g. c4 Y8 F4 g+ [: x //取回文件内容 * W6 X/ p7 U+ q# i* z dataset=new DataSet(); . Q. {8 d0 ~% r$ F1 { strSql="select * from 版本"; 8 b2 @ F1 S/ v: `& E myCommand=new OleDbCommand(strSql,myConnect); , R# m( B# z2 n5 N9 d, t1 } myDataAdapter=new OleDbDataAdapter(); & e8 s8 t0 C$ _ R0 H- G, ] myDataAdapter.SelectCommand=myCommand; & [; F7 O* ~- w7 K2 }- _* W myDataAdapter.Fill(dataset,tablename); 7 s. ]* u3 T% A# i- r+ R- e j) ?# _ //将文件下载到本地 & }$ K! \& m* G; \2 ~ S( I1 ? DataRow row=dataset.Tables[tablename].Rows[0]; 7 M+ M3 R j9 g' z! h$ | if(row["文件内容"]!=DBNull.value) ( H |3 G- C3 T/ u) V5 ^2 W {

0 K4 X& K8 w0 a9 F& R, ^

Byte[] byteBLOBData = new Byte[0]; , H" G/ P1 N* E( X3 U: J w byteBLOBData = (Byte[])row["文件内容"]; 2 |3 V* t/ {2 j try ' d: b0 k/ y. {; ^0 ^1 N6 d# Y' [ { 0 L+ _$ e" a7 W6 \4 W FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); , n z. {; B. m, I' `' d8 H7 ] fs.Write(byteBLOBData,0,byteBLOBData.Length); , F/ w$ a8 Z2 l' w! V' z `! L fs.Close(); # F5 w+ k* d/ Q o: d6 z7 t3 V //写入当前版本号,供下次使用 % j1 ]6 M) _, y* C- K$ W( M this.WriteInfo(filename,version); 3 I# p7 F- @/ U) [+ a: O* ]: L } - X. d5 ~9 [; ?' o+ y catch(Exception ee) - B4 ]3 \4 d( h1 R0 i% f9 d" \ { 0 `) L" c& Z p% s1 t MessageBox.Show(ee.Message); ( t% ]' j- g4 Z6 K! K } $ d/ u) c" \8 U7 C$ K3 i: J: V }

! u' g' q* `& r+ A8 `! U) ?/ O+ U

}//有新版本 8 j- b; P# q6 ^ }//有文件

! j0 A# m7 \% w) s5 |; I6 `0 _( s

//关闭连接 , n$ M7 y, q* F. I myConnect.Close(); 2 D% N* j( y. w4 x7 e5 X } 7 A; Q& ?! B1 l, @ catch(Exception ee) / ^: w& O$ P/ H3 C { 8 @8 @: u) O6 j% r' ]; a5 C' G MessageBox.Show(ee.Message); & g6 V( ~( m7 @: } return; 1 v& ]" I' I- L0 ?: C } 5 y; _6 o" G# _8 T$ b* p: }) A //允许登录 5 [6 ^( C$ G. c% H! X this.btnOK.Enabled=true; 2 X1 P( P# I6 W2 r6 w } 2 x" Z1 H( w" F0 F- Q8 x) V

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: % ]2 E5 |! D1 n7 I private void Form1_Load(object sender, System.EventArgs e) . N) L$ N+ ?, o4 }$ X$ m { * f" B0 Y5 E; [ //为加快显示速度,将数据库连接等放到另外一个线程中去 / \: r* o- ^ o+ D! p4 B Thread thread=new Thread(new ThreadStart(GetInfo)); * S# Y$ R' u6 ~ e( t' y. z+ J thread.Start(); 0 F6 y9 k& e0 B+ y/ Y$ e$ g } & R' `; E; J8 M5 @3 g有了上述准备,我们来编写确定按钮的响应代码如下: 6 p& i* q8 y* S* @: A2 Yprivate void btnOK_Click(object sender, System.EventArgs e) ' h. ^ I% q, K9 ?1 |- L1 I { . q# S% \ g; \ //根据组合框的选择,得到当前用户在DataSet中具体物理位置 2 I5 U v! J3 d2 c; S; O4 m. V if(this.combUsers.SelectedIndex<0)//没有选择 9 O- U! t. n& }* b: W return; ( e9 q. x, @; l" t- T+ i" V DataRow rowNow=null; $ _, }) O) i* V: r1 `" E foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) + @ Y. J5 i% o: U" c { , N; G c: L% F b if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) " B! n7 k4 I: T4 A w, ? { 2 z. g/ d O6 f, L% {( Z% x rowNow=row; - d: k5 E, z6 S3 H" O( M break; 5 G& E% Q9 l1 K } 4 ?' x# g7 J/ v. Z6 j } % D2 W3 A. k5 H4 s! w if(rowNow==null) * ?7 O! O8 ?# V3 J: h return;

//获取当前正确密码 # f6 o0 J7 h- \3 @3 } string strPassword=rowNow["密码"].ToString().Trim(); / o2 Y5 q; j( _1 M& ^5 L this.txtPassword.Text=this.txtPassword.Text.Trim(); . A3 ]( H, w1 y: b if(this.txtPassword.Text==strPassword)//密码正确 $ k2 t+ k4 ^* }% a' q5 f4 [ {

//主程序名称 8 b$ |, g& M( C9 k1 t string filename=Application.StartupPath+"\\"+"MainPro.exe"; 1 Q5 t' G' u; V+ w //参数名称 : W# [* d7 j6 R# M1 {' z) z string arg=this.combUsers.Text+" "+this.txtPassword.Text; 6 D$ g" j& |* E T* E //运行主程序 ' c3 n1 B. ^/ A2 I* `4 ~# k* f2 V System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

//关闭登录框 M4 ?% r7 z4 b5 X6 R this.Close(); , X/ ]' O) |8 h) q: ? } 3 P# a8 L$ w$ m else 6 ~& R6 u5 ?* K) Z+ i { # H( R, [4 o, y8 g! Y8 O MessageBox.Show(" 密码错误!如果你确信密码输入正确,\n可以试着检查一下大写字母键是否按下去了。", 2 W8 |! r. a1 I "警告",MessageBoxButtons.OK,MessageBoxIcon.Warning); & Q* ^! V9 N3 _/ i0 P; x, l3 U this.txtPassword.Focus(); . g# p7 j/ k. V+ V0 L: a! l9 p* m this.txtPassword.SelectAll(); 4 F* D j6 I3 [ } 7 P( Q6 |* W9 y) H, x0 s } 6 y O2 _; ]1 T& ?, I6 Q取消按钮的代码非常简单,就是关闭登录窗口: 3 T4 K; y6 G% z private void btnCancel_Click(object sender, System.EventArgs e) : }+ \/ _. U- t" f$ } { 3 O( J* h$ _& I# q4 X" R* C4 M this.Close(); - }# ?8 g# p& g/ S+ ? } 8 Y$ a. i: Z1 n把Login和MainPro软件连同其他相关文件打包成安装程序,将Login以快捷方式放到桌面或开始菜单中供用户使用(当然,快捷方式名称可以随便取了),用户运行Login后,会自动更新软件。 5 A8 P: K K& h b: m( V本例中所有代码请ftp://qydn.vicp.net/ 下载,文件名为update.rar,解压缩后别忘了在D:\创建一个output文件夹,并将mydatabase.mdb复制到该文件夹中。 / X- A9 [& Z4 G8 }7 i0 w 说明:本文只起抛砖引玉的作用,通过该思路进行扩展可以完成许多功能,如通过修改上传/登录程序,不仅可以实现对主程序的更新,而且可以实现对任何要用到的资源文件进行更新,本例中不能实现对登录框本身的更新,我采用的办法是在主程序的Closing事件中更新登录窗口,因为此时登录窗口已经关闭了。在登录窗口中,可以放一个“保存密码”的复选框,如果用户选中该组合框,可以将用户名和密码保存到注册表中,下次登录时直接读入,用户只要点确定按钮即可,免去了每次都选用户名和输密码的烦恼, 3 N/ p$ @7 ~* O% m6 z在本例中,我们可以看到,数据库的连接、查询等工作是重复性劳动,且三个个项目中用到的数据库、公司名称等是一样的,在实际工作中,我们可以单独新建一个cs文件,不妨取名为MyTools.cs,将一些常用函数和变量(如数据库连接、公司名称等)做成静态的,各具体项目中链接本文件,然后直接使用,我们只需修改MyTools.cs中的相关变量或函数而不必在每个项目中都去改,既方便又不会遗漏,MyTools.cs参考如下: 2 f! k7 k" o% b///<summary> 3 o7 c/ F9 j. N# |///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 % O* y7 y$ V' k ///</summary>

//#define NETWORKVERSION

using System; , n v8 N( \( J: I: Pusing System.Drawing; - f! n m( W" t% P: o7 h; i, w. O using System.Collections; 4 O9 L! u! H( m' v) g" @using System.ComponentModel; 0 S/ W% c r X using System.Windows.Forms; 5 t0 C* H5 p+ I1 [( p9 W using System.Drawing.Imaging; ) p3 i& R1 \% i4 y9 O9 l using System.IO; 9 |; r1 M! @: ?. c5 M' _using System.Data;

#if NETWORKVERSION ! M* r' n/ Q: x, w# V( v( iusing System.Data.SqlClient; + G7 W" w6 v5 M #else 3 Y3 r4 h$ T7 {, v, o" S& c K2 [. v$ E& | using System.Data.OleDb; 7 J! Y0 B8 k, M6 I7 j9 |#endif ; C* J2 n T: k+ A. z4 E6 fusing System.Reflection; ; k& ?, X# f* `7 ^5 w8 F' D using Microsoft.Win32;

namespace OA . h4 [' R" ~2 _6 S. Z{ 4 b' g3 u6 X! N public class Tool $ ~$ W+ }% y7 o. z8 m9 H) e { ( |; u3 x# R1 N1 I5 j public Tool() 8 ]8 l$ q" k' D$ W" K- u( _' ` \ { : g$ h* O2 v5 T1 i. }2 R2 \ } . o3 H. |+ \% o/ C- X* D+ d: Y/ Q /// <summary> $ n& U% l. p( N4 d) j( g" f8 z /// 主程序的文件名 ) _& _/ f# r% D+ `, c5 e /// </summary> 3 w; J3 e/ f8 {/ g! e public const string FileName="OA.exe"; 5 T" v- M. z; D/ y4 ?, g public const string g_TitleName="丽汽集团办公自动化系统"; + H: z# ]% D# ] public static string g_UserName; ( \. O% u+ V O* t5 M: T8 W- n public static void WriteInfo(string p_keyname,string p_keyvalue) 2 y v# P1 z9 [5 |" n' j { ) S" C" m4 J% A! m4 x …… " |3 F" K/ w; P: N( {; W4 C7 H: @ } . I* N2 n& a6 S; }3 y+ N$ ]8 s6 P//其他类似代码略……

} 4 ]4 v- z* n1 p} $ S& \$ l9 m/ P9 w$ O) c8 \/ d 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: $ Z( ~) p1 b: T9 S. ]8 W& f 在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: ( z% f# K7 V% K, D using OA; 4 [$ u' H0 X: |# `9 f) u private void myFun() 1 N2 u/ O* C* b( y{ 1 h ~, l9 x" C* @, fstring s=Tool.FileName; " g- U7 _" ]% [8 }3 l2 S } & k2 P8 W; m5 r 如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 ! M* ]; K0 M5 S6 O$ j- L C4 Q 我们还注意了一个细节: 6 M+ Z6 A" L/ S9 E1 K2 \///<summary> . s2 T/ f1 Y2 m' S///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 1 s2 [1 X* h8 v2 O# S1 h ///</summary>

//#define NETWORKVERSION 4 o, U" o2 T' I' @$ p9 n我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: : D, v3 O* A8 t5 | /// <summary> 5 \4 h+ v' v( \9 ]1 n2 @ /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 ' d1 N7 \/ N6 L5 Q+ ? /// </summary> ! N) E- J, v$ B' H/ \) K /// <param name="p_Sql">查询用到的SQL语句</param> ) j- i7 X4 h6 q0 s( b/ K /// <returns>查询到的结果,没有时则返回空""</returns> 0 l0 U E+ n! m1 a% Y' @ public static string GetAvalue(string p_Sql) 8 j& I: e8 \: E { , F& m9 A: e* T' S; n4 L6 |& g string strResult=""; 2 I7 Y( a8 _; [: b Tool.OpenConn();

//设计所需要返回的数据集的内容 7 d# l0 N7 u1 k0 m# ?( h, t' V1 K1 @ try - `6 Y! \3 j7 Q$ ~# g% ^# _% e { 3 B/ i( v8 `" y0 L: @4 h$ s8 @ // 打开指向数据库连接 ! K; S/ ~0 v8 J+ {6 Y+ `#if NETWORKVERSION //网络版 % U$ s9 B C, d SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; 7 e4 y4 a" n, _ SqlDataReader aReader = aCommand.ExecuteReader ( ) ; 0 M3 W; g5 ~; \( z3 K#else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 1 ~/ s5 T# G, w OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; ! f$ U, }7 |+ t4 B OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; # u' C& t: Z, h* t3 @5 h" l #endif 3 v& Y5 r3 G1 F- [& x * S$ F- o" V: {. p // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 0 O4 h8 K! X, M8 A8 @ if(aReader.Read()) ( B1 y( X& e: h6 a strResult=aReader[0].ToString(); , Q- P: ~/ p5 D, g9 [ aReader.Close () ;

} ; h" }6 W( Y. S2 | catch(Exception ee) 7 H( ?0 N. o+ b' p) E$ ^# z { # B' h6 q" u& g, y# m MessageBox.Show(ee.Message); # A3 a9 d0 t9 a/ z } " v/ l2 H3 S2 W7 u* J) g. _( @ return strResult; ! @3 ~5 Y; ^1 J$ N } 8 A6 X$ q+ E) |! `% g& j8 ~ I% G 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2024-11-25 05:14

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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