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

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

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

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

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

×

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

' Y5 ~2 q( `; ]* I9 P* @

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 5 ?" m- F$ _7 Z% L4 G9 m4 ? 由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 - v. L" e g) {+ e3 U8 D9 V: m创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: 7 t" Y3 ^5 c$ k! j5 e) _6 @5 \- j' @ /// <summary> " _7 K# ~5 F+ u% i4 Z* Q6 P4 q' H( k /// 应用程序的主入口点。 ' H5 x( f7 l( X5 C8 w- p /// </summary> 5 `1 e9 V# h" e+ R) H) v [STAThread] 9 X! d8 J( @; D* v6 P. h- R8 N static void Main() # y; I) }9 z# D { P# g$ t% O# L7 P Z& M/ r Application.Run(new Form1()); 4 v$ _" x! d4 K3 p } 4 E+ h( U2 U- V1 H _ 为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: / K& K' w0 x4 Q6 Q8 N private static string m_UserName,m_Password; [) n. Y: r2 z3 f5 G! r$ }0 _( y /// <summary> 3 X, w& f( e: ?/ f /// 应用程序的主入口点。 2 f" M% u' t: D$ t# v2 l) V /// </summary> E- s) S+ n2 u) ] [STAThread] ! j/ Y$ h4 f2 {% o static void Main(string[] args) 8 f% B. X( R- T- W) d; j7 D { 1 {5 Y! A3 @, Q# O if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 2 \$ u* B6 }9 N0 L) d# d8 Q { ) V, r3 j2 O; u$ X. i! H //记录下用户名和密码,供软件使用 ; h% b! I- c8 W m_UserName=args[0]; ) G% u0 Q6 o$ `2 E4 V2 a6 |" \ m_Password=args[1]; G. v% t/ g3 g z* v Application.Run(new Form1()); G; _1 W* B$ K& I2 O% g1 m# T } % }. i5 e( w! L3 ]7 s" A else 0 q8 Q5 f1 C1 B; O4 G { - A! ?$ R# v9 o0 d MessageBox.Show("不能从这里启动"); % Q( C6 Z, V) P. }* d } * o( y. h. @6 j8 I7 U } % ` g- g4 k# V' j [% m为了显示登录是否正确,Load事件的代码修改为: . P: R4 \9 C2 v! f private void Form1_Load(object sender, System.EventArgs e) # ~8 I" t' I: o { 3 [' b4 S1 q: {/ H) r" G string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); + J* Y$ Z. K+ p/ [3 A. u& D MessageBox.Show(msg); * G/ n# g" Z! z, i } 1 C6 ^1 |- `, @3 Z: o这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 a6 c! N+ n1 v' D由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

$ i" G: }$ z2 F( q

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 / X( V: @3 m- [在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: ' O& y- Y6 m2 z; M* M: f. d 操作员表 版本表 5 z+ f# k- r6 [ 字段名 类型 用途 字段名 类型 用途 6 C P: _6 ~ U& N0 G 序号 长整型 主键 序号 长整型 主键 1 L8 I' I9 d' d+ G姓名 字符 用户名 版本号 长整型 存放当前版本 ! z9 W& z! X- J0 N 文件名称 字符 本记录对应的文件名 4 N3 c' o' [- U2 D% i a' D密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 : @2 ?* M5 j9 v+ s 我们手工输入一些用户名和密码,如下:

* v+ P% |* Y8 }9 U- ]4 d4 e) X3 u* q

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

+ ]- g+ \* X) [$ t$ |5 Q. }

点“确定”,同样,配置输出路径为D:\output。 - V; Y3 B b/ | 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

7 U* P: ?. U7 ^" s( a* a

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 A1 J* S6 }# u- f3 [! F1 }4 T- \ 添加浏览按钮的响应代码如下: # S, y4 S2 U4 J) t1 N private void btnBrow_Click(object sender, System.EventArgs e) , {. }; f0 \7 U { 1 {9 M* P- h- {5 e D' A( q- i OpenFileDialog myForm=new OpenFileDialog(); 0 Z; E/ t/ ~+ e* L' k4 ]" z9 g" \- q myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; / F% g! l# y8 g% {' w0 I if(myForm.ShowDialog()==DialogResult.OK) . d [/ u4 ~ h5 i; _4 m) F2 F1 e# f$ M { 7 Y' d8 ~! ? Q( T0 c# Z2 Z this.txtFileName.Text=myForm.FileName; ! J5 D. t, T; s. H5 S- k' {" h. w } 4 J5 g8 A: U+ e+ F5 s ^) _/ u1 x2 o } 5 k1 s# w4 N& b. N! ^该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 / p, E0 T$ `+ X0 X. j1 ` 添加取消按钮响应代码,目的是关闭窗口: ) ?' e; n9 ]8 p0 X8 n3 f8 N private void btnCancel_Click(object sender, System.EventArgs e) / j' J. s9 J2 @ e { $ `( e+ Q5 b# j) t7 c9 P, C' b; w# T2 L8 r6 v this.Close(); % K) ]' y1 {4 m2 y! J8 t6 u2 C& f8 T } & q6 N( m7 }4 m7 j7 [添加两个引用: # Y6 d' M g" z0 |+ _/ B using System.Data.OleDb; . O1 Q& c' g4 F5 g using System.IO; , F3 B+ v' B6 k' D 再添加两个变量: / `7 l/ W0 T2 B2 W {+ a0 X$ K private DataSet m_DataSet=new DataSet(); ( h) o' R1 W% n9 v; }& j- c" W/ I private string m_TableName="版本"; : R; X8 w+ I$ t! Y$ h 下面的函数去掉文件名中的路径: 4 F% }) x* x7 \0 V# s3 ] /// <summary> ) i% s E( `4 R; ` /// 从一个含有路径的文件名中分离出文件名 9 q. W" |- x1 E0 }+ T /// </summary> : Y: y% {9 m! q8 h$ u8 L /// <param name="p_Path">包含路径的文件名</param> $ o' D7 Q+ ?" o5 |2 U3 H. p /// <returns>去掉路径的文件名</returns> 4 @9 i1 B* A% A" f' G& F$ ^ private string GetFileNameFromPath(string p_Path) % s% c8 F' N4 e { 6 A6 s$ _7 v: Z6 V5 t/ s$ o& u string strResult=""; ; K* f# ]# V' _8 m; T8 ?0 w int nStart=p_Path.LastIndexOf("\\"); , t1 R2 J+ C0 m' F2 G- L if(nStart>0) ; e0 p. X6 _- v k, E0 r/ H { 2 Q2 t' Q" e* H% W. b strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); , h& ~4 ^5 b% W, X } # T* B, u4 Z* w7 O: w7 N/ k, M' \ return strResult; 9 n: h) r; X2 N } : P& w6 G* C' N9 ~1 z添加确定按钮响应代码(含注释): 9 C D3 c' ` T6 K8 O; ~ private void btnOK_Click(object sender, System.EventArgs e) * R+ `3 {8 {( m% y N { : B- U2 w8 O7 ?1 d! E //检查版本号是否合法 1 G% F: e1 Y9 g9 o. u$ @( K try 4 E# ~" T" \% I( ]3 B0 I { : r/ f' ^! p7 V Decimal.Parse(this.txtVersion.Text); 8 P5 m: E! {, e } / C4 y" |3 D8 |8 S! l$ h catch 6 v& o7 b1 M0 a { 6 ?" U! m# ]- u( Y! M2 }/ s5 o MessageBox.Show("无效的版本号!"); ! f1 G7 F- |! N* c this.txtVersion.Focus(); ( X; |7 M) o, D, y% @: ^- Y% Q this.txtVersion.SelectAll(); * \. Y: S3 ]+ t a# I, j) c return; 3 t( m( b. ~) k. c$ p* U( Q }

9 ~. s; S* o0 n9 [* G' F8 ?

if(this.txtFileName.Text.Trim().Length>0) + |7 p' @; l5 @ d# E+ H4 p { . g) N: [( O' S8 Q5 O3 Z //检查文件是否存在 7 w5 ]) G. P4 h9 W6 @3 H( ] if(!File.Exists(this.txtFileName.Text.Trim())) : m) i: B: Y: ?( ]1 l7 x { ( A7 H; v3 W% E" P- c, W5 h MessageBox.Show("文件不存在!"); & P& {& O f5 \- o' v$ g* F$ e2 R return; 7 D# G; P7 b/ L0 H2 ] }

- k4 S! u& Z4 ~

//连接数据库 - |' M$ z) S( x$ g3 C- R string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ ' D8 |; D1 @' U$ `/ q; W Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 3 @: r. m/ S$ }+ x$ O7 s* u' a OleDbConnection myConnect=new OleDbConnection(strConnection); ) w' t' l% H& r6 a% P0 Z% g' H OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); 2 y! g/ s! @# W V7 N8 u OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); + i6 |8 Q, g& \ myDataAdapter.SelectCommand=myCommand; ; y# T; B- g2 e5 } OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); & B. Q- y3 C- k( p1 g3 j8 \3 i myConnect.Open();

" C+ k+ v, H: L7 p3 ?* e

//获取已有的数据 # w$ j' @* h6 P3 J# F m_DataSet=new DataSet(); % ?$ y; ~" p8 k: {4 b2 T/ q try 1 c- ~/ P, g1 E3 P { * k1 E- m) U! [$ Z! L myDataAdapter.Fill(m_DataSet,this.m_TableName); ) h! ^& h& `; g, R+ f2 O //如果是首次上传,则增加一条记录 & c% u! e! f, {, l0 Z. G if(m_DataSet.Tables[m_TableName].Rows.Count==0) / n% N: L. }1 F1 W7 k5 L1 } { 9 @' B% W' b, O9 k2 F DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); 0 [0 C! d! E6 w newrow["序号"]="1"; 6 K5 s; S& C( t1 S8 a8 {$ ]! O m_DataSet.Tables[m_TableName].Rows.Add(newrow); # l5 _! l* B( Y- ^. ^2 ?: `" w } , R/ F. Q5 y% q1 f2 s, l/ M3 [ $ m$ t& M( j. Z# K DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; 8 Y$ e5 j3 X0 m9 g+ \4 C //填入去掉路径的文件名称 + u' L4 @! V+ G row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); 7 U% p, l- b: t$ z //填入版本号 . ~7 {1 k7 X4 K$ ~1 P/ T" f7 O row["版本号"]=this.txtVersion.Text.Trim();

, @: I" D4 p! P. t- @

//将实际文件存入记录中 8 Q, `1 O# \" Y' Q2 y. b FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); 9 m/ a& M/ t4 |4 O6 o byte [] myData = new Byte [fs.Length ]; 1 K1 D1 r, h4 y7 s fs.Position = 0; 5 j. f3 L+ i/ g7 T4 V6 o+ w) c fs.Read (myData,0,Convert.ToInt32 (fs.Length )); 8 W9 x! G w* C3 Q1 }/ F- w* c row["文件内容"] = myData; " i c! `9 H( r" T& F fs.Close();//关闭文件

8 ?1 ?3 s- m1 r

//更新数据库 + W% l. Z3 W) u' q( r myDataAdapter.Update(this.m_DataSet,this.m_TableName); 6 j. [4 s4 f5 G. o% s: q9 ~/ K( c myConnect.Close(); / p: c+ }8 @9 W8 N; M6 t) ^ MessageBox.Show("文件更新成功!"); 3 @4 e8 b6 S$ o# x } * Z' m5 A d0 k catch(Exception ee) ' f8 S8 i) O: j { ; i5 R; ?9 N! k; X g" E7 g MessageBox.Show(ee.Message); 5 q, k% p7 e3 r } . L" A; G% J# o8 q5 L4 `+ J4 h/ Q 7 ?- \! X$ X/ w+ s } & n7 r w* B( O5 i& B6 F' S2 v( Y else / c( t9 c+ i# H+ s& F' Q { & V' d6 l0 @- C6 n4 A. @ MessageBox.Show("请输入文件名"); ) j* W5 \, v5 s% u. `% | } - z. Z+ B K' K$ X, a' F. i$ _# _( S } # u* D+ @3 m! _' i" x$ k# E! {1 Z至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 3 G& ]+ {# ~9 e5 j/ T8 p最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 " k' K$ q- B+ n' M* l( z e 添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

3 S0 [& B, X1 }4 ?* a) C0 h1 M

切换到代码窗口,添加引用: 4 h% @8 ^2 l# N( |9 j, q% Ousing System.Data.OleDb; $ k4 v% l+ i& Q$ _8 C using System.Threading; & P X7 D. \8 musing System.IO; ! B$ L* r: F; k3 }6 U; ^ [using Microsoft.Win32;

# m* A8 w' f& M- r% j

再添加如下变量: : D; |# p- m8 S- x) S/ e8 z /// <summary> / k2 ^' \2 O! \ /// 存放操作员及密码的DataSet 5 N) k/ O+ h+ A( v1 g /// </summary> 4 }* [5 M0 C( b0 p, A. @9 |& }6 N private DataSet m_DataSet; 1 P8 ?: z$ y- U% L8 W$ f! f( j4 y, R. U /// <summary> 1 l* m. h2 H7 f( t9 ^ /// 本功能用到的数据库表 ; y% f4 ~3 G# f& Z /// </summary> , m; X z# O5 S, O private string m_TableName="操作员"; : R. Q) O x- ]/ L private DataTable m_Table; 8 M; S& X7 y, O' A' I+ d1 o 为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: ( ]* W* Z: {" d: `6 Z# s) S, i0 e /// <summary> 5 {1 m3 h) e% O, l /// 定义本软件在注册表中software下的公司名和软件名称 3 `2 h7 C& Y8 X7 B' D1 | /// </summary> + P' c7 j& @1 z E/ z5 b private string m_companyname="lqjt",m_softwarename="autologin"; 5 v6 d2 S9 Y1 B$ W8 g# r1 h /// <summary> . ~- w! A" e8 [2 J9 p1 Y) w$ ? /// 从注册表中读信息; : v+ G/ P" A" ~# i2 P /// </summary> I8 ^9 s+ x2 @6 f! M$ Q /// <param name="p_KeyName">要读取的键值</param> : O; g% m9 ~4 N, ]5 p4 h) C /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> ! z% u" \) f& N) y: _+ N private string ReadInfo(string p_KeyName) ; F3 t- C6 `7 W( x5 N7 ]" ]) K! h5 G { $ S$ o3 j4 n/ ~, H( j RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); - } d; S$ R |8 f/ h RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); 9 z$ A3 l- h& I1 I ]: @/ P string strvalue=""; * F8 Z3 V6 W' C1 Q , q- `: A$ q* r0 ^5 \8 B0 t( q if(CompanyKey==null) 9 E* o! \$ c5 q. V1 t return ""; 3 V, O4 O& ?+ V* |5 q$ ? RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 : T( P9 k3 M* g4 [+ E" J if(SoftwareNameKey==null) 0 O$ Q6 x6 b0 _, N return "";

& q3 O' h9 n: b0 T

try % h/ _0 |! e: x7 L8 U { ! I; X N4 q+ s z1 K" I strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); $ a; b, e% ]* J } / a" h H" a" q# `& |$ P9 r2 o catch " h1 e: B" Z1 I; W6 V) w8 n& h {}

* r% E1 e2 z! G% _) H4 ^

if(strvalue==null) 3 _5 l7 q4 Y; `% r strvalue=""; ' u" A4 t* p3 ? return strvalue; ) B7 e I5 B% M$ U: R } 8 y, T3 T+ o- E& A9 F /// <summary> 4 h- W& q. m. @$ s /// 将信息写入注册表 ; V$ Q$ L8 }) g! O7 V# o; v /// </summary> 9 O- t- X# R, l" P /// <param name="p_keyname">键名</param> 7 B" Y% ~) `1 x& V7 x5 T5 I' E /// <param name="p_keyvalue">键值</param> 1 Q9 I2 [, S5 r& g private void WriteInfo(string p_keyname,string p_keyvalue) 2 e! d6 M; m$ K+ V4 n { O; {, X7 O% N( N2 @ RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); & Z$ L5 M0 @* e6 @0 r% M C RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); , {3 g) u2 u# o4 ~ RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); + g# W0 L: g% C0 X% w5 N( M( \ //写入相应信息 0 ?! c8 z$ h6 w8 A$ b1 f/ O3 d! g SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); , a- {) j' P( J: ~* X, Q } 9 H7 t+ T. w8 ^' g+ K再写一个函数,用户来获取用户名/密码和更新主程序版本: ; x, w9 A! O2 }8 q7 z; h/// <summary> 4 W: m- r) v+ ]5 J+ ?+ O* w3 s* Y% B /// 获取操作员情况,同时更新主程序版本 , R( n. s2 B- N /// </summary> / K. v- [9 R! C private void GetInfo() % `7 g5 C7 [% Q7 x( H9 E3 u { . O" s) i2 [4 l this.m_DataSet=new DataSet(); 9 | H" q- ~1 f. {! ]* b' L+ n this.combUsers.Items.Clear(); 8 h% a4 t- }- a! ?/ X# Y6 w string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

/ o, f2 E' C }. i- q; E

//连接数据库 # q# l, d3 J b2 S" w. ]: a6 e string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ O" ?, @: A! R- f+ M1 o. y Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; # `6 X4 F7 u( ?" t' V5 ] n6 j OleDbConnection myConnect=new OleDbConnection(strConnection); 0 j5 x2 e% v: X$ k; \ OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); 5 b, v. u! E. H; Y- a7 e OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 0 B. @5 d$ c, y. U0 l, G myDataAdapter.SelectCommand=myCommand; : ]) E$ U3 ~0 M2 }, n try 1 b9 R! m$ y, B# @& f6 k { 5 P; N6 o8 U* J+ |3 `6 y. o/ R myConnect.Open();

9 J$ ~- X4 E' u% V( a

//获取操作员信息 2 B: a: @# R( ~/ Y/ G myDataAdapter.Fill(this.m_DataSet,this.m_TableName); % N( k3 x8 H! g //将查询到的用户名填充到组合框供用户选择 5 D' q2 R+ X/ ?" c$ A6 i this.m_Table=this.m_DataSet.Tables[this.m_TableName]; ( _: R3 B z7 Q2 I% b4 d foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) $ L! t6 j) U% ] { # g; g! c6 U5 O' a8 k+ x/ k this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); 8 ^$ R$ _ C; H+ U3 g }

' q6 M5 g0 A8 X3 N0 j

//检查是否有新的版本 " C' P- T& i, y g; p# H5 @ DataSet dataset=new DataSet(); / C0 n' ]0 P W. c! Q! S string tablename="tablename"; ( @, N2 f3 V# b2 j$ } //为减少数据传送时间,不获取文件内容 0 J" s: L6 B5 @5 T! ?4 \5 _ strSql="select 文件名称,版本号 from 版本"; ; Y. e- w. o0 S. F/ j0 S myCommand=new OleDbCommand(strSql,myConnect); # w, A, c3 K" A2 t myDataAdapter=new OleDbDataAdapter(); . M( N* W% o: ? myDataAdapter.SelectCommand=myCommand; . {% q, ?* K& ` myDataAdapter.Fill(dataset,tablename); 6 u% C' a5 ^7 }& D( l" w1 P) Y if(dataset.Tables[tablename].Rows.Count==1)//有文件 0 x- _7 S: m& f( q) v/ T! Y { 8 B8 }8 C. W8 \& t( V/ v string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); # T* {5 D0 y: X, k T9 r, U string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); 0 t4 O2 P6 d6 a* T5 z( z# m8 B //读入本机主程序的版本号 - B6 z) ? s$ G, {6 S3 h string oldversion=this.ReadInfo(filename); ! q4 u8 l' Z' C1 r/ y if(oldversion.Length==0)//不存在 1 [& v+ }* h {7 E3 O* Q- T oldversion="0"; # V, k* W9 K# l% C5 d if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 ( W7 Y( J- t1 n. E; Q { ; o1 W- Q, O8 d; K7 C* z. Z/ \ //取回文件内容 1 S6 q5 r0 K0 y9 p/ ]' y& n% b dataset=new DataSet(); % s+ i' L; z2 m6 w% L9 N strSql="select * from 版本"; " q( O- D- |( y9 Y7 J: d myCommand=new OleDbCommand(strSql,myConnect); / _! h5 ]/ @% |/ D8 Q: V myDataAdapter=new OleDbDataAdapter(); 4 g+ {$ J$ c) a myDataAdapter.SelectCommand=myCommand; ' Q5 d& F- m9 ]; E8 j myDataAdapter.Fill(dataset,tablename); : ~5 B. s2 [5 i- C2 y! ~ //将文件下载到本地 - `+ u2 v# ^- l& r; X1 H' H DataRow row=dataset.Tables[tablename].Rows[0]; ' u* ?& [7 b) T* n! V; R if(row["文件内容"]!=DBNull.value) & p1 }5 g! b! T7 E- ] {

2 `* h* T0 i( g2 P

Byte[] byteBLOBData = new Byte[0]; & J3 ~/ P* Q/ j' z! K7 R8 z% C! H% @ byteBLOBData = (Byte[])row["文件内容"]; / a6 z9 f: j. ?* o M- ?% H try 4 v; l6 |" X0 V' z* w* \) ] { , [' h j4 {0 { FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); , M( c2 \6 O' E3 f2 b2 P' s: R fs.Write(byteBLOBData,0,byteBLOBData.Length); 5 c9 m$ P8 _! S. R fs.Close(); 5 N5 w; J v) |9 a. D: c //写入当前版本号,供下次使用 7 d) `3 a3 D8 J this.WriteInfo(filename,version); 7 c: j t) g# F1 H! I } . @" M4 E% ?4 m1 X catch(Exception ee) ' N# N1 h) V/ r) v$ Z8 K. o4 I { ( Z$ b! _1 C8 U7 T4 I( x( ` MessageBox.Show(ee.Message); : k b, \! T" a, V$ d0 ]+ N$ P } + E& A; m* P0 _5 }: @) g1 L" o }

6 F4 \# }; k; `3 ~

}//有新版本 : r1 Z0 [5 L# t; h+ c( H }//有文件

$ w( ~8 I% `1 @2 ?

//关闭连接 5 P/ ~( U& c+ } myConnect.Close(); `& v5 \+ V1 M3 N } 4 ~, C9 [& L2 [. ~ catch(Exception ee) & E: z6 J( C5 i: V6 D { ( x A4 E& q% }' ^2 @6 U W7 ] MessageBox.Show(ee.Message); 8 a; V2 b: v" Y8 h. `8 e r4 h" E+ | return; 0 K1 k7 O( O. m3 [" H% n } ; E- n. f) s" Y: _# V //允许登录 & V) P& _7 D; j# I this.btnOK.Enabled=true; / V# l/ _. n( Z; T' e5 m: ?( {: ] } " H0 _) b9 E& b& v

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: 9 t$ W: i7 A/ H private void Form1_Load(object sender, System.EventArgs e) ]3 o U$ N1 Y5 i/ Q { B G& u$ M. [ //为加快显示速度,将数据库连接等放到另外一个线程中去 0 q$ U# F+ ]& O. c" N Thread thread=new Thread(new ThreadStart(GetInfo)); * o7 J% p) ~& O# A! [9 B$ ] thread.Start(); $ k" a# {7 _3 u* c } ; \$ h8 ^ d0 N 有了上述准备,我们来编写确定按钮的响应代码如下: 6 j& k" X7 R8 L. nprivate void btnOK_Click(object sender, System.EventArgs e) - H2 h1 D) }4 t7 F+ y; p8 ] n { . }6 H* E/ \4 b/ v" m/ X //根据组合框的选择,得到当前用户在DataSet中具体物理位置 ' F# Q7 _7 O+ k v8 {6 q4 v if(this.combUsers.SelectedIndex<0)//没有选择 6 }6 d+ Y8 r( i* V/ w F. P6 {# p1 z return; 4 `2 G: q9 `/ s _ DataRow rowNow=null; ! {/ Q7 @$ I# D) o! U foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) 3 F1 E% @, T7 e( w/ _ { $ m9 ?1 k* L$ l! b5 ]) Y Z if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) 2 X" X) C6 n, h' a { ; o( J. m2 s M L rowNow=row; - Z' x1 N0 r; r: o$ g* s/ V& t( q break; / W: {" t* ~% Q2 V( f6 K9 g } % T+ [6 Z. b/ {2 t3 u% I } + |1 z- e5 @0 e5 a2 H, ] if(rowNow==null) 7 b# y5 j, A: i: [ return;

//获取当前正确密码 ; ~: K$ l7 d* x- } string strPassword=rowNow["密码"].ToString().Trim(); 2 J$ X4 r0 H" R9 O0 B2 N0 f this.txtPassword.Text=this.txtPassword.Text.Trim(); 4 Y7 c6 ^& A* P. J if(this.txtPassword.Text==strPassword)//密码正确 3 _9 r/ y8 N8 e$ m) q5 E4 I! a) b% l b {

//主程序名称 ' q# G! t* R: L P string filename=Application.StartupPath+"\\"+"MainPro.exe"; ! f% ~' A! a X8 p) s //参数名称 1 q- n7 T. `+ S6 L string arg=this.combUsers.Text+" "+this.txtPassword.Text; 8 K% H) U x0 V) w //运行主程序 ; }; h( P4 P& I' Q% ` System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

//关闭登录框 / @: F3 E( I: K; @/ a8 } this.Close(); e1 `- S" c* U7 g8 L0 X } " m# a3 m; _. w# e& v1 Q else M; c% n$ Q6 Q3 A# p) P$ U { ' B9 Z s& ?) T, o+ N9 ? MessageBox.Show(" 密码错误!如果你确信密码输入正确,\n可以试着检查一下大写字母键是否按下去了。", ( w/ D" d$ g" K "警告",MessageBoxButtons.OK,MessageBoxIcon.Warning); 1 V1 q q1 u. U* g2 g' k this.txtPassword.Focus(); 7 Z" t9 {) H% G this.txtPassword.SelectAll(); ' U: O" G1 @+ M0 ~7 Z1 G } " u. O( q4 t ? } 5 ]# K1 _6 `9 i8 e1 K- Y取消按钮的代码非常简单,就是关闭登录窗口: |. p& b7 B: W* R4 @ private void btnCancel_Click(object sender, System.EventArgs e) + o& w6 y% I, P2 `+ u { 9 M7 q2 G9 x9 z$ c this.Close(); + M: {0 M+ R# c1 `0 e3 f } G, _& j+ L- e# i! y" y把Login和MainPro软件连同其他相关文件打包成安装程序,将Login以快捷方式放到桌面或开始菜单中供用户使用(当然,快捷方式名称可以随便取了),用户运行Login后,会自动更新软件。 9 A8 g) s* w- ?" c* {4 B 本例中所有代码请ftp://qydn.vicp.net/ 下载,文件名为update.rar,解压缩后别忘了在D:\创建一个output文件夹,并将mydatabase.mdb复制到该文件夹中。 ; Z, m: Y7 p& D; W6 {! n" f 说明:本文只起抛砖引玉的作用,通过该思路进行扩展可以完成许多功能,如通过修改上传/登录程序,不仅可以实现对主程序的更新,而且可以实现对任何要用到的资源文件进行更新,本例中不能实现对登录框本身的更新,我采用的办法是在主程序的Closing事件中更新登录窗口,因为此时登录窗口已经关闭了。在登录窗口中,可以放一个“保存密码”的复选框,如果用户选中该组合框,可以将用户名和密码保存到注册表中,下次登录时直接读入,用户只要点确定按钮即可,免去了每次都选用户名和输密码的烦恼, ! |9 s0 j4 @" f4 Q( u 在本例中,我们可以看到,数据库的连接、查询等工作是重复性劳动,且三个个项目中用到的数据库、公司名称等是一样的,在实际工作中,我们可以单独新建一个cs文件,不妨取名为MyTools.cs,将一些常用函数和变量(如数据库连接、公司名称等)做成静态的,各具体项目中链接本文件,然后直接使用,我们只需修改MyTools.cs中的相关变量或函数而不必在每个项目中都去改,既方便又不会遗漏,MyTools.cs参考如下: 9 o9 p9 V- F0 R# F! L$ K6 c///<summary> # f q/ o* G8 A2 d4 E///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 2 W5 B! V f2 O3 Q' r4 {# b$ z9 G ///</summary>

//#define NETWORKVERSION

using System; $ o0 L, N5 c) e/ }, s$ m9 iusing System.Drawing; 3 Q* C5 Q8 t( o$ U# | using System.Collections; 1 L, R9 L3 n5 D' s& h, P using System.ComponentModel; 2 @/ j$ u3 B m. j0 D, U using System.Windows.Forms; " c7 _$ l- q( f# ` R# X- @/ [using System.Drawing.Imaging; " ]# A. r" c* {; p using System.IO; . V* |* m, h3 S( q) l2 C using System.Data;

#if NETWORKVERSION + S8 y" U4 Q9 B7 v6 f6 |/ r. @ using System.Data.SqlClient; ) ^' M. k; m& W, E1 @) x$ \#else 5 _: _% y3 v4 H z using System.Data.OleDb; " |2 n& [% z. s% e$ J #endif $ w3 ^6 f/ \2 \using System.Reflection; & U: m% ^- x7 R$ F. l8 D using Microsoft.Win32;

namespace OA ) s, g4 ]% b0 j+ p8 W: V# ?4 J{ 3 R- _( F& s) L; N. N public class Tool X9 Q/ i E! r- m: F { ! o- O& g' ?* q, j! Y3 _' p" A public Tool() " w( f! f1 k& Z5 ] { # m6 d Y' [6 u7 ]3 `- w, k. | } + j) G R, c C ^% ?, B /// <summary> * T7 q. ~( c M1 I1 D/ ` /// 主程序的文件名 4 R# G& r/ U+ W9 T2 s" }, s$ } /// </summary> 6 |' q& ^5 Z& B7 { public const string FileName="OA.exe"; % f/ P& s0 u4 _* o public const string g_TitleName="丽汽集团办公自动化系统"; + Z h$ J+ K. M( k public static string g_UserName; " U- E6 X. m1 ^1 ^) P+ A$ l public static void WriteInfo(string p_keyname,string p_keyvalue) 5 G0 v) x$ J! u2 n8 y: l) N) K { . o, O" L# j) s& y+ h+ M! y …… - A4 w( P+ S, k } ) V. f d: e# |! Y1 @1 M//其他类似代码略……

} 6 ~' Q# [9 g, K* w7 q} e. r, W8 H: m4 O* x& n% \ 如果一个项目中要用到MyTools中的内容,可以按如下方式进行: $ u% T* V* [9 L* }- ~ 在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: # x2 v* M4 x2 S# L0 a using OA; 2 ]2 v* D) t6 f* H private void myFun() 4 |0 r5 M1 j4 q6 m { 3 H4 R* X) W5 b/ n* H* y, Astring s=Tool.FileName; 4 q9 ?, o8 @! r. p9 h; Q} & P1 i$ o5 ?+ \4 t( l7 U7 { 如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 ' p* }/ y- @4 `2 u+ r6 V& @9 W我们还注意了一个细节: ) C( d* ^+ O. K///<summary> / L3 v N& f: x7 W- K7 V* J ///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 ' r6 F5 c! a- e( f. @ ///</summary>

//#define NETWORKVERSION $ b" O+ Z, Y) W$ a/ s 我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: 5 h. a8 e& B; {; \# v6 t /// <summary> }6 {- H2 j5 Z! k3 w9 t /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 9 }% U1 o1 n# a6 q3 E% G8 o/ ? /// </summary> 9 F; f* D& v" \" O7 W) a# G4 \ /// <param name="p_Sql">查询用到的SQL语句</param> + w) J' L# N8 |9 v0 R. C /// <returns>查询到的结果,没有时则返回空""</returns> ?0 i- J7 G: ~. K4 l public static string GetAvalue(string p_Sql) " ?8 ]3 r2 I' Z2 ]) Y { ; c- ^2 O0 }9 d m) K. g string strResult=""; $ w: k# p' X. [ o: q Tool.OpenConn();

//设计所需要返回的数据集的内容 ! U0 u& X% ]" P" F try $ p' a" W1 t3 j% b% z' |$ i { / s- a! |0 {) l // 打开指向数据库连接 : w: `* x; W- |$ O #if NETWORKVERSION //网络版 7 o, J3 \8 S! }4 u1 D! p; Q# Y SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; 8 e% U0 v9 [: {* n0 e, {+ e SqlDataReader aReader = aCommand.ExecuteReader ( ) ; . F4 \2 N: F: X3 U #else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 ( t, T: U O) h8 p" i/ ? OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; : b7 J2 y" ~7 t+ T OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; H( n5 G4 v! t7 H6 t8 y+ N' H#endif ' z9 Q0 j5 \- \1 l) I& h4 Q , U3 i* O+ A- \ // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 0 U/ v# Q2 ? |0 q4 K if(aReader.Read()) 7 C4 H/ K3 T3 ^' H2 a strResult=aReader[0].ToString(); 1 Y6 s9 `4 R! \4 J" ?) x3 D aReader.Close () ;

} 1 o/ f c% f! j6 f8 ]4 z3 Z9 Z catch(Exception ee) 3 V, `3 r) Q2 P* k/ `# u: S { - h1 o L2 e( b! p' F/ Y MessageBox.Show(ee.Message); " h3 e* n+ G: |+ H& g: t1 [ } 1 i. l1 D) l8 @2 P- Q- q return strResult; # {! ]0 }4 M* X* | } ) T8 N9 ?( |8 {6 a; e! q 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2025-2-22 16:48

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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