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

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

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

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

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

×

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

. z. _" }5 s7 W2 B) y2 h

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 - ]3 R+ a3 u7 {' f5 e1 s- U 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 + W- N( @+ g- s+ {: l T) d% v" h创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: x, v5 Q$ ^' h5 b: ?' A /// <summary> % f$ \) C+ a2 l7 b /// 应用程序的主入口点。 . F: ~4 r% H/ o. ~7 {" V /// </summary> 5 ^' Y+ L: L% k* M [STAThread] # X3 g5 r) v! V: B static void Main() ! D+ m, e) N( f7 a$ C V, z) B" f. e { " h9 h0 T3 c" K1 F) m Application.Run(new Form1()); 6 l1 t: _2 A, S9 Q/ j" @ } % k; l# ~* N* N5 r7 ]; d0 U) } 为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: 5 U& K. a( C5 j private static string m_UserName,m_Password; + g7 g. d7 _2 c/ x$ x /// <summary> " q/ }1 o. l# \ h! U: D0 P /// 应用程序的主入口点。 ; u t l, X/ i; Q$ a /// </summary> 0 h2 s" _2 b5 h4 [/ }4 a [STAThread] # a; P# R9 }. c( y; B Q& Q: }/ _4 j1 B! y static void Main(string[] args) 6 M# a' b# K8 g. P4 I8 B { & O7 f/ y# s0 i' M" y0 W p if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 1 h/ ~4 Z! r" ?' C0 Q, S { # k+ R a( k9 f8 y% l, J' C //记录下用户名和密码,供软件使用 - l" M; }0 f$ r8 K4 I+ b5 F m_UserName=args[0]; & C/ `: N p. J( Z( O3 r! r5 Z$ S9 I m_Password=args[1]; ( F) s s) k, t3 |; ^8 ] Application.Run(new Form1()); # f+ k% @2 m `- m9 r3 {: Y2 u } 7 y7 L K$ j, A# D3 ?- b* Z/ t2 z else 3 k' ?# _9 v$ c0 o. C { 2 a6 M# ?" X+ g: G1 z MessageBox.Show("不能从这里启动"); f6 _& j: ?' M+ ? } 7 j5 \) V$ c( r W } 6 Y0 @5 @; k! Y" f1 s) k' `% `) Z为了显示登录是否正确,Load事件的代码修改为: & y# r+ Q$ P& p1 }4 a$ x private void Form1_Load(object sender, System.EventArgs e) ( R2 h2 ?& x5 z) M4 i9 N { 1 j1 @. |% u+ m- b& T$ l% R& z string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); # y" r/ C- s. c! H1 U @ MessageBox.Show(msg); 8 ^0 Y5 X/ u1 T2 \0 R } 0 |+ N0 O$ l) U; z- r" V# s# l这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 * w7 l0 E2 y8 l5 K/ n% J2 ~$ _由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

/ F) Y1 g. `! C; y6 t

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 6 U. Q8 Z3 o% a+ }8 k# P. t在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: * V$ w9 E! R( X5 }0 p5 o( ?操作员表 版本表 1 X# J: ?0 ^/ @6 e' S 字段名 类型 用途 字段名 类型 用途 # I* r, \+ _3 r% J3 p$ h 序号 长整型 主键 序号 长整型 主键 - D8 N1 Q& p$ J. E3 p4 X姓名 字符 用户名 版本号 长整型 存放当前版本 + X6 k+ h: `* Q6 ]0 T+ V' { 文件名称 字符 本记录对应的文件名 ) r$ ?/ _6 Q8 H 密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 * Y( P) F9 O/ ?5 p! z& g; t8 O 我们手工输入一些用户名和密码,如下:

# j; x; I, N/ Y0 A9 b

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

9 |( B7 E: |5 i+ R' t/ m

点“确定”,同样,配置输出路径为D:\output。 : {% A9 j. q% ]3 N+ G5 u 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

6 k: D* u m- \; y$ Z

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 * Y/ ?1 o/ t5 I4 a" h添加浏览按钮的响应代码如下: ; [$ t, j& E8 L H' T private void btnBrow_Click(object sender, System.EventArgs e) 3 m" \) T) Q( M" D) f B { 9 z0 c$ y/ [. E" |0 `9 i) ] OpenFileDialog myForm=new OpenFileDialog(); 4 @) _3 X9 `* q: g/ D myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; 5 I' s/ B% ^$ j) h if(myForm.ShowDialog()==DialogResult.OK) 9 f/ V. Z4 o, s+ R! G3 r) P { 6 J; N' a5 [& \6 j3 q/ @- X this.txtFileName.Text=myForm.FileName; ) i" V" O% \) }4 m6 t v } 0 _* N4 O+ G7 S. E% g0 z } 2 m( y" R4 K2 i) x* ~2 i- {该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 : Q$ e' r3 h, w3 x2 g4 v 添加取消按钮响应代码,目的是关闭窗口: # B- z4 N* x% s, s" u8 W4 x/ N private void btnCancel_Click(object sender, System.EventArgs e) ) b3 {" O4 _' U( R { ! B N2 p, o+ ? this.Close(); * F4 k0 }8 ^! Y1 N# ^( \- G } / ~: l' C+ g, q) m0 ^ c7 O 添加两个引用: 8 W5 E$ [! \* a' ?3 D# U5 [ using System.Data.OleDb; 5 f7 W( b: L, p" h using System.IO; 9 U$ i. j& U |) h! R6 a0 r4 h再添加两个变量: ! C: s! [' W9 w; `" I) u8 H private DataSet m_DataSet=new DataSet(); + e* w2 C) H7 }- s# {& R* B private string m_TableName="版本"; & r! M: g7 g1 R* d7 I( E6 ~" D4 d 下面的函数去掉文件名中的路径: 8 w1 {; o/ }# ]/ O6 J. B /// <summary> . g9 @/ r3 w& y2 h. u* V6 B /// 从一个含有路径的文件名中分离出文件名 / Y' l! s. ]4 E0 a /// </summary> * M: Z1 ` {) D( `# e; H3 ]7 m3 }( Y" e /// <param name="p_Path">包含路径的文件名</param> + f' c2 m6 C. A7 j /// <returns>去掉路径的文件名</returns> ; J" [% | A: |, z/ S private string GetFileNameFromPath(string p_Path) 6 C5 t( F' m0 R) N3 S* q* {+ I1 s { 3 C3 R% N2 I5 Q2 H* D" x* p+ _ string strResult=""; ! d- Y/ L+ E/ X/ [- {7 @" S7 B5 H. B int nStart=p_Path.LastIndexOf("\\"); & H' C# D6 O# d) @ if(nStart>0) , ?4 v" ^5 \: R* l$ R0 Q { ! y; U7 _/ F0 k+ t. ` strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); ) C2 _: o; A5 P2 b& a+ R } * n$ M; D7 F: `/ d) d return strResult; $ \+ A. o& n1 D& u" ?" o } 3 m# {2 j, ^5 Z+ }9 T9 j% X2 f# X 添加确定按钮响应代码(含注释): + e4 I/ O- o/ {9 p! oprivate void btnOK_Click(object sender, System.EventArgs e) - w% }9 y) I H/ S8 j$ `& }7 ~ { 1 n$ y& m( s+ K# Y2 _; e //检查版本号是否合法 / }( c! E3 P4 O: h: t try / ^5 Z& B6 X% g% m( q { - @1 y* s& w2 Z5 E Decimal.Parse(this.txtVersion.Text); 3 n! P1 _5 n! S; `, f } + Y7 K, z4 r' w8 q/ L( x, ]4 Y catch 0 h; i. U% O1 m/ M" M; ~) T { 9 v0 O4 x" @, ~) a# ` MessageBox.Show("无效的版本号!"); # p: S$ P, S9 i% t6 K z this.txtVersion.Focus(); % Z1 G. [3 V8 |3 l this.txtVersion.SelectAll(); P- s& p4 I0 Z0 ^8 \) L- k return; 2 |* A7 Y& X8 R4 m+ P }

A1 Y2 u2 U% d

if(this.txtFileName.Text.Trim().Length>0) 7 F3 J6 f) U) E { ( b2 z% [- y, ?' n7 c) g //检查文件是否存在 " F5 _( m9 n# I if(!File.Exists(this.txtFileName.Text.Trim())) 9 x! Z, X/ d/ m' m; W5 q { 6 M e, [7 F- t: t* {$ R6 T MessageBox.Show("文件不存在!"); + Y) R* V+ e2 _1 i" L/ J return; " @, G7 L/ \: ?# C0 {9 L }

& K/ \ Z) A+ R/ i9 N( h

//连接数据库 9 E+ q" j# p1 |" e. f; k$ i string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ + ~" c* K! F, j2 e6 J Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 9 f: d/ x6 m( A' F5 w OleDbConnection myConnect=new OleDbConnection(strConnection); $ _2 c* [& y( P2 ?% M" i( R" V+ \ OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); 4 I0 r8 _" P! M. [ OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 9 o& W; n$ h' d, C/ [ Q myDataAdapter.SelectCommand=myCommand; 0 b' s/ A/ ]! i: A6 l4 V OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); + x6 [$ p3 d0 J( {- Q myConnect.Open();

5 m4 C( m; N% q# ~& z

//获取已有的数据 ( L& s. x8 E0 v x- y m_DataSet=new DataSet(); 5 ~( D' v( V. i. v; \+ h( a. d/ g% k7 v try 9 [* _( Z: F! h { & U; u1 O, Y4 C* C i" @7 o myDataAdapter.Fill(m_DataSet,this.m_TableName); + f6 O8 A z% _/ N //如果是首次上传,则增加一条记录 - a( I! g+ o7 E7 P- n if(m_DataSet.Tables[m_TableName].Rows.Count==0) , [2 w0 g# Y; c { Q$ J8 S- b' ?" { DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); 2 X$ ?4 T" e. Q. @# N" \ newrow["序号"]="1"; . D/ i) s' g3 z! i1 Y m_DataSet.Tables[m_TableName].Rows.Add(newrow); 2 q& N9 Z- r0 l6 O4 ]% T' _% z% }4 z } / e* J' W ?! R" \; } 2 y8 _3 i; p/ H/ G6 ` DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; 4 d) {" g, \1 U: f //填入去掉路径的文件名称 / j0 T5 r' K4 x+ g7 d2 R% E& W row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); 7 G' E$ Q! X; }9 e8 t //填入版本号 3 G+ Y- a$ W% I- W5 r2 G. y4 ? row["版本号"]=this.txtVersion.Text.Trim();

$ L9 _5 e6 t5 W/ V

//将实际文件存入记录中 " j r$ x3 k, [7 ]/ {* D; s FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); ! l( {, |% e2 ?$ y/ P; V& z byte [] myData = new Byte [fs.Length ]; ! u! s; r8 }- Q4 }$ C2 k* Z fs.Position = 0; 8 h& s# v4 I o; O fs.Read (myData,0,Convert.ToInt32 (fs.Length )); ! m( f3 w) T9 G# g0 t row["文件内容"] = myData; 0 I6 K7 A9 N: E, e fs.Close();//关闭文件

+ N2 m& S/ }' N. x) a3 @% M ~

//更新数据库 9 r( q$ E0 ]5 W% B. N myDataAdapter.Update(this.m_DataSet,this.m_TableName); ! `8 k9 _* N2 B, l! f myConnect.Close(); 5 d3 w* X8 p" u$ z0 I* L MessageBox.Show("文件更新成功!"); " t7 f% k8 Q G+ Q9 V: r2 X; x* O } 5 `* ^: F/ L0 Y catch(Exception ee) 0 y) n5 c7 X5 [6 D% d$ [! N" X, R { t) y1 g9 L3 I, M/ P1 U* i MessageBox.Show(ee.Message); A) O3 p; s! u( p6 j* g } & o+ ~3 e n. S0 Q+ U6 B3 F0 I# O 5 k. Z1 C, S' \) |8 T6 w# y } v6 B- `8 b* M" b else * [/ \3 T$ m( ^, S0 {: x { * }" o8 Q! z) k4 u: I- C& D MessageBox.Show("请输入文件名"); 0 o) p8 ^$ N, B0 P } 5 l1 H% W T9 z: T1 m* @( U4 z } 5 b6 A# z& z# A 至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 ; X, ^! N' E( r. K* ]; U 最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 9 u- {( {% d4 ^* I! F5 o- @4 N 添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

8 [8 o' g5 b' U9 R3 W

切换到代码窗口,添加引用: + W0 D) u' U* r" a b! l1 @ using System.Data.OleDb; " l+ j/ Q+ y0 s2 O8 L7 Z% t3 s using System.Threading; : p( y" D# Z7 g' H! o) k9 \ using System.IO; ' C: t; p1 f; K; R Wusing Microsoft.Win32;

! \9 M# u. r" E

再添加如下变量: : t, j& c) r$ f, N4 e( v /// <summary> J9 E3 o" X: N) S0 [; f3 K /// 存放操作员及密码的DataSet 1 T u( k/ H# I. L /// </summary> 9 p3 M9 W t& x private DataSet m_DataSet; # T* ?4 U2 Z7 J4 W8 `8 ]9 e /// <summary> ; ^, A5 ]4 r q9 b z7 [ /// 本功能用到的数据库表 2 {0 v4 P* I) M- ^3 D% b /// </summary> " r6 w$ F/ i/ ]- Q' l1 ~" R1 | private string m_TableName="操作员"; * c# J" F; ] @1 U g# m private DataTable m_Table; 6 R& i4 z* T8 E4 e1 m) h; _ } 为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: $ _6 @/ F( G2 w1 q+ ^7 A /// <summary> # p j/ K' p. K- ~' P' f: T /// 定义本软件在注册表中software下的公司名和软件名称 ; v% l" @% V: S3 i: A! R# M4 w /// </summary> 7 _6 X# m3 C4 T8 V' i, ^ private string m_companyname="lqjt",m_softwarename="autologin"; ! V9 f% L+ E j" A4 t& C /// <summary> 2 z6 x& T1 Y4 w+ E+ J7 w8 x /// 从注册表中读信息; : \! z; c, w+ b- a% n: Q) W /// </summary> / q. z# }: J8 l( N1 u /// <param name="p_KeyName">要读取的键值</param> % T* U6 z `6 ~+ c$ z" V /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> " ~ k( v1 k2 ?* n8 x. _. I' B private string ReadInfo(string p_KeyName) ' d8 U! q: L6 u8 R3 G: r7 _( ? { - k) C- A( w4 R9 O RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); $ I! L! {8 U O, O' s0 ]. J/ A RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); 1 N; k3 v& m1 I9 ] string strvalue=""; % R) Y- B9 K n' } 8 x$ ~$ }/ M# ] if(CompanyKey==null) ) V+ P0 h3 o! \$ T Y4 s, H return ""; 8 R- F' ]6 P* M; J, j9 v RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 & H# \! h7 {5 ~- u8 ^ if(SoftwareNameKey==null) 1 T ]7 ?# q C, ^$ B return "";

- k/ `: C5 r/ `

try # _& F7 e# t- g- V7 _* I { 8 R1 W# }3 J! w( a( W strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); 7 A0 Z9 {8 n2 v0 G( Z4 M2 D# i" A } " m1 U9 n1 Y+ Z0 i8 a. F- w catch + n, Y! t* P9 M {}

2 @& U6 w& p( Z

if(strvalue==null) ( S7 j+ c! h* f strvalue=""; & I: {9 s5 n% z, K5 A; E+ }: I return strvalue; 2 K6 p* }5 H% ~" d# K9 h5 [ } & J6 D1 G% k3 Q4 H7 a+ X" B0 ? /// <summary> 8 _! k8 ^0 t4 @- `: Y. v+ F! M0 c /// 将信息写入注册表 + j& l. `) r# C! \2 |: [9 x4 o3 o0 `+ F% A /// </summary> ; c. {& [/ {. K /// <param name="p_keyname">键名</param> : a1 b9 p9 h! q9 p$ r. |" O /// <param name="p_keyvalue">键值</param> & z9 N5 f* t( R7 g private void WriteInfo(string p_keyname,string p_keyvalue) 5 c% L9 [' I- o2 R3 n1 J { % H& u k4 G4 T/ S4 _ RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); 6 ]( g7 [1 L1 N4 ^ RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); & {+ @6 O3 g# X RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); ( L9 j% D& s: `0 R; O6 y" R5 _3 \3 n //写入相应信息 & U- _! T* ^' j- }5 P/ o8 r- E SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); L, u5 R/ t4 P4 ^+ P2 O- f0 |8 R } , m' }/ t \% L" H再写一个函数,用户来获取用户名/密码和更新主程序版本: / G, J ?0 e9 k5 B8 ^/ b /// <summary> 9 B7 ?3 f! Y; i/ T /// 获取操作员情况,同时更新主程序版本 3 F% o5 r5 q: [3 ` /// </summary> " r/ h. f8 B( b |; E0 Z2 } private void GetInfo() g3 B% `8 u7 | { ) h3 {, P6 w. ~# n! |% q this.m_DataSet=new DataSet(); 6 X+ W/ x1 L$ O8 q9 ?9 _ this.combUsers.Items.Clear(); $ {" g; ~8 z3 M/ G0 k- X6 B: i6 K string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

d! C4 U+ ^; F; D1 X4 P& a

//连接数据库 - @( ?4 R0 y2 C7 K3 s3 Y/ K, Z string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ ! s0 ?+ ]' A( s- R4 [) ` Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; " V9 n7 P& h( Y OleDbConnection myConnect=new OleDbConnection(strConnection); - w. C3 u |5 {- B. ] OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); ! Z$ U3 v9 \- V* Z" ~ OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 3 W) m6 M9 h$ B" W2 O, F } myDataAdapter.SelectCommand=myCommand; 2 }. D7 a$ y$ D; q" h$ [3 M# Q, U try " `1 i/ V8 E! P9 |1 p2 { { " `4 U. z6 z$ E/ B myConnect.Open();

& \! w, W6 A1 z0 A

//获取操作员信息 / m# |% @; E }# P5 @ myDataAdapter.Fill(this.m_DataSet,this.m_TableName); ( h4 t$ c; B9 M) E //将查询到的用户名填充到组合框供用户选择 ! e& m( V: R4 c- _' f4 B W this.m_Table=this.m_DataSet.Tables[this.m_TableName]; ; u3 \0 A y; _7 A foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) $ ]2 w6 S$ j9 F2 b { 5 l" o7 t" C' G this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); 1 u( y2 j; r5 T3 m }

& o3 M) [3 o% f% ~# U

//检查是否有新的版本 / D8 V1 E9 J! c6 M$ K2 v DataSet dataset=new DataSet(); : K3 I2 N: x: x1 F8 G2 P7 V& C string tablename="tablename"; # P3 K) f$ W; b9 `& C //为减少数据传送时间,不获取文件内容 + y/ |7 Q- N- z& P# q- R strSql="select 文件名称,版本号 from 版本"; |3 D. t9 Q5 ~; ^% |1 F! l myCommand=new OleDbCommand(strSql,myConnect); 6 B+ l5 Y2 |1 t1 p: y myDataAdapter=new OleDbDataAdapter(); / Y2 D3 y' H- D$ F$ i myDataAdapter.SelectCommand=myCommand; . w& o: I( k& w3 e9 ]/ \ myDataAdapter.Fill(dataset,tablename); 3 |: d; E; E- o2 Y* ?3 a$ P if(dataset.Tables[tablename].Rows.Count==1)//有文件 7 {- ^% T# d7 Z { # Y9 C/ S0 X% U( _, e string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); 4 R* p& D! V) W$ J string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); + O1 c; Q; i4 \8 K# ]. r" k //读入本机主程序的版本号 / t' c4 r7 ]/ ]2 R, x, K string oldversion=this.ReadInfo(filename); # p6 W: J& f! Q6 B2 |" ?3 X if(oldversion.Length==0)//不存在 ' ~8 k, u: V& J+ {5 o oldversion="0"; 4 g. V& ^# D$ r$ I: N5 o6 i# u! J if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 7 }0 C' o* O2 ~# J) @ { * P& ^4 X5 a) D //取回文件内容 ( D* Z% A" r# X( w# @* d0 a3 B dataset=new DataSet(); / Z* l: Y' h e' c' c9 [; R- s/ } strSql="select * from 版本"; 6 u7 W+ P5 P4 s! q# Z4 r9 U8 Y myCommand=new OleDbCommand(strSql,myConnect); C! E9 a5 U6 f( d7 N. ~ myDataAdapter=new OleDbDataAdapter(); & {+ K6 r8 E( Y* i6 D9 H1 _ myDataAdapter.SelectCommand=myCommand; # C( L$ M3 P+ d; s& ^ myDataAdapter.Fill(dataset,tablename); + c$ E- G; V" x9 z //将文件下载到本地 7 i: x/ |# T1 z9 o. `) t DataRow row=dataset.Tables[tablename].Rows[0]; , o4 L2 X, a2 F% q" ~ if(row["文件内容"]!=DBNull.value) / ^9 B( i0 Z; ~" `' w {

- A5 Z2 F' I$ ^! D* j

Byte[] byteBLOBData = new Byte[0]; / m# [# e+ C0 }, H7 x6 o5 L byteBLOBData = (Byte[])row["文件内容"]; 4 D; {8 y3 K7 B. q5 c6 j) x try * I/ Q4 R: u# N4 P+ o2 W, \ { ) I9 u1 K2 p) A0 ]6 d FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); , \, l* N8 L: O$ k% ~4 ?4 `- \ fs.Write(byteBLOBData,0,byteBLOBData.Length); % E( M: {* N! t* U, s0 L- Y) o fs.Close(); 6 R, L# }4 g" |: t8 G( k! Z- \ //写入当前版本号,供下次使用 2 E4 W8 Q) s' f this.WriteInfo(filename,version); * Y$ p( a6 x; P9 S2 e4 ?- | } $ E: d: h: J1 {' ^2 ] catch(Exception ee) * z1 ?2 c" i* Z. l! C. x% L$ U5 M0 T { ! B7 E8 G" F6 O" ^& E: Q6 U, ]3 p MessageBox.Show(ee.Message); ( ?+ a/ `- k+ I& K5 @6 d } . K) A8 w: e" H8 g1 J }

, z" J7 \, ^3 o" I Y" o6 f

}//有新版本 . b& E' ~ z3 Q* H }//有文件

9 }4 R; c: G) }

//关闭连接 , d, d0 Q' ~8 U2 L% u4 ] myConnect.Close(); . X% k, }4 f" F' z2 l l+ o+ p } 0 A' k. A! y% e; Z/ ^ catch(Exception ee) ; Q9 c! P. P5 ] { 8 m% r4 O5 n( k6 J: T" _ MessageBox.Show(ee.Message); / L/ v1 O: G: N return; ; Z) ]( g$ y3 n- o* I, D } . j- G1 b& L% q% O) u! l //允许登录 : F, X0 `1 E/ Y: w9 `- {! | this.btnOK.Enabled=true; . V3 o* P6 K$ T1 e& _. y I0 A, R } ) }3 c4 O. n1 D6 _& {

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: 1 W; i$ t: u) `$ y% J private void Form1_Load(object sender, System.EventArgs e) . `+ B9 J9 L9 S8 b4 U" a { - [( [1 f$ _$ d! F( }3 h& N; \( D //为加快显示速度,将数据库连接等放到另外一个线程中去 5 P! A' p S+ [+ A2 p+ H* z Thread thread=new Thread(new ThreadStart(GetInfo)); - j: R' Y% U% R" p; p# ~ thread.Start(); 0 G3 w* o W; p: t- g3 g, k' F } ; m+ l% u. w- n) ^有了上述准备,我们来编写确定按钮的响应代码如下: $ X) k* b+ a/ G' Iprivate void btnOK_Click(object sender, System.EventArgs e) + f" q$ e0 E: x) F { 2 _" Y% q0 {# u7 y //根据组合框的选择,得到当前用户在DataSet中具体物理位置 1 A, i8 B) D! J5 a9 [7 x if(this.combUsers.SelectedIndex<0)//没有选择 9 j( x; N, m+ X; E return; ! U; y: \/ W9 i! u2 Z2 ]: d DataRow rowNow=null; , T2 {, c' w" \ i foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) 0 u5 m- D# m1 S$ S8 ? { 6 |8 o/ Y1 w$ S3 a1 p. o4 v if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) $ x3 ]2 w% i( ^4 I { 1 B0 H. [8 I1 N5 X: @ rowNow=row; + @9 E6 _, u, L% o" b# t- W& r break; ; R" w9 G8 g- I4 Z c } 1 f" z, t& O' \ } : C, @& p4 [9 c2 { if(rowNow==null) ' Z( ]8 \) [& `( U% g1 O return;

//获取当前正确密码 7 L& U* k8 f3 ]/ X string strPassword=rowNow["密码"].ToString().Trim(); ) p4 x. \, o, r5 Y this.txtPassword.Text=this.txtPassword.Text.Trim(); 0 ?% C' u7 N/ P# I7 J if(this.txtPassword.Text==strPassword)//密码正确 ! {1 M: Z3 F$ q+ R0 m5 K+ y {

//主程序名称 6 ?; l3 e: X0 {( \: K string filename=Application.StartupPath+"\\"+"MainPro.exe"; I% \6 a5 f6 E: T4 M( ^8 A //参数名称 2 ?% \( C6 i1 u. {$ D* E string arg=this.combUsers.Text+" "+this.txtPassword.Text; * P- ?; I$ b2 p$ D! p //运行主程序 8 ]& f& l) t; q System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

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

//#define NETWORKVERSION

using System; . z4 |/ o+ K/ }& q( A+ e9 susing System.Drawing; + f" C) L: b% K0 G! W/ B: |) C using System.Collections; ! Z. g( Q% p/ u$ D5 m' V& v using System.ComponentModel; # b) c* ^- U* v6 K8 b! Z1 [ using System.Windows.Forms; ! _" N/ L' f& v/ q0 | using System.Drawing.Imaging; ; f( W ?7 @7 h6 P( _1 w, F4 }using System.IO; : I) Y9 `# m; l8 R using System.Data;

#if NETWORKVERSION 1 \. j! S7 s% L# H# uusing System.Data.SqlClient; 9 A# M1 k% J7 q) b, i W#else , T- o9 i) M6 h O using System.Data.OleDb; , v! k. W# S' k" w% }2 @ #endif 7 n, M- z O+ }1 j. [' b using System.Reflection; M. @3 Y# M3 I% g5 dusing Microsoft.Win32;

namespace OA " T; I: V6 b% s3 o" f; ~4 C% t# @ }{ 7 ^8 y7 t) `' o* m1 v public class Tool : N: `, _+ q, [4 x* a8 h { 5 x7 Y" d6 {: R' @$ u/ W' L0 k; D public Tool() 8 A& p# `; ?) a) I9 x4 J { - l$ T) X, o$ o1 B; r } ; A4 s$ {# r: \! ] /// <summary> 0 q* h9 I2 G% {5 X /// 主程序的文件名 7 O- f" g' y9 Y, Y; Z /// </summary> - ?+ }6 L8 [# f) ^4 D public const string FileName="OA.exe"; ) A: E n% l. |$ D7 i9 v* A public const string g_TitleName="丽汽集团办公自动化系统"; $ x9 o; }' l$ V1 X4 ` public static string g_UserName; , ?; N8 g3 ~) L public static void WriteInfo(string p_keyname,string p_keyvalue) ' H1 x8 n0 I7 C# \# \ { 8 e( V9 y: F p0 l …… 9 t C5 F# V# R4 ?! J. X } + z% c1 ~) C, O3 Q/ d//其他类似代码略……

} - ]1 r/ O* y- E( H } 4 b7 C* [" o# i; E+ T4 _7 j 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: * {( ]! r- U6 Z A: G! ]# I7 y 在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: - S' @! r; x4 Xusing OA; % u- z6 G r& J) `5 m4 r/ vprivate void myFun() : v$ ^9 Z3 Q0 Q{ ( N% b! h" Y- L/ E4 {: ^7 u mstring s=Tool.FileName; 0 k3 t Z- p* e/ |( F} 4 f* r4 G% b3 w% x如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 * u# D5 V2 K1 O7 s' a8 c" I! g' @ 我们还注意了一个细节: : y7 f2 K& |$ X ///<summary> 8 p! `9 h! ]- z% m: u$ h. B$ I///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 8 ?# ]! H; _; z; { ///</summary>

//#define NETWORKVERSION 4 a# Z0 Y% _: ]; n& Z 我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: . C. ~ D3 ~* ]0 A# J s /// <summary> * A7 a$ s$ J; b( ~. p( R /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 % W7 S- O! x- B5 [5 ]$ r( ~3 ^ /// </summary> ) x/ Z; [! S0 g$ P; L( Y /// <param name="p_Sql">查询用到的SQL语句</param> - L+ Q3 g5 H/ @/ P0 Y$ l! K /// <returns>查询到的结果,没有时则返回空""</returns> ! Z* D. W' q9 B: N5 } public static string GetAvalue(string p_Sql) H1 D! }. U: n { % Q0 j: n/ {, H string strResult=""; & R- [$ Q B/ A& ?% }" ^+ R1 {3 B Tool.OpenConn();

//设计所需要返回的数据集的内容 9 L" Q D5 u9 B. O- U$ k try 6 O! f/ F) U- J+ z J; |; Y$ H' S, l' ~ { 8 c1 \5 @, V; a- f' v, Q // 打开指向数据库连接 0 g7 Z5 ?: ^2 l" B#if NETWORKVERSION //网络版 7 I' e7 k) D5 g; w* f# }* q( i SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; ) k% r# B( w) i. U: V SqlDataReader aReader = aCommand.ExecuteReader ( ) ; 9 Y9 v2 A; ]; Y#else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 1 P1 i9 y# Q3 x* A5 B e OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; 6 Y, c1 Q3 k4 C( `7 c4 Q' X6 K- t5 ^ OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; * h) q$ q. A& J% @. i& Q g #endif , p% Y5 S1 n0 Q. c; g$ `( n$ d " t9 b; a; n' k# _: g5 y a4 B // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 - L) n, h) O4 @$ x if(aReader.Read()) : z7 O$ ]/ K0 I1 y strResult=aReader[0].ToString(); + K2 ~# t8 y! |( R- R; u! z aReader.Close () ;

} ! @, c' U U% Y4 x* y' a9 q' s catch(Exception ee) 5 ]7 \+ W8 F& [6 i# X, _ { 5 P* u0 ?) s! d MessageBox.Show(ee.Message); 3 ]% W- z/ M$ f2 z1 k7 w } 4 Y) S6 z! M- @/ }+ b4 }6 B return strResult; 5 k1 ^: X3 T2 l! u( I! C } 0 e1 ?, T! O3 p3 W5 s! q9 l 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2025-4-4 22:50

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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