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

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

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

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

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

×

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

: A: p( y$ s+ h! Y3 B W# G7 D

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 0 [! g ^+ h# Q2 F 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 ( t0 P K! I# U" d+ ? 创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: " s. \: L0 M0 ~0 x /// <summary> * B2 l% r" l" S6 @0 _" ]$ B /// 应用程序的主入口点。 % P& {4 ?$ Z' L/ f* H4 X /// </summary> 3 v! ?8 d2 W: ~' x3 ~ [STAThread] 9 h8 v8 P- i" p1 v. t9 }, ~ static void Main() ; ^ R' m6 L+ s A$ i* x" K# X { 0 V. D6 q( o/ X9 [9 ` Application.Run(new Form1()); ! b! q8 b9 u0 m! Z7 a3 v# L: o } : P+ i& F Q; q. h 为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: * Y$ v. L+ `$ j1 P1 L private static string m_UserName,m_Password; " m, }/ z5 e7 b. U /// <summary> # C1 O, Z F( o /// 应用程序的主入口点。 8 e5 T9 W) h$ _% s: x4 d# u& S /// </summary> : q5 J$ V! Q3 P$ U* c: L [STAThread] 2 [" z9 L6 ~; w, S; z- H" g% B static void Main(string[] args) 7 Z2 n& k0 R7 c( t. O _ { ( {' }0 a |7 `: | ^" ` if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 , r! D9 }# N4 v7 Y! s- P { % d9 E' `3 H6 ]$ ? //记录下用户名和密码,供软件使用 0 t4 s+ ?: u0 h# t7 F6 N* P; Z9 f m_UserName=args[0]; * y' ~( T5 T# r7 K- g# y5 i$ F m_Password=args[1]; 0 T5 A3 }0 e' Z1 J" ~6 \, M* u& ?- s Application.Run(new Form1()); * [9 g; N3 `( K+ M4 a$ f0 X9 y } * I! J3 q; x+ E; O! n else ; a, _* U" y1 W" p; x { 8 A- G# m, i! J) O4 R+ p! _7 }1 C MessageBox.Show("不能从这里启动"); # O) L: v! S3 B1 [7 K } ( L% D6 M" w6 W$ s% l } ' D( k* X: W6 |0 C; l 为了显示登录是否正确,Load事件的代码修改为: , F: B( M- B9 w/ w& K private void Form1_Load(object sender, System.EventArgs e) & C8 C) f* D5 Q. f* I { * J2 E& F0 y* q9 I# g string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); % R( i/ Q* G5 o' }+ a- p* B) I0 z MessageBox.Show(msg); 8 j* C$ }2 E: b$ t } * U# F: _8 a, h; [6 P* B9 t% I9 i4 Q 这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 . I5 ~: D# M0 n/ ]! S/ `8 l 由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

8 l$ T9 R8 n n ?2 V+ g& o

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 " d0 o6 P5 Z/ G8 A- g在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: 9 O8 c2 m6 V& I 操作员表 版本表 5 y& T1 ], I/ V, `; X8 a 字段名 类型 用途 字段名 类型 用途 & D5 a8 C; [: S序号 长整型 主键 序号 长整型 主键 . d! v& k4 g) g1 F$ g姓名 字符 用户名 版本号 长整型 存放当前版本 # c4 p. y0 v/ L7 u 文件名称 字符 本记录对应的文件名 8 m' {3 Z* I1 _: D2 n密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 - u1 Q" D& H9 V2 C 我们手工输入一些用户名和密码,如下:

3 ^) t$ g- P3 W6 ?! G

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

; b& `$ e0 G" k" V; Z9 }" j+ ~

点“确定”,同样,配置输出路径为D:\output。 / V/ J& O. a, H8 `9 g$ f# }- ` 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

$ ~! s# G* i4 T" W% ^

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 % S0 l4 ]# p% l5 v9 ?6 x 添加浏览按钮的响应代码如下: ( l5 {5 b2 ~' ^7 h# L private void btnBrow_Click(object sender, System.EventArgs e) ' Y/ H3 h: u" B. c) V0 I6 i: y { 3 H$ ~, A9 L; m% _ OpenFileDialog myForm=new OpenFileDialog(); * D4 w+ w9 Q; t# N myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; / ^- w- U3 N% C+ A3 E( Q6 k6 q/ p if(myForm.ShowDialog()==DialogResult.OK) 0 y7 W! U4 z& }" O6 H/ G- [1 S { 2 ^) [- K+ w! C6 z7 q, ~" |! f1 V this.txtFileName.Text=myForm.FileName; ( l) Z7 e* b) Z) @$ v c1 e, j } 1 I: ^3 m2 w M$ z% x- T0 k } ( H" Z+ A% c, ?) O7 ^1 u8 t: v( }; a该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 ; o" N; j# m7 N% l 添加取消按钮响应代码,目的是关闭窗口: 8 W& i% _8 L K- [3 M8 O private void btnCancel_Click(object sender, System.EventArgs e) 5 x2 I, q, S' A { 8 N+ k) P; K6 l2 c this.Close(); + T7 M) w" p) \ } ; P V( z! a2 ], ]2 ]( t# [ 添加两个引用: 4 w- g& c( `0 Z& p: y% e using System.Data.OleDb; % N3 g' {1 @1 X2 Q' m using System.IO; 3 F5 x" k$ n0 G 再添加两个变量: + m e8 H5 m' X! [& C private DataSet m_DataSet=new DataSet(); ; I% ?* D' p$ x4 X+ H! w0 S6 K private string m_TableName="版本"; $ V( V( }5 N2 d3 ] 下面的函数去掉文件名中的路径: - l( J" r5 v) d, u l /// <summary> . i, j1 M4 `: @" a+ }2 w/ ], ^0 M /// 从一个含有路径的文件名中分离出文件名 % `3 U* P3 r: H /// </summary> " S& B1 M" N4 Q& n* O' I: c9 m7 V /// <param name="p_Path">包含路径的文件名</param> . b1 w% ]. J2 Z! M; Z ^- i /// <returns>去掉路径的文件名</returns> * L3 J% Y; U' V- N$ Q0 R private string GetFileNameFromPath(string p_Path) ! Y2 F* l- I* J' U { * ^$ Z% P: u$ j8 F: s string strResult=""; - [$ O( s% W$ ^- ] int nStart=p_Path.LastIndexOf("\\"); 3 }% ]0 G0 _, ]( n& A if(nStart>0) " Y* x/ g5 N. q- @ { ; c5 d. g; S8 n$ `5 N5 i strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); ( x, V `, g/ c1 ?1 \ } 6 G* b4 p5 \; i6 n5 Y6 e8 T return strResult; " G: u4 |8 c7 `3 _5 l } " g- P% G6 ~" V1 D添加确定按钮响应代码(含注释): * v6 d1 Y5 R7 [% D' A8 Wprivate void btnOK_Click(object sender, System.EventArgs e) 9 M$ [+ a* v: f. l+ U0 O9 L2 B( G' R+ D { % c1 X- \* y" v# I2 g5 q //检查版本号是否合法 - C' }9 b. a) ?4 m7 L' } try ) ]( C7 j' k- y8 J( A! H { \; p* o' b9 j5 ~- V! J. z Decimal.Parse(this.txtVersion.Text); . f4 N, w) ]5 b } 8 Z! f! ] N2 v8 l. p4 u catch , X- Z# D( t5 D- s { 6 K9 r0 p9 g7 b$ e9 J* A MessageBox.Show("无效的版本号!"); 0 f: n. b- ~: y& ]2 A/ l this.txtVersion.Focus(); 7 T- X! T2 Z, ]) j" d this.txtVersion.SelectAll(); + c2 W: d. N# D- G( f. r return; & E1 d! L/ n: ~" j+ E }

/ n: z3 h- V9 x

if(this.txtFileName.Text.Trim().Length>0) $ ]! |) Z. y$ j$ [ { $ Y# H+ W# V) O; C G //检查文件是否存在 3 g8 S1 H7 ]. n! O7 J8 \2 u$ o if(!File.Exists(this.txtFileName.Text.Trim())) 9 L& q7 |, V/ F. P { 2 w$ y( g) a2 v, q MessageBox.Show("文件不存在!"); & k6 T, l% D/ L return; $ v( k- Y6 O% ?" T) `/ M }

: C' D% E2 }0 p6 Y

//连接数据库 , x4 G0 y; W* G/ w string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ 3 j& K' \4 F- G9 P: ]- p Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; ( {- w1 c7 s, M+ B% k4 w p; _; l OleDbConnection myConnect=new OleDbConnection(strConnection); 3 f; Q' S4 M1 \+ h; V# n OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); $ r4 y6 ?; \8 y OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 1 {$ a( i% z) m! o4 { myDataAdapter.SelectCommand=myCommand; 0 T/ f, w4 s- J8 ?' n8 H OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); + C( n* u ?# \* ` | M8 c) X myConnect.Open();

" {. Q2 J v2 Z8 n

//获取已有的数据 % g/ T- f d. D* s3 C( l m_DataSet=new DataSet(); 2 @! T; A/ d+ ?; d9 A$ ^ try & n/ N0 G! g4 u F { ( E8 M" |. X* S- [ }% ?! l myDataAdapter.Fill(m_DataSet,this.m_TableName); c$ A6 k" t/ G7 ~ //如果是首次上传,则增加一条记录 3 L3 W- I: ~7 A if(m_DataSet.Tables[m_TableName].Rows.Count==0) # ?% o/ ^9 y* s7 f7 ^6 o4 \ { 7 I) x& x$ X/ S' c* a8 M! b& ]7 o DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); 8 }; ?; B/ H. j& O" b4 P' o; h; M newrow["序号"]="1"; 3 M7 c* s# I* B# B1 C m_DataSet.Tables[m_TableName].Rows.Add(newrow); + J7 \. x$ h& I$ ? } ; `. k" ~* e* \/ C! `8 [ ! D! C" t3 M4 w4 I1 `1 n E( T9 G+ o1 [ DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; ( {* v" j+ ?0 q- I8 [: l8 c9 e, Q- L //填入去掉路径的文件名称 ) z! s6 L) ?7 c3 G/ R O1 F( U7 J row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); : o$ _% a, C2 t. K- x //填入版本号 4 G d* @% E8 E2 ?% `) N; h& @ row["版本号"]=this.txtVersion.Text.Trim();

3 |, E' ~- v' t2 r

//将实际文件存入记录中 7 v8 \" y ]% L+ E, ? FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); - y% O. e' o, O5 D( H/ V& o6 e byte [] myData = new Byte [fs.Length ]; - c) X9 ?& r8 r7 k0 y" T fs.Position = 0; ! H. ?* a T e" D8 M" A fs.Read (myData,0,Convert.ToInt32 (fs.Length )); 3 Z% s0 [8 M6 ?( i) u$ I, C: I row["文件内容"] = myData; 4 s" u [5 A) W' `! a: } fs.Close();//关闭文件

& O6 t& |7 E" @, x7 X

//更新数据库 # V# J1 w2 X z, |" n# M' l% y myDataAdapter.Update(this.m_DataSet,this.m_TableName); $ o$ |& y# U* s- x9 }+ [( M myConnect.Close(); # L: y3 ^2 Q S* Y( _/ G# K0 F0 n MessageBox.Show("文件更新成功!"); . V( ?* D9 [3 I. [+ K q% G0 w } . ~0 ?' A7 U, M% v, p+ G" t catch(Exception ee) 3 b, ?3 X. n8 h# i { # `2 l( m3 ~! G) t% ] MessageBox.Show(ee.Message); / A& ]1 I& q$ f9 Z } ( @, P% K. {) a 9 a5 |9 a( z/ c' c' ^) j } % d J: w& e9 {, y/ d& m/ ]# H V else # X0 H+ K; I: d4 x2 @4 @ U { % n7 C/ T; C E MessageBox.Show("请输入文件名"); ) F( V0 U- ~; y0 Z# E } 1 K4 r" m! n2 r } + o% U/ \1 K1 l; y C, G至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 X3 u! ^* v5 G% r 最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 7 @+ d5 A+ X5 L3 w* U添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

& m* ?; W( w7 G3 m. P, M

切换到代码窗口,添加引用: 6 W0 I3 H7 J7 V: b2 m8 T3 b, a using System.Data.OleDb; 3 T- S3 b: S- U+ @using System.Threading; 3 g ] P' I( H; ] using System.IO; & X5 Q% @0 P' z9 l" D3 S* w$ g. kusing Microsoft.Win32;

" |1 j3 R& q! u( x2 v e

再添加如下变量: % N' \* \) ~ ]* G; ? A( P! J /// <summary> ! F7 B# x; W1 f* E/ a! |: F /// 存放操作员及密码的DataSet # {( J! l' ]3 a* H4 z /// </summary> * k# \: m8 L* S( ~- k. y; v+ P private DataSet m_DataSet; 7 m8 ^7 N2 ~2 ?$ U2 ]" k2 T# J, m$ ^ /// <summary> 2 O7 b5 M0 J2 m- a! Q- j: |( \* z /// 本功能用到的数据库表 O0 \8 R4 h' q) U% m+ F /// </summary> # x3 M' C/ e% v9 {; ?9 I. K private string m_TableName="操作员"; & u. z/ t! w$ c( I1 G private DataTable m_Table; 3 g5 V! k" h5 `4 A7 u为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: & { M; F3 J; Z6 q0 O3 r) N. \+ H /// <summary> % p. y7 ?9 [1 h( M1 P9 d$ c /// 定义本软件在注册表中software下的公司名和软件名称 P( M& u" e% F; a% u1 Q /// </summary> & [; l4 x1 h, J3 }% C2 D9 M; F private string m_companyname="lqjt",m_softwarename="autologin"; - [; ]7 D" L* j/ W8 l! ] /// <summary> 8 U( D, J$ }" M5 G+ q1 f2 c /// 从注册表中读信息; 9 Z. j i/ N4 `8 }. y a /// </summary> 8 o4 g! f8 |- p+ z4 W% }% \ Z) | /// <param name="p_KeyName">要读取的键值</param> " F7 B6 X. J9 ?4 T9 ] /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> & D: p k; [7 ^! a4 k& H private string ReadInfo(string p_KeyName) 2 a' S r- z/ x$ _+ |$ v { . O4 R" j; }; Z1 H/ b a/ {2 W RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); ; a {, ?* W e$ C, V$ i RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); # n7 D, [& \& A string strvalue=""; 9 H$ p W1 l/ o! H 0 I8 l, X, o3 h5 H if(CompanyKey==null) " H1 S1 i6 s' M return ""; : S4 d9 K* u2 l* C RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 9 T7 E0 u6 X( @ S, q. l$ p0 { if(SoftwareNameKey==null) 9 ~4 M1 T# C+ ~' \" T6 }, j5 o return "";

6 j& z8 ~$ X8 S$ O. m7 e2 Q

try 3 ~% z: X( s6 t$ D' ~% x { 8 U H+ J8 E- X+ G5 ] strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); I6 f$ _& p% A& h } ; u. J$ l( d3 S: f7 S catch 8 y' N* l' ?; K5 j( d {}

% S! n% [* H5 J$ W0 k9 m) A

if(strvalue==null) 2 T& \( N, m4 Q3 f) k9 E6 r5 ] strvalue=""; . P3 S- T0 u3 T0 [8 F return strvalue; 1 B- K) E* R& L/ m2 F) @ } ; ?5 p% s" H l6 Z4 {4 l /// <summary> : |" V- Q8 @) X+ K /// 将信息写入注册表 4 x3 ], D |( E) [& A* E /// </summary> & m& h4 j5 A [' z+ \5 d9 J /// <param name="p_keyname">键名</param> . W) u9 C% M# j! I /// <param name="p_keyvalue">键值</param> " s* N6 E# z# Y( Q! s# G ?/ ~7 [ private void WriteInfo(string p_keyname,string p_keyvalue) K1 a! q) l& X; \& i8 J { 1 w+ q3 ]5 X V RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); ; K0 l! |; @$ N- r% y RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); * j; v( m- }8 E5 ~ RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); 6 Z' C8 a. C! u' i( F4 e //写入相应信息 9 y2 p/ @: e b0 A' Q SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); 8 N$ Y6 V% @3 F. y$ _6 U5 v& @ } m, y7 X- i- y) o+ |6 {: {再写一个函数,用户来获取用户名/密码和更新主程序版本: ; Z$ n# |: _, t: Q! i* w2 k /// <summary> 7 ~/ i" r" V. o( w2 k f /// 获取操作员情况,同时更新主程序版本 : e) K" l# ?# v& G0 k! E& k, i: f$ x# _/ x /// </summary> $ c3 J6 S ^& m" n1 `4 j! B0 O private void GetInfo() 4 y( P: a- |' X0 P! W { & Q7 f6 ^3 h0 O$ C7 M- y( I* R" _ this.m_DataSet=new DataSet(); ) r" K G2 ]8 J5 S3 T) V: P$ ~ this.combUsers.Items.Clear(); % m" m" m& M% q0 i- u- B$ O6 l string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

* n/ u* _' `) r2 f$ r7 K6 ?

//连接数据库 $ W- P% K+ U# K5 g& p! X; N string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ 8 l! }- q2 B$ U6 r: b Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 9 o. s6 x3 q. y. ?! G E OleDbConnection myConnect=new OleDbConnection(strConnection); / x' y% q( s) W; r OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); 8 i3 Y" W- R i OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 1 H1 A% y) ~- V+ \( ~8 X' k myDataAdapter.SelectCommand=myCommand; ; n; `- a$ N$ N3 S7 G- f try . z0 a! |; g! B+ g { + A) c; s2 ~8 w myConnect.Open();

" C+ O% x) H4 I+ [: ^0 ]8 n. c

//获取操作员信息 0 _: p3 R1 b# [* o! u; p) r9 _ myDataAdapter.Fill(this.m_DataSet,this.m_TableName); " Y! E- w* u6 U" R, C/ {6 ~ //将查询到的用户名填充到组合框供用户选择 / o1 I& o6 M0 G! P. F: s0 Q: y4 p this.m_Table=this.m_DataSet.Tables[this.m_TableName]; ) l9 o1 G( w, C0 B: n% z0 C foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) ( ?" j5 D$ g0 V3 C1 t3 A. r { @0 Q6 p: E+ L6 M this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); e: L1 ~$ U; w3 ]- l* J }

% \' ?; B4 B/ x! Q

//检查是否有新的版本 . `! a: L6 E |- }& v& p4 y4 @ DataSet dataset=new DataSet(); % G& x. N6 w) f5 U string tablename="tablename"; 2 g8 |1 M5 W4 A1 v* j //为减少数据传送时间,不获取文件内容 ! a6 w. B# Z$ Z) @. ]' l strSql="select 文件名称,版本号 from 版本"; , s7 [* O# r/ L- i# B9 u0 r myCommand=new OleDbCommand(strSql,myConnect); 3 M f2 b1 F2 k; H myDataAdapter=new OleDbDataAdapter(); / R! }& @4 a" d0 r4 X! L myDataAdapter.SelectCommand=myCommand; 9 S9 D- u% H5 b/ [0 o% i myDataAdapter.Fill(dataset,tablename); 3 k$ [7 ^7 `, `: V3 Z1 Y0 x if(dataset.Tables[tablename].Rows.Count==1)//有文件 % C# r; |3 t" x: Y { 4 N! `8 `$ t' c( t. F string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); ' j$ y9 O! y! O2 s9 ?2 n5 W string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); . c3 S: h+ Z) \- f3 c0 w1 Y //读入本机主程序的版本号 8 L0 G# ?# i3 W string oldversion=this.ReadInfo(filename); , b0 |2 C# s' M! ^2 M if(oldversion.Length==0)//不存在 ) {3 f' i$ y8 W$ y+ G9 _ oldversion="0"; 9 G3 [! K( k& P1 m" F! j if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 - I. R, a( _: o& U' q { 9 h/ ~. @& ], ? //取回文件内容 6 |5 t8 G$ u, T# Q dataset=new DataSet(); 4 k& g6 i5 t7 R2 u" b a strSql="select * from 版本"; % A G, _ ^% O( B2 T1 y+ w5 ?1 c7 Y" z! w myCommand=new OleDbCommand(strSql,myConnect); ( \/ L/ @- }$ z8 X myDataAdapter=new OleDbDataAdapter(); 8 x/ q2 S: e% p3 Y8 u myDataAdapter.SelectCommand=myCommand; 0 h+ n& `" I' w- }) \ myDataAdapter.Fill(dataset,tablename); $ p' \ I, a8 m2 J" k2 K9 T //将文件下载到本地 - J0 K/ ?: d. M, N7 T2 M DataRow row=dataset.Tables[tablename].Rows[0]; ' |- r7 [! ]& {9 c if(row["文件内容"]!=DBNull.value) # W- c, ^* a( z, Z, r {

$ c: h7 w. Q$ r

Byte[] byteBLOBData = new Byte[0]; 6 S. O x- W) w7 P4 U2 ~& ` byteBLOBData = (Byte[])row["文件内容"]; & a! P) w3 k$ A try ) N" g4 @0 W* I { $ [1 N. t9 A% @+ Y) K, k; E FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); ! m( m# D' a7 p5 |3 s1 n, } fs.Write(byteBLOBData,0,byteBLOBData.Length); $ O' ?) n0 x7 L2 g/ U! ] fs.Close(); 2 ?. B4 d5 g0 ~: p: l% H/ c" O% t //写入当前版本号,供下次使用 7 t+ O% z0 A) J) N3 M% j this.WriteInfo(filename,version); & y+ y5 f6 P( @ } ( @" P# R/ x6 {+ |4 h catch(Exception ee) * z' V4 Q( N$ l9 { { 9 w5 a' J- W) y; m4 X7 [% |$ y; B0 s MessageBox.Show(ee.Message); 3 w) c6 y {* u4 w" n& O } 7 s( T* E. `" `1 g; [2 O/ v }

1 t9 o; r% G2 X0 l" t

}//有新版本 6 Y6 ]1 A- m; A% C! p- o }//有文件

0 q8 p; @8 ~3 K9 ^

//关闭连接 $ \" U* m5 k0 e% o( B myConnect.Close(); - Z: ^1 [4 M# P } 3 T' c+ B) l. f! h# [# c catch(Exception ee) * v# }7 u. n6 v { ! S. Y2 P* h2 W& i* m. i MessageBox.Show(ee.Message); . _. d% T5 `$ A3 O, ]- I R/ ~ return; - q, g0 ?; t6 c f/ e6 G } # `9 |% \0 V2 t- X: j //允许登录 ) s7 c! h4 }+ t# o this.btnOK.Enabled=true; & t/ a6 ], G3 F6 i, l* @) S } 2 L4 _, r& k' P* s

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: ! K) S7 ~1 i3 x private void Form1_Load(object sender, System.EventArgs e) / M2 D9 y5 P$ i2 T { : ^. u: g+ k9 Z' P3 n2 a' _; M5 g //为加快显示速度,将数据库连接等放到另外一个线程中去 5 [) J% e6 O$ n( g& n Thread thread=new Thread(new ThreadStart(GetInfo)); " w; S: U7 N" g5 N3 _. l# g# S; q thread.Start(); : I1 @ n$ L0 A* |# c } : S! w1 b- q2 q) I- C3 C9 U! Y1 Y 有了上述准备,我们来编写确定按钮的响应代码如下: 2 z" R+ H! F" W+ i% x6 n' ]) xprivate void btnOK_Click(object sender, System.EventArgs e) ' [. M" v! N# Z4 w5 b { : h3 ?# _* Z& k" h/ g$ R6 a, D; w //根据组合框的选择,得到当前用户在DataSet中具体物理位置 ) q+ }4 ^, `3 @1 c" a if(this.combUsers.SelectedIndex<0)//没有选择 3 d" G# _6 {% j- [ return; w: l. Q6 m/ | DataRow rowNow=null; 6 ]. f& [! a& k- y9 q9 T6 ` foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) . n9 ^8 }% {/ g5 r9 d { ' }3 @( I/ S7 X$ `+ @& W7 s0 o7 V if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) / w* S/ P1 _) G$ a" C a# W8 z! ^ { 8 _, a8 |/ v: t( `/ o, x rowNow=row; * x: {" J; P& ~ break; 3 o: h( L. Y2 V4 i$ C } 1 @+ V/ I' d+ m r' H3 s7 Q% I9 q } ( Y9 x% o. P1 [8 k if(rowNow==null) 1 J! \' b# L! D# N' O6 w, h return;

//获取当前正确密码 - z& W. U% B1 g7 h string strPassword=rowNow["密码"].ToString().Trim(); 9 B/ g" v6 {! `& L this.txtPassword.Text=this.txtPassword.Text.Trim(); ; h) } x) ]& T' f! x8 o+ @ if(this.txtPassword.Text==strPassword)//密码正确 / E' y5 B2 O8 s4 [ {

//主程序名称 $ r' z& [; ^4 F% f; D string filename=Application.StartupPath+"\\"+"MainPro.exe"; - {! c' @& ]4 o //参数名称 $ i" u2 b* Q8 I0 E) e string arg=this.combUsers.Text+" "+this.txtPassword.Text; 9 P( ~( Y5 t6 m0 @0 O //运行主程序 ) X; C, S3 M0 P System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

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

//#define NETWORKVERSION

using System; % |* a c1 s/ T& T using System.Drawing; O( ^6 R% U0 Z' K" x using System.Collections; ; ?$ ?) k. C/ |3 O7 K1 b9 G using System.ComponentModel; , i: M2 m" }% Y- ]7 p. v; H; rusing System.Windows.Forms; 4 G# T/ N; ?1 ~/ Y" H, c) nusing System.Drawing.Imaging; " J5 A8 g# `. u3 e% k using System.IO; 2 [' A. i# n( C! t2 `5 N using System.Data;

#if NETWORKVERSION * u* M X5 z0 U( E0 O% }using System.Data.SqlClient; E/ H# G1 \! u4 z! d2 ^" g #else 2 p5 y/ n( |0 m! ~$ q4 d( Fusing System.Data.OleDb; : \2 s3 ~( `8 ]#endif # y: a3 Z& P$ q& a1 {9 J: ] using System.Reflection; 1 X$ @8 ~$ M& W2 ousing Microsoft.Win32;

namespace OA 6 I( W) `& H& b6 ?{ ; x+ q( b9 E. @$ t }) C public class Tool * R! h1 s5 [0 ]# | g { 0 [. U# ~. _( X' Z public Tool() 6 O) ]3 A# O( v4 D { / E8 U/ z/ r& ]% i1 ^$ A. p8 b9 N. S } : u Z. W+ `& ^* Q( W /// <summary> # U2 \8 z! ~+ |3 W: f7 g7 S& k /// 主程序的文件名 . n5 r8 U( w9 N4 h2 B! I- B" o6 [ /// </summary> % x1 ^' i& M2 a3 t3 {/ A public const string FileName="OA.exe"; 5 k3 j- A- Q, e# Y/ X! w& n; t, q public const string g_TitleName="丽汽集团办公自动化系统"; 2 W0 r: k7 A( _/ J. [ public static string g_UserName; ) y9 w9 y% Q0 S% ^6 S public static void WriteInfo(string p_keyname,string p_keyvalue) ' H! C! k& N, l { 4 r) y7 N7 |3 Z$ a; P( N. X3 X …… . ~. f/ L Y& p" l } # j* U- I+ g. k' Z" l$ O//其他类似代码略……

} ( ]1 o! }9 t: x" U# V! G" A } ^+ d) l4 ^5 u- ] D; ^ 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: 4 S! _! ]' }! R, Z: q在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: * c# ?+ r5 P3 k% Yusing OA; ! ^7 X! c$ ^9 ^4 j- Mprivate void myFun() ; o: A8 W* j/ ?7 R$ `: s; Z& @* ? { ' q" Q; t# |9 n$ T' [ string s=Tool.FileName; ' \, W$ J9 p" D% `& r4 b } ( P9 N: U$ A; w" w) Z+ g* R 如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 : O7 r) x& V. Q$ ^3 f' K6 x 我们还注意了一个细节: 4 r7 i4 o3 r9 _1 T4 R! E) Y ///<summary> . _$ d" F" Q0 B# ^2 z ///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 0 R2 K' h, T; K- b/ w4 w///</summary>

//#define NETWORKVERSION , [" _, A' @% y2 {! { 我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: : |) S5 N% u2 ]; h3 I- g" g /// <summary> % W6 q+ {, h! p6 S& ^$ l0 k8 k /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 $ J' [, W/ [& T" X /// </summary> # f& a+ N$ t% B* g# b. A" \" ~! Y/ w /// <param name="p_Sql">查询用到的SQL语句</param> 2 I% p6 f) t* T; b0 `0 T /// <returns>查询到的结果,没有时则返回空""</returns> & G( t1 N( d; |8 P public static string GetAvalue(string p_Sql) 5 l0 Y( _5 D8 R, q* g& ^ { % Z5 ~ F2 y% h4 K string strResult=""; / b9 P3 X i3 C; b3 Z Tool.OpenConn();

//设计所需要返回的数据集的内容 8 v1 h2 X, [; W/ O/ L try ; j: V/ p8 [6 w { / v# S' |/ H, B0 d // 打开指向数据库连接 * y: b* ~) x8 m: s; c3 Z }#if NETWORKVERSION //网络版 3 P1 U9 {. P( \ SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; / w0 r2 \. J+ Y; q" j SqlDataReader aReader = aCommand.ExecuteReader ( ) ; 9 [1 M a: |2 ~' H#else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 , i8 W7 l" r" [& l OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; ; l; ~8 u* D& P0 b- h3 I3 L OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; 3 K& Z) S4 O* j" h R0 p$ K x( A #endif + [! h7 H" x+ i5 g+ I5 ^ & v1 }1 \" X) x& i- ~" Z" j // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 ) R! l3 Y# K0 G8 a, Y if(aReader.Read()) 3 Z1 b8 o3 }+ {3 y* K* q strResult=aReader[0].ToString(); , w4 x. E8 g5 I: o aReader.Close () ;

} 3 {# z7 c% ?/ }) B3 v8 h catch(Exception ee) " j4 L$ u+ v3 i6 [9 H1 Q, l# N { - K4 N6 `3 }0 U6 P1 y8 s MessageBox.Show(ee.Message); , d0 k0 A# P f4 C } * h! s% D/ l% ^# K) B& ^; O, ` return strResult; 2 n) l; e# Q! k* V. o& m& U } " |6 X4 T+ X4 S$ c. M2 e/ t 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2025-1-19 03:13

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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