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

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

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

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

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

×

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

* W% X/ ]$ U6 e: r: u: W7 E& l

winform程序相对web程序而言,功能更强大,编程更方便,但软件更新却相当麻烦,要到客户端一台一台地升级,本文结合实际情况,通过软件实现自动升级,弥补了这一缺陷,有较好的参考价值。 ( {. q9 ^- D( {由于程序在运行时不能用新的版本覆盖自己,因此,我们将登录窗口单独做成一个可执行文件,用户登录时,从网上检测是否有新的主程序,如果有,则从后台下载并覆盖老的版本,用户输入正确的用户名和密码后,通过参数将必要的信息(如用户名、密码等)传递给主程序,实现登录,我们还是以实际例子来说明。 3 I# L+ V( ~1 Y0 ?+ m4 z 创建一个项目,不妨取名为MainPro,作为主程序,切换到代码窗口,看到如下一段代码: ' c, X; T$ n+ ^! Q /// <summary> Q. l0 S H# [$ W. { }4 b /// 应用程序的主入口点。 / E0 x) A7 d4 x: _5 x% m /// </summary> ' w+ }, }, B1 q0 @7 W6 W* E7 f [STAThread] $ U" _& g5 V |8 R; p static void Main() + L: o: s( h1 p2 @2 R { $ K, L# u% J) B, c; C Application.Run(new Form1()); 7 }# l ?" n4 W) i8 C4 d* l } ' N2 Y: v! Y" p4 n为了接收参数,我们添加两个静态变量m_UserName和m_Password用于存放用户名和密码,并修改Main函数为: ; K0 n3 s# l5 G( a; {$ ^ private static string m_UserName,m_Password; - w- M9 M$ U4 f/ ?/ l2 d /// <summary> $ Z! s5 T( W: |" s* V /// 应用程序的主入口点。 * a+ \# A/ f& w' r3 _ /// </summary> ?$ ^4 U0 v' ~9 ^2 O [STAThread] 7 R4 r# @5 V5 Z/ o static void Main(string[] args) $ k$ ~. A: L. L* y { 5 `, d; f' F) F# ^) {9 g$ ` if(args.Length==2)//有参数输入,你还可以根据实际情况传入更多参数 & V; R& ~/ `/ v% J* k2 @9 n+ W) S { + x+ h5 r, G- b" ]9 D //记录下用户名和密码,供软件使用 9 f. r u( J2 r3 g* _2 l b m_UserName=args[0]; " [/ V1 v4 j" N# D4 N m_Password=args[1]; " l& o4 z3 d% P Application.Run(new Form1()); - l# O; b/ l" J5 A2 @7 U* I } 2 ]4 ?4 `* q% o else 6 @1 g2 Y! O2 g9 E& T { % j0 R) y E. O' }, [ MessageBox.Show("不能从这里启动"); 0 o- F) k" B; Z, N) T7 [ O0 A, r } " [9 n$ u! g; q2 Z" b- x } 1 @) O0 D7 ]; L/ A& U( @ 为了显示登录是否正确,Load事件的代码修改为: 0 n1 x8 \8 Y) X5 @) D- h$ @: m private void Form1_Load(object sender, System.EventArgs e) , @5 `; D H" M8 A { 2 i) p4 |* h" i* r! C string msg=string.Format("用户名:{0},密码:{1}",m_UserName,m_Password); 1 f1 {7 D$ a( l4 Y7 S MessageBox.Show(msg); , v0 N G6 f8 K0 W } % P- t" J' n) v$ ?这样,我们的示例主程序就完成了,只有加入参数才能运行该主程序,例如我们在DOS窗口中用“mainpro user pass”来启动该软件。 - E: i8 i2 l6 B9 `' l由于本项目涉及到不止一个程序,为保证运行正确,需要将编译后的可执行文件放到同一个文件夹,尽管我们可以编译后再将文件复制到同一个文件夹中,但每次都手工复制比较费事,这里采取一个简单的办法。先在硬盘中创建一个文件夹,如D:\output,选择菜单“项目”→“属性”,会弹出一个对话框,在配置(C)后面选择“所有配置”,选择配置属性的生成项,在输出路径中输入“D:\output”(如下图),再编译时你就发现,输出的可执行文件乖乖地跑到D:\output下面了。

9 R7 e p& C( D* ?! \: l3 E& b

接下来做一个上传工具,目的是将最新版本上传到服务器上,为简单,我们这里使用access数据库,当然,在网络版中可以使用SQL Server,原理完全一样。 9 l! r- f; A9 C在D:\output下新建一个access数据库,取名为mydatabase.mdb吧,新建两个表,一个为操作员,用来存放操作员的姓名和密码,另外一个为版本,用来存放主程序的最新版本,两个表的结构如下: ( @! J0 r: S$ |3 P6 P 操作员表 版本表 0 S- Q# e" [5 Y: t字段名 类型 用途 字段名 类型 用途 ' E& @) _' F; _! f序号 长整型 主键 序号 长整型 主键 # ?/ Z; r o9 N7 c/ _姓名 字符 用户名 版本号 长整型 存放当前版本 7 A5 j% _6 r8 U" R8 I( ` 文件名称 字符 本记录对应的文件名 1 G# {* t/ E1 b& T: \7 y 密码 字符 密码 文件内容 OLE 对象,SQL 中为Image 存放文件的具体内容 & w4 Q8 E, A$ }3 v$ w 我们手工输入一些用户名和密码,如下:

5 u& v: |& U7 s- l a

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

- G% o# X% u3 k

点“确定”,同样,配置输出路径为D:\output。 * o3 _- V8 |% E5 t! w9 C 在窗口上放入三个按钮(浏览(btnBrow)、确定(btnOK)和取消(btnCancel))、两个文本框(txtFileName,txtVersion)和相应的文字说明,如下图:

/ E9 p" }. d0 k: p z

在“解决方案资源管理器”窗口中,选择“upload”项目,单击鼠标右键,选择“设为启动项目”,就可以运行该程序了。 3 ~+ r5 K2 C/ l, p添加浏览按钮的响应代码如下: 0 d Z/ i% W: X9 j }( ?7 x private void btnBrow_Click(object sender, System.EventArgs e) ) c( ]/ g x/ c; \ { : M' h- U1 D; [* \* w OpenFileDialog myForm=new OpenFileDialog(); & ?9 s3 P, r$ v8 j myForm.Filter="应用程序(*.exe)|*.exe|所有程序(*.*)|*.*"; " ?9 ^/ a+ N0 `( ~ if(myForm.ShowDialog()==DialogResult.OK) % Q) H% N7 }" j% _6 B- M: r { 7 u8 p$ s1 H5 L$ m this.txtFileName.Text=myForm.FileName; ! `& k0 {4 N8 b8 L2 J) W# V } - q( o$ z& y5 a8 y( L } ( l* x- J& T( t' Y0 o. v: J) C该按钮的作用是得到要上传文件的文件名称(实际应用中,还可以根据得到的文件名,从数据库中得到相对应文件的最高版本号,自动填入的版本号文本框中供输入新版本号时参考)。 , D; A6 Z& {& L2 W; [, y" q 添加取消按钮响应代码,目的是关闭窗口: : J6 w n( `3 _7 b9 m private void btnCancel_Click(object sender, System.EventArgs e) 7 O% O q: V: _; o! y8 |; U { 1 i$ P& T" J: e this.Close(); 4 s N/ c" Y& _ } + R: V m/ h7 l3 d9 v6 b添加两个引用: v! q' b8 V( _& n- w+ Y8 ]. C8 _ using System.Data.OleDb; " h& `3 Y7 |5 ] e/ c* y$ O using System.IO; / l: L S$ \3 x) i) U4 j' B再添加两个变量: , j2 R& N# X1 E) o- ~ private DataSet m_DataSet=new DataSet(); , Y0 f" t: G2 }7 ?+ L' z private string m_TableName="版本"; ( y0 X7 R" Y* u3 n4 X* N4 g 下面的函数去掉文件名中的路径: 0 U# {! X* n/ Z4 g+ s /// <summary> : j S* v7 c* ~1 k! m; { m /// 从一个含有路径的文件名中分离出文件名 4 p* W1 L+ `3 D7 r' i7 N8 \ /// </summary> 7 f7 M" \, R2 W9 l /// <param name="p_Path">包含路径的文件名</param> 5 R3 m6 m, w; n; x- v1 R2 ~, T% q /// <returns>去掉路径的文件名</returns> 3 ~1 _' ^- \) U$ c. O private string GetFileNameFromPath(string p_Path) 2 L& [% U: Y9 f# B0 J { " K w' W+ n: w# x& R string strResult=""; / } g6 T) x. q int nStart=p_Path.LastIndexOf("\\"); 7 q, p- s* Z) G1 | if(nStart>0) 7 X+ u, K; v6 R* w' s { / K+ V3 t: @7 D/ y) T. W) ^+ K strResult=p_Path.Substring(nStart+1,p_Path.Length-nStart-1); + @" G6 R. Z _8 {+ H2 o } , b- r+ A4 I( g( x* v' K3 m return strResult; , Z' ~) @4 V) w5 S5 m7 P/ B } . P; |" t' g; @3 U 添加确定按钮响应代码(含注释): 3 Y$ L! J7 ]7 B+ mprivate void btnOK_Click(object sender, System.EventArgs e) . g; H; X* N6 S! q& g { 9 |9 a r" h* ^8 u* {: D4 q8 }4 E //检查版本号是否合法 6 u% D% }8 ]# c1 @) o try K& \+ e' s) {! E { & v! r2 \: k3 N3 \2 f Decimal.Parse(this.txtVersion.Text); - _* B1 B* c% b# ~ } + }7 x: }3 y" M; k) T J' G$ D3 } catch ) n3 Y2 \# @" L3 f4 L { & ~0 f" K6 J: O. `6 t MessageBox.Show("无效的版本号!"); # F3 _* n) C! N this.txtVersion.Focus(); ( N/ \4 w' R3 |' z' `# b this.txtVersion.SelectAll(); " q9 C6 o) q }8 T/ `' b- N8 U return; 7 f* S- Q% ?- v' { }

; w# a7 K) n$ o/ `

if(this.txtFileName.Text.Trim().Length>0) ! s# l4 c! V1 G. U0 \ Q { 5 C/ V1 I. p6 m& Y C/ \' J9 g. ~- z //检查文件是否存在 0 N" t4 f: ?3 R+ u2 N8 w5 T if(!File.Exists(this.txtFileName.Text.Trim())) - `8 O" m. E7 E( f8 } { ~; \ B! ]/ n. `, u: G" ^7 ^ MessageBox.Show("文件不存在!"); 7 n; \7 n: r4 d6 g return; 9 K! j5 X% n: N. \% O) j }

- J6 z7 x2 e* j! ~, B: T

//连接数据库 8 N6 }" \) s) A/ S! [ string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ : o. B7 h1 k% }4 k1 q8 Q$ f Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 1 j% r+ y% w% S" r/ e OleDbConnection myConnect=new OleDbConnection(strConnection); 8 @, }8 X# b$ Y' I2 d2 t" E) G OleDbCommand myCommand=new OleDbCommand("select * from 版本",myConnect); 4 q5 @9 V% E5 i" @( z8 I3 G OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); ' y* D' W- s& O# P: b myDataAdapter.SelectCommand=myCommand; 9 E# C G" a8 p OleDbCommandBuilder myCommandBuilder=new OleDbCommandBuilder(myDataAdapter); , ?% @8 f6 c) u6 {& m, D myConnect.Open();

- |0 F# e3 ?" \6 K# b% ]# c: T

//获取已有的数据 ; f, G2 r8 G- ^! ^ m_DataSet=new DataSet(); 6 p1 q! B; z0 x try " f' U$ F! ^5 m4 |: p4 ]* u { " H; T; X6 Z6 a myDataAdapter.Fill(m_DataSet,this.m_TableName); - J% N4 k: O6 S/ l, D9 y //如果是首次上传,则增加一条记录 ! h( h/ `$ E; Q$ }7 c( K6 H( |$ R if(m_DataSet.Tables[m_TableName].Rows.Count==0) # ^- H3 D. {' ^ { ) y- _, w4 d/ i" ] DataRow newrow=m_DataSet.Tables[m_TableName].NewRow(); & D. e, d D# M. [3 |& e newrow["序号"]="1"; ! c2 v" q- t$ j+ n. e7 n m_DataSet.Tables[m_TableName].Rows.Add(newrow); % P& ]2 l5 t$ b } ) g8 Z& v, T5 j+ a% J0 t( |$ v + B$ I4 Z- b0 N$ [& g6 N DataRow row=m_DataSet.Tables[m_TableName].Rows[0]; . I/ z, O* \* N3 L //填入去掉路径的文件名称 ! N; E) X" ]: Q0 Z2 B3 f/ M+ ` row["文件名称"]=this.GetFileNameFromPath(this.txtFileName.Text.Trim()); ! \5 R, f' B1 W6 T' H# j7 N //填入版本号 8 J* h/ |" P) f7 C K row["版本号"]=this.txtVersion.Text.Trim();

) `. z4 X8 ]/ ]6 x

//将实际文件存入记录中 / \: D3 Y. d+ n) g9 w: L, Z FileStream fs=new FileStream(this.txtFileName.Text.Trim(),FileMode.Open); % ]8 _; G% G+ T1 N5 Y byte [] myData = new Byte [fs.Length ]; 7 b5 _6 C* |& m u0 } fs.Position = 0; 5 g1 f6 S! `8 f7 @1 X3 T; F$ c* Q7 Q fs.Read (myData,0,Convert.ToInt32 (fs.Length )); 8 | X* q+ w5 ^/ w# J7 c3 N' z3 I row["文件内容"] = myData; & M0 U) a3 j U. X0 [) S# a- N fs.Close();//关闭文件

$ i% l, b5 M# @

//更新数据库 " z. T5 P, b/ D6 x$ ^: O* e myDataAdapter.Update(this.m_DataSet,this.m_TableName); * H4 G" u0 C, y; L' @3 ? myConnect.Close(); . v: Z: F! Y: C( x/ H* t MessageBox.Show("文件更新成功!"); ! R& Y( g1 Z7 B6 W. ?4 Q4 B1 N } 9 j8 W$ w8 J9 d, {( ]) t( ] catch(Exception ee) : D" ]+ g! x2 t: |/ |& K m8 B# \ { 7 f7 i& ~9 Y/ X$ U: N% D. z! l2 J MessageBox.Show(ee.Message); * X, E) }6 k: k# T- K } ) o, f4 r' K4 V; e3 d : v* t# w( i) n6 @. B4 c } ( r x! \# [1 Z- V$ b else / r8 \- A, o3 p) s4 D$ \) H& R { 4 h R) o8 j" N3 Q8 ` MessageBox.Show("请输入文件名"); 3 E1 h5 J5 \$ o( ~+ T" R. `( [ } 5 I0 h0 b0 F7 n) S. |5 ~% U } / M3 K* P, H( Z; w& v3 n至此,上传工具制作完成,通过该程序,可以上传主程序文件,当然,该工具是给软件开发供应商用于发布新软件用的,千万不要给用户哦。 " x' y1 G/ `% K8 ^: @最后是编写登录程序,按照编写上传工具的方法添加一个项目,项目名称为Login,设置输出路径为D:\Output,并设置该项目为启动项目。 ! s& R6 V5 }6 c4 m4 h 添加一个组合框(combUserName),设置DropDownStyle为DropDownList,用来选择已有的用户名,添加一个用于输入密码的文本框(txtPassword),设置PasswordChar属性为“*”,并在前面加入相应的文字标签,再添加确定(btnOK)和取消(btnCancel)按钮,并将确定按钮的Enable属性设置为false,目的是如果新软件没有下载完成,不准登录,布置如下图:

Z1 l C$ U. m1 t; ?" b' D

切换到代码窗口,添加引用: ( v* x" u+ z5 C% Z0 ^6 dusing System.Data.OleDb; 2 {0 z6 G( z& ^: T S0 G$ Musing System.Threading; 7 u2 S e( ~( b using System.IO; 8 n$ b" T4 n7 F" I2 D0 y8 h using Microsoft.Win32;

# G- {& s, m' Z' ?5 {

再添加如下变量: * U0 U5 z& @! u. [% I1 T) b: P9 c /// <summary> . D ^" ?0 G# G9 J /// 存放操作员及密码的DataSet . L! F" C; e1 O) A /// </summary> |2 J0 a2 S9 a' g# v( \( T2 }1 w; W private DataSet m_DataSet; 9 _! Y: E/ y8 H /// <summary> M M( j9 q: ~" ]) A+ Z) \3 i /// 本功能用到的数据库表 6 V4 H, B, t1 U2 C /// </summary> $ [* Z+ x8 V, w* e$ z2 g" F% o private string m_TableName="操作员"; 3 @5 q+ S: p+ S% v* m5 |0 { private DataTable m_Table; # T/ ] {$ B9 H, \: D! {# G为了避免每次都下载主程序,我们将当前主程序的版本号要保存下来,我采用的办法是保存到注册表中,为此,写两个函数,用于读取/写入注册表,如下: # A, _2 N8 y1 r7 j( N- h /// <summary> # z, H7 L) L+ O /// 定义本软件在注册表中software下的公司名和软件名称 u+ q/ ~ y( S& L) h: h* e" y4 k /// </summary> + {5 [. ^$ M* p+ e9 D8 x! P! w w private string m_companyname="lqjt",m_softwarename="autologin"; 9 {: L- E1 H' t3 K3 ?! z8 J /// <summary> 6 g x4 P5 Y1 F4 w/ h+ C& F! I /// 从注册表中读信息; 6 H0 e: J! S; h7 o a' s: `$ g9 B. r /// </summary> : m/ S2 F* @( R5 P: _$ { /// <param name="p_KeyName">要读取的键值</param> 4 ]' N5 i9 {- S6 d& d /// <returns>读到的键值字符串,如果失败(如注册表尚无信息),则返回""</returns> # \: I. V- j5 o$ S private string ReadInfo(string p_KeyName) 5 ~' h+ G* T+ Z2 r { 8 u: P, s0 O- Q/ D RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); 8 z" @* y- [3 ] RegistryKey CompanyKey=SoftwareKey.OpenSubKey(m_companyname); 9 I3 U; `" c9 r6 y0 Y string strvalue=""; # s: D$ |1 b! i ! [$ x% u! v1 c" v if(CompanyKey==null) ) e( z4 _- b; S K7 F1 |+ V return ""; 5 ?1 X# H+ r. M( w' U" V RegistryKey SoftwareNameKey=CompanyKey.OpenSubKey(m_softwarename);//建立 + Y( X& [1 M1 p7 B) K9 y2 T/ g2 Y if(SoftwareNameKey==null) 8 I8 u( x! S; f% A: Q2 l( p return "";

0 k3 z$ Z8 A' h$ Z6 J

try + \1 D# U6 N: T" k+ _* @ { 6 s( {- A) o S+ i0 l! [; F# K strvalue=SoftwareNameKey.Getvalue(p_KeyName).ToString().Trim(); ) T# A; g: {* ^9 S! H } & P6 L/ ] B1 R H$ u1 f4 G catch 8 r& n1 {3 D, x, M {}

3 P9 U2 [' }3 h5 x

if(strvalue==null) , q& `% X' j9 U& O2 I- I7 M strvalue=""; ; D/ b/ E7 Y: ` return strvalue; $ k0 h# X0 `4 G9 } } 1 y, \+ c% g. S /// <summary> ) S5 [9 x. |7 c4 w) u3 c /// 将信息写入注册表 - @& q9 g6 V; P) r /// </summary> E& E( I& U; y+ T /// <param name="p_keyname">键名</param> $ | M+ `1 G& M z) y2 j7 ?* u% h /// <param name="p_keyvalue">键值</param> $ j8 H" k! b6 Y E( y& x private void WriteInfo(string p_keyname,string p_keyvalue) ' m7 \! y0 J) b# ?; {+ w { % V6 t9 c0 E& {; p9 v( E RegistryKey SoftwareKey=Registry.LocalMachine.OpenSubKey("Software",true); $ N5 O% {5 G" w* g RegistryKey CompanyKey=SoftwareKey.CreateSubKey(m_companyname); , E+ z1 @# @7 ?5 n* \5 l- U RegistryKey SoftwareNameKey=CompanyKey.CreateSubKey(m_softwarename); 3 Z6 K. \' u# e D //写入相应信息 0 [& t/ k7 K9 e4 M$ `" y SoftwareNameKey.Setvalue(p_keyname,p_keyvalue); ; d% c9 Y. q8 `# B) Q& x } / m' R% u7 z7 ~" [ 再写一个函数,用户来获取用户名/密码和更新主程序版本: . U8 f) K1 ], {9 {) ^- @2 f/// <summary> 0 K+ H. r, F1 k4 s /// 获取操作员情况,同时更新主程序版本 , s1 M7 |( }+ o/ q! n3 H /// </summary> 9 N# [/ I: E3 n, g3 Z' Z private void GetInfo() " U- r# ~4 h1 W. s/ r& o L6 { { , q4 y. L8 a# ?( a, h6 V( j this.m_DataSet=new DataSet(); ; f* O* x6 d1 q; ^ this.combUsers.Items.Clear(); ) b, M3 S: W' I! \; u$ Q string strSql=string.Format("SELECT * FROM 操作员 ORDER BY 姓名");

, F$ ?7 h r) ]" c" }5 k

//连接数据库 ) R0 {; Z; z& }6 B string strConnection="Provider = Microsoft.Jet.OLEDB.4.0 ;Jet OLEDBatabase Password=;Data Source ="+ 0 T4 n2 |- V7 @2 S3 B7 j; d h Application.StartupPath.ToString().Trim()+"\\mydatabase.mdb" ; 0 @7 Z9 C9 o! _ OleDbConnection myConnect=new OleDbConnection(strConnection); ; Z' f! ` e: n% ^8 m& J& U6 w6 j) Q1 Y OleDbCommand myCommand=new OleDbCommand(strSql,myConnect); 7 a4 G e# F( [" P' r, K OleDbDataAdapter myDataAdapter=new OleDbDataAdapter(); 0 G5 X. L. _4 [ myDataAdapter.SelectCommand=myCommand; ' z/ g! \% J; K2 A" a' d5 o5 t try . s0 z9 q+ g2 C7 \ { 9 i- [" H2 Y) T9 c ] w* a myConnect.Open();

. Z! B; a& W& H1 u% g4 b) f2 W

//获取操作员信息 5 j6 C" Y# s6 Z! a/ R myDataAdapter.Fill(this.m_DataSet,this.m_TableName); $ c1 f3 G% l& V% o" D7 j //将查询到的用户名填充到组合框供用户选择 . x" L/ T5 U1 W% N* ` this.m_Table=this.m_DataSet.Tables[this.m_TableName]; ' ]5 b8 m6 b/ V4 N$ z7 ]; g' W foreach(DataRow row in m_DataSet.Tables[m_TableName].Rows) - I$ |& h; C. N |6 C# T G# S# y { 6 D: T2 W- Q: W/ b5 G( F6 e this.combUsers.Items.Add(row["姓名"]).ToString().Trim(); * T$ \7 {: ^3 ] }

5 x" C. r; L9 z5 ^' J

//检查是否有新的版本 7 e/ V# s% g$ k4 U( ^ DataSet dataset=new DataSet(); / W$ L. C/ [4 k string tablename="tablename"; & B/ [6 S; P4 F# ]* A //为减少数据传送时间,不获取文件内容 9 V0 d* ^& B8 s; x+ f3 w7 [ h strSql="select 文件名称,版本号 from 版本"; 7 Y' l/ S$ y+ {. h myCommand=new OleDbCommand(strSql,myConnect); * \: d; ^, y1 {& \ myDataAdapter=new OleDbDataAdapter(); " l1 a) F% v! w6 D; g D myDataAdapter.SelectCommand=myCommand; : ~0 Y, n M2 |7 q: g* o3 c myDataAdapter.Fill(dataset,tablename); ; `0 Z5 y3 i/ h8 T if(dataset.Tables[tablename].Rows.Count==1)//有文件 4 Z3 x. T% @0 R4 D' k2 o$ ~- k { ; g/ a1 v, r& `7 a4 @ string filename=dataset.Tables[tablename].Rows[0]["文件名称"].ToString(); k0 c9 j' W7 c3 f" \% b2 d$ M% i string version=dataset.Tables[tablename].Rows[0]["版本号"].ToString(); % L; |& D0 O) k0 q' l: F# S& i //读入本机主程序的版本号 % a2 a* K( U2 x" V0 q8 g string oldversion=this.ReadInfo(filename); 9 j k( n0 m9 t" j" \0 C2 U if(oldversion.Length==0)//不存在 . @6 F6 Y6 x8 j0 W9 G( S6 Z oldversion="0"; ) b$ }7 i3 q5 ^, u8 F7 K if(Decimal.Parse(version)>Decimal.Parse(oldversion))//有新的版本出现 * Z. [$ Q) U9 E { 9 i& p+ w* h; V- ~3 L //取回文件内容 " Q+ n2 ~% } L' w dataset=new DataSet(); + M y+ [! I! u) A/ X strSql="select * from 版本"; " R* ~/ J1 _/ _' l* @9 Q myCommand=new OleDbCommand(strSql,myConnect); " p! l' B4 I6 ?% I; \5 B5 ^& Y: L myDataAdapter=new OleDbDataAdapter(); / k- C5 R, _& f- o, t( Q" Y- v! f myDataAdapter.SelectCommand=myCommand; 4 B$ n5 R2 e# e3 w! u4 \ myDataAdapter.Fill(dataset,tablename); ! ]3 O% s9 L1 L //将文件下载到本地 6 i, U/ }2 k% n+ D }0 F DataRow row=dataset.Tables[tablename].Rows[0]; ! {: O3 p. x5 F' N4 w+ E if(row["文件内容"]!=DBNull.value) 2 D5 L" T" ^) H4 O( F' ?( Y" {" [ {

8 y3 v+ s( X- D2 J4 F( G

Byte[] byteBLOBData = new Byte[0]; , {$ X; l1 ^# M/ z byteBLOBData = (Byte[])row["文件内容"]; ) u( I2 m h# X% I7 |6 h- M try * Z; e; X/ G% J' e2 I { ' Y* Y8 \' ]& [ FileStream fs=new FileStream(Application.StartupPath+"\\"+filename,FileMode.OpenOrCreate); _4 E2 O5 Y' g' Y$ U1 m# s6 I fs.Write(byteBLOBData,0,byteBLOBData.Length); % z% Q. M) p3 v5 u fs.Close(); . Q( _5 {, y1 y# b //写入当前版本号,供下次使用 $ j) A5 Z4 _& P, a' t* X) ~) Q6 m this.WriteInfo(filename,version); 0 u9 u9 @' O4 t/ Z, d9 | } 0 Y4 F+ N$ `% j1 v# [2 {9 @8 T: P catch(Exception ee) ; L5 h) _6 Z; u' D# l: M# B$ L! O$ S { 9 F. c6 E2 l) [$ } MessageBox.Show(ee.Message); 3 C$ c* u) w# G/ I1 Q- @# j } 1 M4 n% P2 M$ `( W# t/ s }

Y" K) O2 D& ^) j2 i

}//有新版本 ( n/ F& [5 _: l# ]! z( S }//有文件

% y# k% w/ P# M

//关闭连接 2 r! T8 J/ Z% }+ ? myConnect.Close(); $ B [! D$ X- s8 d" S P } + M/ c& x: Z- p: n! \" X3 c catch(Exception ee) : l: @; S' f3 ?" F" k { ( H# ^5 I8 H7 `5 l( _& ~ MessageBox.Show(ee.Message); " W& ~3 a0 N) y# |" N* g return; 4 T1 `+ Z4 F- u2 u+ G4 p+ v% w } 6 f# o) U5 X2 @2 U //允许登录 $ s! n* v' P* _& V2 ~ J0 T; S. f5 a8 j this.btnOK.Enabled=true; ! b- }% y4 @( i1 x8 R2 D/ n } + y; \- f6 u" l {1 q

遨海湾-心灵的港湾 www.aosea.com
 楼主| 发表于 2004-12-24 14:18:00 | 显示全部楼层
为了不让用户等待太久,在启动时通过一个线程,让获取用户信息和更新在后台完成,即在窗口Load事件中,通过线程调用上面的GetInfo的函数,故窗口Load代码如下: ' O" I+ p. h7 U private void Form1_Load(object sender, System.EventArgs e) " E3 _" h7 Q$ M9 J% O& t( Y { + \6 v3 W |+ g' j/ P1 e6 U //为加快显示速度,将数据库连接等放到另外一个线程中去 ' X, z! Z* ?( n Thread thread=new Thread(new ThreadStart(GetInfo)); 7 @, l7 V+ Q9 e thread.Start(); 5 H/ Q8 ?8 O- h v3 o1 R% E$ C } 1 a- {* L; a) S! U/ S( i# ?4 t 有了上述准备,我们来编写确定按钮的响应代码如下: $ W; D* \& l( X& \6 ~; Wprivate void btnOK_Click(object sender, System.EventArgs e) ' ~4 N9 X6 l% c5 q$ e9 K& p# d3 c+ G { 4 A$ G1 K* ?" ]1 [6 C9 w; u //根据组合框的选择,得到当前用户在DataSet中具体物理位置 6 J. p! F* {- y' h8 G4 j if(this.combUsers.SelectedIndex<0)//没有选择 5 D, M; ]& t' d% t9 n return; 7 k! f/ R, M, h+ Z DataRow rowNow=null; # w! I" N) j* q% @ foreach(DataRow row in this.m_DataSet.Tables[this.m_TableName].Rows) ! v L- O) a) l2 H { " F! K+ k/ b( A if(row["姓名"].ToString().Trim()==this.combUsers.Text.Trim()) 2 j! k( c- k" u$ A4 J { . i. }4 m, A: n# I) f L" w rowNow=row; 2 U, X2 R, G S' f break; # ?4 z" g- P$ A% T' n } / r# [/ X+ v# ~5 m. t } ) C+ s3 k4 r5 Y( M* X* Z% i if(rowNow==null) ) P7 k' s% i8 j) | return;

//获取当前正确密码 % k$ f( i* P8 Z0 B3 K2 \ string strPassword=rowNow["密码"].ToString().Trim(); Q4 \- t: ]8 X1 A7 n9 d4 G) H this.txtPassword.Text=this.txtPassword.Text.Trim(); " j. y7 V+ D6 O3 B% K if(this.txtPassword.Text==strPassword)//密码正确 3 U+ o1 B# x/ r1 u {

//主程序名称 " f$ A/ h/ n( F4 ]0 [ string filename=Application.StartupPath+"\\"+"MainPro.exe"; 0 S- D: _" l3 e+ {4 w% J# M8 Z //参数名称 # u- v2 v4 S; m6 Q* G4 {4 \ o. y2 ] string arg=this.combUsers.Text+" "+this.txtPassword.Text; 6 K5 X3 |( Y1 [; y/ O //运行主程序 * p" u8 l! {5 U System.Diagnostics.Process fun=System.Diagnostics.Process.Start(filename,arg);

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

//#define NETWORKVERSION

using System; 6 T- b2 @+ d& e using System.Drawing; 8 C5 G7 B5 R4 t$ husing System.Collections; % e* {& R9 V: N3 ]& _ using System.ComponentModel; R- y3 D( F* `9 \6 F4 R8 Dusing System.Windows.Forms; + j l. Q* M \0 {using System.Drawing.Imaging; ) W- `& M5 c! u+ A3 v# }+ w1 L! e7 kusing System.IO; 9 I; D; g9 b# musing System.Data;

#if NETWORKVERSION : Z3 @3 I* `. musing System.Data.SqlClient; 7 f! K/ b8 G* o$ l#else : I! s2 M# U; n) D% c using System.Data.OleDb; 2 O8 @# u* H$ _. t' F. L! l #endif 1 i0 X5 U; o' L- v7 m O# uusing System.Reflection; 3 V4 a8 L* {" Q* u1 C- Iusing Microsoft.Win32;

namespace OA + T" K! \$ G/ j1 A) d{ 5 N1 n6 E; _7 r& A public class Tool $ g/ I3 J. q; u( ~$ T { ! M }/ _& y1 ^) L5 Z! m2 e' G public Tool() # [! @% F# a. k/ G1 ?& G+ i3 `- T { 9 V- X( \1 y& V4 Z- \ } 9 Q9 t+ j4 \0 W5 |3 ~8 h /// <summary> * o0 l) d) E4 f* u /// 主程序的文件名 " j$ E8 e/ a2 ? /// </summary> % w# J1 i& f9 F9 C9 o( X0 Q public const string FileName="OA.exe"; 3 G* E# ]2 S, Z; ]" F( Q& D public const string g_TitleName="丽汽集团办公自动化系统"; p" }( H/ I! i! U public static string g_UserName; 3 N& w9 [' l) d, c1 |& U public static void WriteInfo(string p_keyname,string p_keyvalue) - d3 v2 a* }. c* x7 [ { $ x9 }* ^+ u0 h4 }* @3 I7 W …… : b+ w2 x+ A9 G, F( w } ' o8 e& A& A. T' i1 c+ j //其他类似代码略……

} ! E8 r5 ]( ^* O; e5 L) k8 W' O} 2 }8 y$ g& y: @# y( y7 \如果一个项目中要用到MyTools中的内容,可以按如下方式进行: : h! Y3 A# b0 c2 d在“解决方案资源管理器”窗口中选择该项目,选择菜单“项目”→“添加现有项”,此时弹出打开文件对话框,文件类型设为所有文件(*.*),找到MyTools.cs,不要直接点打开按钮,看到了打开按钮后面的“↓”了吗?单击它可以弹出一个菜单,选择“链接文件(L)”,这样插入的文件只是一个链接,不会生成副本(如下图)。

使用时,添加MyTools的应用,再使用Tool类中的公共函数,如: " z# Z' E) N# ~ using OA; 2 [: v# q. }4 w! Y& S. Y% Tprivate void myFun() 5 n/ {' h& h/ J7 B" `0 E2 m, G { - G3 i! X& C; _: W3 j; L/ N& f string s=Tool.FileName; . V6 L W) A- B- ~+ m f( `& j} & `) x6 t! ?2 ~- a1 F# P 如果单位名称变了,我们只要修改MyTools.cs中的变量就可以了,不必到每个项目中都去修改。 8 S0 A2 q* s0 c/ }! B) S 我们还注意了一个细节: 5 B1 u) M, x7 ]8 T1 [4 K///<summary> $ J9 A0 Z+ y- z1 }: f///预编译选项,如果定义了NETWORKVERSION,,表示是网络版,使用SQL2000数据库,否则,使用ACCESS2000数据库 : S+ g% q! Y' B ///</summary>

//#define NETWORKVERSION ( E0 L' J2 y( B; j' k; L我们知道,对于ACCESS或Sql server等,除了连接方式外,其余操作几乎完全一样,因此,我们定义了一个选项(如上面的注释),如果#define NETWORKVERSION,表示是网络版,使用Sql server数据库,否则(将#define NETWORKVERSION注释掉)就是单机版,使用ACCESS数据库,在MyTools中我们将两种连接方式有区别的地方分别编写,就可以通过是否注释掉#define NETWORKVERSION这一行分别生成单机版和网络版软件,参考代码如下: , N0 m) e3 ^" R _; q+ c3 s /// <summary> ! m( S- I# B3 g; W& s6 T /// 根据SQL语句返回一个查询结果,主要用于只要求返回一个字段的一个结果的情况 , j6 n, n0 V! Z- B7 r1 o( t6 N /// </summary> 3 D. A7 }5 L" Z6 m /// <param name="p_Sql">查询用到的SQL语句</param> ' e* t! W, e3 T$ O- u. b /// <returns>查询到的结果,没有时则返回空""</returns> 7 [5 ^. K7 D [/ u% ~2 W public static string GetAvalue(string p_Sql) 8 m: V' B, b* P" x }( j& a8 s { / g6 r; W# a+ r D/ Q% f( ?. { string strResult=""; + c9 C+ h; R/ W& N" T* T( ^ Tool.OpenConn();

//设计所需要返回的数据集的内容 9 ]2 [ j6 t6 D& E+ i try # v4 K4 G( m5 N' _8 [ R2 } F# M { ' \2 }9 k8 i% C! | // 打开指向数据库连接 6 E2 g/ b( X4 M4 f0 ~9 V. L #if NETWORKVERSION //网络版 , k/ A: B' t0 f% a SqlCommand aCommand = new SqlCommand ( p_Sql ,m_Connect ) ; 4 _( ~4 B: I$ Q) G9 [$ M5 @2 x+ j& R SqlDataReader aReader = aCommand.ExecuteReader ( ) ; * m4 S7 {3 t6 H3 t- `9 f7 _#else //单机版,注意变量名aCommand和aReader在两个版本中都是一样的,有利于编程 1 t/ ]3 b, n1 p) b2 f: ?" t; x6 h! V8 ` OleDbCommand aCommand = new OleDbCommand ( p_Sql ,m_Connect ) ; % f/ ]/ C7 `9 t5 w9 b4 n; l OleDbDataReader aReader = aCommand.ExecuteReader ( ) ; 5 @; I" M; B& }4 S8 E #endif 4 s5 i7 x4 H9 F+ l ; t1 h( A8 {0 p5 Z& P // 返回需要的数据集内容 这里就不分单机版还是网络版了,反正变量名一样 ' i* x. m8 y' a8 m8 M% } if(aReader.Read()) % o( G) ^ n: v5 z: k, N strResult=aReader[0].ToString(); 1 E! J9 W k' P9 a aReader.Close () ;

} 7 ?/ n9 M6 O [: j3 S7 d catch(Exception ee) 9 d8 l2 Q: v, J+ I. b+ C+ m; u { 2 H* h+ n! Z$ J/ y# i: q- B MessageBox.Show(ee.Message); 2 [, w8 |6 R" ~9 b } 6 v! z/ |. @% n9 B9 ]# Q return strResult; - l1 h8 I+ o( i } + c$ l5 J1 p" Q& I 以上类似的小技巧还很多,注意总结,定会收益多多。

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

本版积分规则

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

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

GMT+8, 2025-5-15 09:38

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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