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

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

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

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

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

×

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

0 o6 T8 \6 F: E5 D2 S6 J4 M' V

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 $ G6 Y \7 T9 E, C ? 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 9 |; n3 {( n& F. M, i2 U; L创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: : L' |4 t9 Z7 n2 j /// <summary> 3 o8 a' ]" K1 i0 l* e9 o* u: ` /// 应用程序的主入口点。 $ Y. K6 z+ Z: k7 Z4 Y6 B /// </summary> ; `# Y# O! F6 g1 I [STAThread] . o. i2 R: C8 `5 d9 L static void Main() ; \/ {1 j. z9 Q/ l$ J { % t9 f2 b6 x( S5 |% ^; y) H Application.Run(new Form1()); 4 N) f( u+ Y4 M' P/ ] } / t, K5 q, ^$ S 为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: 6 x/ k& N) _% L2 a) z private static string m_UserName,m_Password; ! r0 O" S" b' k8 ]$ K( c7 A /// <summary> # p8 ^5 e; y6 K. @- \ /// 应用程序的主入口点。 , A( s3 o. [+ ^1 c' D /// </summary> : S$ `: O1 q k; d! F- A& L [STAThread] " y/ i' L3 m. G f& J" u/ Y static void Main(string[] args) 2 h2 {, R) N) z2 O# ~+ }. e1 _ { 5 b1 b2 k' J. b if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 & x+ l! R$ t8 e5 v( q3 w3 ?: J { : R8 C, A6 }% X% W8 ]9 l //记录下用户名和密码,供软件使用 9 f4 y: @* ~9 K: x b9 t, h m_UserName=args[0]; - i: {. \0 T/ B5 R( D m_Password=args[1]; - j/ {) i- j% `4 K! |+ S Application.Run(new Form1()); - M1 T% A$ f2 h3 s0 T* H+ M' s } 3 W# O9 i. l' U: E% z else 2 C+ U2 L" A$ f! l4 b { F8 p0 \8 Q# {: v" ~# ^ MessageBox.Show("不能从这里启动"); ) L% l" {1 Z9 c1 Q! x# M2 n8 Q } 1 v/ G8 ?2 O! N- o0 n } 5 b8 @ z6 j' F3 X为了显示登录是否正确,Load事件的代码修改为: $ l& ^6 q/ |. O* j. ^ private void Form1_Load(object sender, System.EventArgs e) 4 D) }' U2 Y7 s- E @/ V1 s: | { 8 [3 {" T- @. q! I9 \! S$ F# N string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); 0 x' I% Y/ P# } U7 @! C& O0 s" a3 t) {0 W MessageBox.Show(msg); " s) T; m# A4 {5 V, u4 h } , b9 o2 e J. m F5 v 这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 8 N# e0 d1 s8 O$ L由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

- @5 r( F `& v

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 4 e, L$ M8 i9 H' Q 在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: 8 l" r3 `9 V3 r& w7 z1 E7 d操作员表 版本表 * Y8 O! |) a4 k 字段名 类型 用途 字段名 类型 用途 ) l! U8 N# h- F. E0 C- j序号 长整型 主键 序号 长整型 主键 " ]' e! S- S3 J2 G 姓名 字符 用户名 版本号 长整型 存放当前版本 / @; ]. O5 V) f$ x* N$ f 文件名称 字符 本记录对应的文件名 % P0 X D1 w( x1 O6 f2 E# f 密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 : p$ S& u" p5 ]+ z我们手工输入一些用户名和密码,如下:

7 n: J+ x% e) ^; G; k+ W

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

2 p. {: r( }9 j0 `& O

点“确定”,同样,配置输出路径为D:\output。 : Y# N/ I i$ ^# V+ N( u" [8 g 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

3 }+ q1 N! {! ?' U6 `$ `: Y) H

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 & H$ T( ]4 M9 S p' o添加浏览按钮的响应代码如下: - \$ H: d$ ~4 ]# y7 G private void btnBrow_Click(object sender, System.EventArgs e) 2 N$ { h9 p, n9 w. @ { " Q# n, u+ d0 _* |- d! q( r OpenFileDialog myForm=new OpenFileDialog(); ) \ R4 w& H, |- d3 ^2 a9 F( [! l6 e myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; ' ?! p4 p3 n# z, y" i if(myForm.ShowDialog()==DialogResult.OK) 3 w1 }& E0 o/ H6 W { / D1 q, s/ s# w$ d5 ?- g2 ^ this.txtFileName.Text=myForm.FileName; ! |1 h2 S! x2 {" |$ j6 E2 B) S } ( K! T! O; v: T- H6 F/ f } % p' i9 P. [! U2 \4 r: ?& b该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 8 @) f$ c* B% f9 P, j9 ~' z G g# ~添加取消按钮响应代码,目的是关闭窗口: 2 A8 F; M4 [8 W( I- C private void btnCancel_Click(object sender, System.EventArgs e) % t5 e. T6 @. ?, x { * p# {& x3 Z2 g+ D this.Close(); L1 g+ j- n3 p+ F- C e } ( i: S% y* E0 m: d% J6 Z+ X$ Y! ]( ? 添加两个引用: % A h o' a& i- a- V F using System.Data.OleDb; 9 {3 e0 l: B( s9 A using System.IO; # z, r, _+ g ^再添加两个变量: ( G z9 D& n/ _" ]1 q$ x private DataSet m_DataSet=new DataSet(); + c3 v" \2 L' c/ D# T; z private string m_TableName="版本"; 0 g8 N+ w2 `$ T+ X* t2 G( p, Z下面的函数去掉文件名中的路径: - S4 G' `* O4 ~ v /// <summary> : v2 D& d3 t1 F% M /// 从一个含有路径的文件名中分离出文件名 . B: c- E( p. T" K p; \ /// </summary> 0 t( m8 y+ ^. S" L% b9 a8 F2 y6 m /// <param name="p_Path">包含路径的文件名</param> 6 L/ G4 k9 ~7 j9 I% G5 u/ J /// <returns>去掉路径的文件名</returns> 8 i( L+ _' f" w$ [1 I% h" V private string GetFileNameFromPath(string p_Path) 1 D1 |' F6 P9 z* x/ O) b { 7 e, [- R$ z* o" T0 I i; S9 i string strResult=""; 3 m0 Q- _. _: k1 h% p int nStart=p_Path.LastIndexOf("\\"); " J# i* G( C. c* v9 h0 e if(nStart>0) & H* a9 Q! [/ ~$ | j- z& a, H* O" K { $ x. m2 w1 |' N& ]8 v strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); 2 G1 V3 q* o. ^) s0 g$ g" Z } 5 U( v2 `1 g& q `6 Z, q- l return strResult; / C2 |. Q% W# A2 B" [- H6 F1 R } , Y) b. e, m3 W0 h% k" w 添加确定按钮响应代码(含注释): 9 U) V6 j: _4 Wprivate void btnOK_Click(object sender, System.EventArgs e) 9 U, j- y$ ^4 |$ l/ P3 C { ; h6 D* A# T+ [. c# ]# d/ q/ x //检查版本号是否合法 1 H/ _: c- p4 X. U try / S3 y5 o( |! N% e: J+ s { * e+ \' _2 L, g7 e8 C ] Decimal.Parse(this.txtVersion.Text); 6 Z% ^* b$ j9 u; m } 5 K0 h8 S! P. M+ T7 M" t5 a catch 7 c1 M5 x* f% Q& M: X9 `/ } { : H- S# e* ]! Z8 f MessageBox.Show("无效的版本号!"); 6 ^, M; b1 x7 d% r this.txtVersion.Focus(); $ l) e( W5 m& E0 L' c w6 S this.txtVersion.SelectAll(); 6 ~5 f% w4 j( Y return; 3 p: ]$ W+ s2 L: l }

R6 g" O/ h. C1 `* Y1 s6 x" X9 n

if(this.txtFileName.Text.Trim().Length>0) : s+ B- I& [& o( C; c: e, P { 0 U7 `. Y# F' D //检查文件是否存在 0 L7 _% |$ H9 F& e if(!File.Exists(this.txtFileName.Text.Trim())) " P, z# A1 ?# H! M5 Z1 z1 {4 M { 4 i) V0 Q( x) {. _1 Q8 H! r MessageBox.Show("文件不存在!"); 3 p9 q7 Z. W1 r9 b return; 5 U& Z' W' H- u' s- r7 K' r }

% Z6 I7 \5 H0 I- {) X" Z

//连接数据库 0 j2 h* ]( z( C2 U! Z& b string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ # S5 `- ^* b/ O' e Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 0 O% h( r9 ?- i2 G4 o$ g, @6 K' G OleDbConnection myConnect=new OleDbConnection(strConnection); " Y9 [- A/ P" c e1 x, l! | OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); / O( t) t/ N( H: |/ f OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); " i$ L0 ?) |2 d+ z; k myDataAdapter.SelectCommand=myCommand; 2 s" M) \3 S+ {$ _0 d/ }; T OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); : m- v& l# Y% J6 W! g% a6 |! n myConnect.Open();

, f0 l! S) d$ k- A0 y2 F1 G

//获取已有的数据 5 g0 s+ o/ D+ Z* N1 ? m_DataSet=new DataSet(); : |: {# C7 D& }' ?& d1 O8 L try . ^: O% n0 p4 N7 ~ { . {) v# Z8 p9 f8 K myDataAdapter.Fill(m_DataSet,this.m_TableName); 3 q A# l- w0 J' q, b" r! ?8 [ //如果是首次上传,则增加一条记录 + `, |. c2 i8 Z0 ^4 B+ E% ] if(m_DataSet.Tables[m_TableName].Rows.Count==0) 9 ^0 P X0 b0 F5 Y { - c) \7 M- @4 B6 X* p7 H DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); ( b$ K* g$ E! g# t' e' d C9 U newrow["序号"]="1"; / v# U6 w6 }3 Q9 V8 u. i5 \: L m_DataSet.Tables[m_TableName].Rows.Add(newrow); 2 D4 ?* j& J2 \0 i' B5 u ] } % ?, [' a, D4 R 1 S5 b, Z, @8 M DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; 1 _* h. `5 f% y$ {8 K //填入去掉路径的文件名称 8 m+ r6 ]5 v# V) a' t row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); 2 l; {; Z! h, \& x //填入版本号 * P/ C4 b* C3 P' r! \ row["版本号"]=this.txtVersion.Text.Trim();

0 _8 d9 @/ R# ?+ e

//将实际文件存入记录中 * T' I* e0 ^; T& ~. f FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); - i$ E7 F4 l& f4 F byte [] myData = new Byte [fs.Length ]; 8 ]+ S+ p. y3 S2 E fs.Position = 0; : w; G: p6 \% [, s9 c fs.Read (myData,0,Convert.ToInt32 (fs.Length )); + E$ C1 F! M1 b: u row["文件内容"] = myData; * S8 [; _, I; V4 G( q fs.Close();//关闭文件

3 K! l8 h' e3 [6 W8 o K

//更新数据库 3 l0 p" U2 ?, H" v; K myDataAdapter.Update(this.m_DataSet,this.m_TableName); 5 H% p% j7 u% n1 R+ l myConnect.Close(); 7 c/ m, Y/ ]. _( g+ H3 e# |$ Z MessageBox.Show("文件更新成功!"); ! Z( T7 R1 b5 B } ! }3 a6 s" t/ Y4 b% T( a0 n catch(Exception ee) ( y6 Y1 k! j q- R& U& g5 I* | { * v' \! M5 i& B) A MessageBox.Show(ee.Message); ( q7 C7 d7 R( A } 4 z: w! t' d( f7 Z$ y ( {: b6 }9 N$ R$ F3 q6 e# p2 H } / f) q; I( a8 u; k# I1 d2 c else + v6 @$ h. H. U+ \6 o! n- A9 Q { 5 d) x( o, n# N* g1 r2 E. ^ MessageBox.Show("请输入文件名"); / w% ~/ W6 w I7 x } ' F1 I0 J: {3 L/ y I/ @9 P } ; t9 p7 k+ W; g5 k1 m$ W1 e! H至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 2 G, [) y; l4 J) F' @! g* A9 l 最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 8 {( k" D4 l& d5 j6 O7 A4 s3 m添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

( P! {+ c; O# m% Q- H

切换到代码窗口,添加引用: * f Q( ^$ V: s7 Z( u0 Wusing System.Data.OleDb; ; e8 _# P5 J' d6 D0 A, v: }using System.Threading; r1 V" ?$ B _5 B, w using System.IO; : Y4 ^/ H, G7 @5 L2 Vusing Microsoft.Win32;

+ ~- T! Z$ s2 ^% R1 ^" ]* y" Q

再添加如下变量: 5 r. }7 D: x7 u+ c6 ], r0 H /// <summary> 0 G7 w2 {' O+ y+ n6 e- g# E /// 存放操作员及密码的DataSet ( a) H- _$ {- s; f: U: j /// </summary> & Z3 V8 y- k5 u. L( Y private DataSet m_DataSet; 6 L3 d. o( t7 B& l /// <summary> 5 t# x3 L, @0 d /// 本功能用到的数据库表 % [% f$ C+ U7 I# y$ d" b" Q5 T /// </summary> & {: n8 b: E* F: Y. U private string m_TableName="操作员"; ; R8 U" w3 D5 B& ]- \" A private DataTable m_Table; " D' f. E# [5 I+ f5 _: j7 H& P 为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: + m3 \7 P$ L5 h+ o8 N- g2 r! t /// <summary> , U' x$ y) F9 a$ U) @ /// 定义本软件在注册表中software下的公司名和软件名称 + {& x& b7 p b% G+ R2 Q" @' d /// </summary> 5 q1 Z$ }3 t4 S1 n* c P6 } private string m_companyname="lqjt",m_softwarename="autologin"; 5 y" B5 C0 p& e /// <summary> 1 U1 _1 M0 D' l1 W /// 从注册表中读信息; ' ?' x1 i. A% _3 r0 ` /// </summary> : S9 Q7 L9 h _: j /// <param name="p_KeyName">要读取的键值</param> ) _1 ?2 @8 x( N4 G+ E' p /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> . M7 G* @! \+ X6 }, A( { private string ReadInfo(string p_KeyName) . X8 X# C4 u0 i7 b) I { 2 ~6 b8 `# I4 ] { L RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); . i6 S9 W" [7 D% d. [+ q RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); & {1 K! _1 m* P* @( f" f string strvalue=""; 7 F/ [2 ` o' \" g$ l& P H' g 3 d$ ~5 g' Q; f. f* R# ` if(CompanyKey==null) / f/ G' X* {+ N- |; y/ f return ""; ' M5 r+ f2 h" W RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 ) C( \, u+ b) N, b/ o6 F$ J if(SoftwareNameKey==null) & Y5 ]( ]0 B6 t return "";

. n. e- S6 h$ b5 t0 [! ?

try : D1 p& G" t4 s3 n5 E& O. z9 s$ ? { 9 n6 _5 t7 C |- n* j' I6 R strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); - ?* J& D# X$ ], ] } $ ^! q, {1 H, h9 m; V% Y catch # g6 p( \6 {& I {}

?. x+ E& W1 u0 w- u2 N) F

if(strvalue==null) 2 E7 p+ C% p1 \9 d* H strvalue=""; ) h( g |& q5 V/ q. U I* p return strvalue; 2 [/ a# C; n. k( q7 O& C. B/ g7 v } & v" k7 C+ E) a/ E6 B }$ ^( S" s /// <summary> * ^! V. F6 g4 F- W' v /// 将信息写入注册表 % |6 N; d' _/ ~3 c6 C# v /// </summary> , H9 w2 E. ~( l- f /// <param name="p_keyname">键名</param> + H( p( Q, X) h /// <param name="p_keyvalue">键值</param> ( Y: ?- ~1 l+ ] private void WriteInfo(string p_keyname,string p_keyvalue) ( Y' D8 f) v) J* f { 0 M I0 q' y* |# }; E RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); + J. R/ V! F0 y4 E" |4 i" j2 ^% K RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); + ^! \ v {+ {' z$ n RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); 8 G3 U1 m* a9 N5 q3 L //写入相应信息 ! o7 u- _) ]( m w SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); / s3 B/ S3 U u7 y' \: _0 a' F4 G/ S } ) e, b; F+ r8 b2 I( j8 F再写一个函数,用户来获取用户名/密码和更新主程序版本: ' Y& `8 A/ ~% K/// <summary> ' a2 X5 y: {$ h) m8 U: x$ g3 H /// 获取操作员情况,同时更新主程序版本 # u; t& I- V8 i: q4 b4 D3 F) z+ Z0 x /// </summary> 2 z4 J. x6 w2 a0 M( { private void GetInfo() ' d6 K- w" k1 L7 k: F1 O8 C* M { , N/ @9 M7 a1 a0 H5 d2 h+ Q this.m_DataSet=new DataSet(); ( S+ U& ~7 s7 ? this.combUsers.Items.Clear(); % S8 T6 M" s2 U T7 f5 v( K7 b! F/ G! k string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

U3 U# b7 U: \1 W

//连接数据库 8 [2 A6 ]) [" y+ [5 R4 J: Y; L% m& L string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ 5 f1 }, G% x5 x C, X& e Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; : D; }3 w8 O) V OleDbConnection myConnect=new OleDbConnection(strConnection); ) H! R$ p0 t( y8 W2 N0 U0 ^ OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); . D- {* Q/ ?3 o OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); & \! a4 a$ } Z3 s& ]. B myDataAdapter.SelectCommand=myCommand; ) E5 {, B: B( T7 D" Y try . u& t+ K% l8 j$ B, {8 s. ^7 ^ { 7 @# v; w: X I& }. j myConnect.Open();

+ `! R9 i# B$ W# s9 o& ]

//获取操作员信息 # \4 Y0 C% R- L! }. p- M* u myDataAdapter.Fill(this.m_DataSet,this.m_TableName); , p) l1 V t& e0 F //将查询到的用户名填充到组合框供用户选择 - I5 L! O; ?1 W. a this.m_Table=this.m_DataSet.Tables[this.m_TableName]; 1 ]) q9 Y" V4 K1 m5 O foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) 9 F. k/ S5 C y$ ?( m7 X) y { + w+ y4 ^8 h% i this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); - m% Y9 g ]6 q/ ]% S( J }

# q2 D: m0 e7 Q, i4 n; i8 _

//检查是否有新的版本 7 d: T- s0 l3 S; L# c3 s8 m DataSet dataset=new DataSet(); 8 M. ~8 c% ]( Y- D) R p' [ string tablename="tablename"; $ `: q- |( x' K; o3 C0 j //为减少数据传送时间,不获取文件内容 % v1 m/ `6 j9 x' w strSql="select 文件名称,版本号 from 版本"; ! J: G5 |! K8 W myCommand=new OleDbCommand(strSql,myConnect); * f* {+ `0 a9 s$ E$ m _ myDataAdapter=new OleDbDataAdapter(); 4 K' q% e4 `8 v) A myDataAdapter.SelectCommand=myCommand; - ^) l+ w! h+ \ X) U6 ?4 g myDataAdapter.Fill(dataset,tablename); - j1 l2 n* e- Q4 y0 | if(dataset.Tables[tablename].Rows.Count==1)//有文件 7 \7 u9 h' v' X$ b { $ d. _: |7 h( ~6 q% a& G string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); 3 _. a' i$ t2 O& ?) s: Z0 V string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); : y0 g' r$ {# O' `) q) D% { //读入本机主程序的版本号 " B' C9 G% u5 q1 h string oldversion=this.ReadInfo(filename); ( [1 _: e0 ^ O( m7 t0 Y/ } if(oldversion.Length==0)//不存在 # V& v* l4 ^& B8 G3 H oldversion="0"; . b6 Y b- I0 t7 A0 e8 O \ if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 4 X a3 e/ o9 B* ^1 F { # e" n' ^2 J9 }1 b //取回文件内容 : s3 w( b: f4 N dataset=new DataSet(); 9 @8 C! k) A3 L: u) Q strSql="select * from 版本"; ) h b! z# V0 \+ s0 p myCommand=new OleDbCommand(strSql,myConnect); ' Y: s$ G' U' y4 Q myDataAdapter=new OleDbDataAdapter(); 2 W3 M& H# \6 o2 g" Z- q myDataAdapter.SelectCommand=myCommand; . N" g6 R) d2 G2 d& E# ^2 W2 z7 a myDataAdapter.Fill(dataset,tablename); $ _+ W* A8 W0 z! ]7 s/ E& z //将文件下载到本地 & V6 A* U; ~2 E8 w3 B) Q+ a: q4 W: w DataRow row=dataset.Tables[tablename].Rows[0]; , A" C+ {, Y2 f `3 f7 K if(row["文件内容"]!=DBNull.value) % {, E, N- a0 X7 H+ t {

. w d* u) K9 K0 E/ Z

Byte[] byteBLOBData = new Byte[0]; 6 L9 h5 y8 q( z/ y byteBLOBData = (Byte[])row["文件内容"]; v( B+ {, o8 M3 ?9 z try * a7 K9 I1 T1 i, {) C/ A6 \: g! L$ O { : O( h: w) F# r FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); & o2 v6 D/ H. w9 B$ T2 ]# c fs.Write(byteBLOBData,0,byteBLOBData.Length); ( y1 [2 P" z7 c& ~& f" j5 [5 K$ Z: X fs.Close(); 9 a5 H3 R# l+ ]! Q$ _ //写入当前版本号,供下次使用 " |: k# E+ e o+ S this.WriteInfo(filename,version); # R9 ?' n8 R7 J! O T# { } * |2 ~& D! ?+ |& A catch(Exception ee) ( s' u9 c% J4 |9 Q5 h { 6 N. e' l$ _7 u3 e; G* d2 N MessageBox.Show(ee.Message); " ~5 {) }6 Z4 n8 e; V } , p3 r- v# \* ] u$ f }

0 y+ W$ o3 z- }1 `$ K9 b, E

}//有新版本 2 q) O- V/ J- @7 I }//有文件

0 o i1 V8 O8 z% O4 x

//关闭连接 ; P5 \. j. B* v8 p* {" x, g myConnect.Close(); 7 E, x! p1 h1 e1 v2 A } ( e7 S5 ~3 \8 ]4 d- B, B; i catch(Exception ee) 7 L, M4 Q7 \, T$ B! W2 o$ T1 a { ' F8 f( o7 f9 t+ |4 X MessageBox.Show(ee.Message); A3 S( {) Q4 b+ [- ?& s return; ! d: u: K5 o3 I } 9 X. Q) x; u2 x //允许登录 6 j0 v; U+ w2 z1 }+ p. } this.btnOK.Enabled=true; 7 t. }# c: e- P } . d! b- u5 b: Y% e: ~

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: ; q& a* X. F0 ~4 D& E- v private void Form1_Load(object sender, System.EventArgs e) 6 S$ ~3 n6 l4 b" |7 w" C* s. j) k { . t+ m; p( R( o; u H% g6 } //为加快显示速度,将数据库连接等放到另外一个线程中去 4 [: R1 W! ~; @/ v! \$ Q Thread thread=new Thread(new ThreadStart(GetInfo)); 5 }( U9 b' g& x7 E9 k( l; T thread.Start(); % z, f# u, C) I( h& {4 P) z( P+ X V } ; @! M; m+ R2 L: Y! a9 g有了上述准备,我们来编写确定按钮的响应代码如下: ) f/ b5 j D. a- {7 i' Wprivate void btnOK_Click(object sender, System.EventArgs e) 7 _* c& F5 q, F L/ S: z { ( v: B# b' j! T& E //根据组合框的选择,得到当前用户在DataSet中具体物理位置 9 r+ Y) ^9 Z3 M4 j0 v if(this.combUsers.SelectedIndex<0)//没有选择 8 b6 N& m6 h4 h- l) G$ | return; n5 n: Z: w0 {+ V9 n$ J7 M5 u DataRow rowNow=null; 8 Q( O! j2 i1 v- F$ p# t" U$ B foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) . A; s! r, l% |7 F4 U5 B! | { 3 s& W. A7 `5 F) |) |% l: ? if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) 8 i2 B" D5 {1 y6 M+ C$ _& ] { * \$ o1 F/ `. Q8 ~) x) Q+ J; o rowNow=row; 8 O7 I6 y" [5 l4 E! Q* ?& _/ u8 A& S% Q break; 2 A6 ?7 l+ T# H1 [9 |) `& @4 P. G } 3 {' v/ d3 }0 N9 f0 x } 5 o! j) g1 A) \$ { v7 ~( E( p if(rowNow==null) ' D# R3 N; b' V return;

//获取当前正确密码 " O/ O; M7 F8 d" O! q. `" C) E0 D" G string strPassword=rowNow["密码"].ToString().Trim(); ; ]: Y. j. ^! P7 \/ A! d this.txtPassword.Text=this.txtPassword.Text.Trim(); $ l$ @1 `: n* f4 y if(this.txtPassword.Text==strPassword)//密码正确 ) i$ U/ J4 g0 l7 b6 ]( f6 {1 ] {

//主程序名称 + Q% [; l" p' B# F/ V% U3 {, o string filename=Application.StartupPath+"\\"+"MainPro.exe"; 9 h9 g! g8 q2 T3 F //参数名称 6 V' y+ j; V9 l1 I6 ~1 B string arg=this.combUsers.Text+" "+this.txtPassword.Text; 7 q; X4 _+ h/ {* d //运行主程序 : R$ H) s: R2 l4 E( |" j System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

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

//#define NETWORKVERSION

using System; - F; \0 b% D, k6 D# G- }using System.Drawing; 4 P) F) N% L3 l& h, W3 Zusing System.Collections; ' I* \0 {4 h+ i8 C% Z) Ausing System.ComponentModel; $ |8 ^# m0 K& Y( `# T; o using System.Windows.Forms; * ~5 U* J2 X$ a+ J' t z0 d( ?. Zusing System.Drawing.Imaging; i7 |; T7 a4 H9 Qusing System.IO; 1 f% Z2 v! Q3 }1 V# yusing System.Data;

#if NETWORKVERSION ' E) ^. K* M3 H/ w1 m s1 w- b5 C using System.Data.SqlClient; " { a" {9 R/ J* U: z #else ( ~; v K1 w% r4 E i4 nusing System.Data.OleDb; # W: e: l0 p0 X3 O" n#endif 0 r F6 p- X7 J' b using System.Reflection; % p6 m1 V: {7 W0 H* C, K, p# m using Microsoft.Win32;

namespace OA / l7 {8 `! s1 k{ / }6 U2 }% g7 w' z# K public class Tool 9 z0 ], |7 u, z* C7 Y { , k2 N. a2 p4 Z' t public Tool() % K1 [3 s v$ U5 J& h9 k4 G& G1 n { ) F- M- N' \# U- j( g% e# @ } y* p0 u) r- ~ /// <summary> : M: @ h& S8 w0 G. @ /// 主程序的文件名 8 g$ \5 j1 a/ z2 B2 x9 d% d2 X /// </summary> , t% {( R( N) U; t public const string FileName="OA.exe"; . O9 N q0 h% j* M! c public const string g_TitleName="丽汽集团办公自动化系统"; ! C3 e L* G& I2 r public static string g_UserName; 3 [# b/ |- m! P X( F T. Y public static void WriteInfo(string p_keyname,string p_keyvalue) / m: Q. F% S( b. f8 b6 \ { ) u9 ?3 a% S4 G! G5 n; a5 q- j& p% \- P …… 2 ?$ V# `8 \$ j- s } ! d4 a3 {/ B u2 J//其他类似代码略……

} ; k; ?& @, @% x# ~ @6 ` } " B; y$ v% I+ W# ? 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: ' h3 U& E7 d8 l5 O, V在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: / ?& w) r( i, a" Uusing OA; ; F: z7 x, x7 ~) H0 u% h: qprivate void myFun() " w+ _& S$ _8 O) s5 Y9 V{ ( k5 ^5 y, M0 ?7 D1 @string s=Tool.FileName; $ m/ m" J# z9 b/ ` T- {7 B} 0 }+ t! g) a; n3 s- T' S3 g$ b 如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 1 b1 p2 C$ y" X+ q0 V, g5 B' d- h我们还注意了一个细节: 1 w$ }0 T) L6 W2 ?# h///<summary> ' N. R" n2 c( D- ^* H& C M) A///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 3 Z* M6 C. m- D* f3 D2 T: c ///</summary>

//#define NETWORKVERSION * t; X. D; J% ]7 ?# ]: u: o 我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: / T3 Y5 u8 H, r4 N- N# z0 d /// <summary> 4 a9 c5 [2 I; i( ? /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 : d5 V4 p$ _3 x/ ^) y" W /// </summary> - a' x4 x/ d1 o6 x /// <param name="p_Sql">查询用到的SQL语句</param> . n% W/ \. w, P" G( W3 z( R /// <returns>查询到的结果,没有时则返回空""</returns> 2 E @; J/ i2 S# F public static string GetAvalue(string p_Sql) ) u+ K/ j, s" n$ e# @3 d) m; l { : |4 r; X m, J) j0 E- t, ] string strResult=""; / x6 w N5 K! L$ Z5 C Tool.OpenConn();

//设计所需要返回的数据集的内容 , t$ F: G! Y$ g try : R1 M% g/ g$ M) `* B- J* j3 b5 r { + o# @5 {2 ?% D- d% A" H1 } // 打开指向数据库连接 & \+ D2 E% C' r% V9 f8 G #if NETWORKVERSION //网络版 # {1 b5 N9 W5 t3 y w* t; G SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; " q2 H2 h" Q8 Y SqlDataReader aReader = aCommand.ExecuteReader ( ) ; , Q" g- k$ }0 F) R6 I#else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 & g ]/ A7 h: @4 A OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; 4 G: I+ \- g" @1 g+ d4 x OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; 0 G1 E2 Y4 V5 _2 C9 ^ #endif ' T0 ]! J! f! { $ n, ^0 T9 T% E% ^6 |+ ] // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 ' e2 @7 e' T/ h' L: o if(aReader.Read()) : ]/ A# U7 h2 q! u$ @# H' j strResult=aReader[0].ToString(); ?1 B6 q4 J% t; q$ A0 E, r# J aReader.Close () ;

} 7 d6 V* Q2 F) ~( Z9 Y0 M, M catch(Exception ee) * q4 {% I" m3 E0 I+ G. D0 X0 B$ a { t) c6 N5 b# E8 o {( H* c MessageBox.Show(ee.Message); & `% A7 \6 g% f* _& {& N6 v } % J" E: h! g1 m; S- ~( N" z2 O return strResult; ' H& X% y. @: O3 a2 h } ! s! Z1 e. x- Y6 { 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2025-10-25 03:47

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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