自动升级程序框架描述

        最近考虑.NET下自动升级程序的问题,到网上搜索了一下,现将自动升级程序框架描述及c#demo实现汇总如下。该框架的描述入图所示。

图:自动升级程序框架描述图

1框架的5个部分

  1. 客户端程序,即需要被升级的程序,在下图中用Client.exe表示
  2. 服务器端程序,该程序与自动升级程序无关,之所以把它画出来,是为了图的完整性,用Server.exe表示
  3. 升级服务端程序,该程序负责监听升级客户端程序的连接请求,用UpdateServer.exe表示 
  4. 升级客户端程序,该程序与客户端程序位于相同的主机上,负责与升级服务端程序通信并对客户端程序进行升级操作,用UpdateClient.exe表示
  5. 升级客户端程序代理,该代理以dll形式提供给客户端程序使用,该代理负责与启动升级客户端程序并传递相关信息给升级客户端程序,用UpdateClientProxy.dll表示

2自动升级程序思路

自动升级程序的设计思路比较清晰,遵循如下步骤进行:

  1. 客户端程序主程序启动时,使用升级客户端程序代理启动升级客户端程序并将客户端程序的当前版本号和其它相关的信息传递给升级客户端程序,由于该操作是异步并行的,因此,客户端程序不需要等待结果即可继续运行当前版本的程序,如果当升级客户端发现新版本时会以消息的形式通知客户端程序。
  2. 启动起来的升级客户端程序接收来自客户端程序传递的当前版本号和其它相关信息(比如客户端程序所在目录等),升级客户端程序同升级服务端程序通过socket建立连接,并将当前版本号传递给升级服务端程序,由升级服务端程序告知当前是否有新版本,如果没有,则退出;如果有,从升级服务端程序获得需要更新的文件,保存在本地的临时文件夹中,下载完毕后,通知客户端程序有新版本,如果有必要,升级客户端程序将结束正在运行的客户端程序,将下载得到的新版本文件复制替换到客户端程序所在目录,然后升级客户端程序从新启动客户端程序,实现一次自动更新的过程。
  3. 升级服务端程序是个常驻内存的服务,实时监听来自升级客户端程序的连接请求,判断是否需要提供给升级客户端程序新版本的程序文件,即,当接收到升级客户端传送的版本号时,升级服务端程序将查询本地的配置文件,比较觉得是否有新版本程序,如果没有则告知升级客户端,如果有,则将已经存储在本地特定目录下的新版本文件通过约定好的协议,传递给升级客户端。

3 C#实现

3.1主要技术细节
3.1.1进程内通信

  使用内存映像文件机制实现升级客户端代理升级客户端程序传递数据。

  参见《.NET进程通信机制——内存映像文件》

3.1.2自定义消息

  导入win32api,SendMessage

        [DllImport( " User32.dll " , EntryPoint = " SendMessage " )] 
        
public   static   extern   int  SendMessage (IntPtr hwnd,  int  wmsg, IntPtr wparam, IntPtr lparam); 

  建立消息类

     class  Message
    
{
        
public const int USER            = 0x0400;
        
public const int WM_TEST    = USER+101;
        
public const int WM_CLOSE  = 0x0010;
    }

  发送自定义消息给指定窗体

public   void  SendMessage()
{
Win32.SendMessage(hFormHandle,Message.WM_TEST,IntPtr.Zero,IntPtr.Zero);
}

 

  窗体接收自定义消息,需要重载WndProc

         protected   override   void  WndProc( ref  Message m)
        
{
            
switch(m.Msg)
            
{
                
case Message.WM_TEST:
                    
//处理该自定消息的代码
                 break;
            }

            
base.WndProc (ref m);
        }
 
3.1.3 C#下代码调用外部程序
System.Diagnostics.Process proc  =  System.Diagnostics.Process.Start(
                
" d://ConsoleApplication2//bin/Debug//ConsoleApplication2.exe " );

 

3.2演示程序代码
 3.2.1升级客户端代理类
using  System;
using  System.Threading;
using  System.Runtime.InteropServices;

namespace  UpdateClient
{
    
/// <summary>
    
/// Class1 的摘要说明。
    
/// </summary>

    public class UpdateClientProxy
    
{
        
private static string _exeVersion = "";
        
private static string _exePhysicalPath = "";
        
private static IntPtr _exeHandle = IntPtr.Zero;
        
private static Thread _updateProc = null;

        
public static void BeginUpdate( string exeVersion,string exePhysicalPath,IntPtr exeHandle )
        
{
            _exeVersion 
= exeVersion;
            _exePhysicalPath 
= exePhysicalPath;
            _exeHandle 
= exeHandle;
            _updateProc 
= new Thread(new ThreadStart(UpdateProc));
            _updateProc.Start();
        }


        
private static void UpdateProc()
        
{
            IntPtr hVersion 
= CreateFileMapping( "exeVersion" );
            IntPtr hPhysicalPath 
= CreateFileMapping( "exePhysicalPath" );
            IntPtr hHandle 
= CreateFileMapping( "exeHandle" );

            WriteMapView( hVersion,_exeVersion );
            WriteMapView( hPhysicalPath,_exePhysicalPath );
            WriteMapView( hHandle,_exeHandle.ToString() );

            System.Diagnostics.Process proc 
= System.Diagnostics.Process.Start(
                
"d:/ConsoleApplication2/bin/Debug/ConsoleApplication2.exe");
        }


        
private static IntPtr CreateFileMapping( string name )
        
{
            IntPtr hFileMap 
= IntPtr.Zero;

            hFileMap 
= Win32.CreateFileMapping(Win32.InvalidHandleValue,
                IntPtr.Zero, Win32.PAGE_READWRITE,
                
00x100, name);

            
            
if (hFileMap == IntPtr.Zero)
                
throw new Exception("CreateFileMapping():无法创建内存映像文件");
            
return hFileMap;
        }


        
private static void WriteMapView( IntPtr hFileMap,string strValue )
        
{
            IntPtr address 
= Win32.MapViewOfFile(hFileMap, Win32.FILE_MAP_WRITE,
                
00, IntPtr.Zero);

            
byte[] bs = System.Text.Encoding.GetEncoding("gb2312").GetBytes(strValue);

            
for(int i=0;i<bs.Length;i++)
            
{
                Marshal.WriteByte(address,i,bs[i]);
            }

            Marshal.WriteByte(address,bs.Length,
0);

            Win32.UnmapViewOfFile(address);
        }


        
private static string ReadMapView( IntPtr hFileMap )
        
{
            IntPtr address 
= Win32.MapViewOfFile(hFileMap, Win32.FILE_MAP_WRITE,
                
00, IntPtr.Zero);

            
byte[] bs = new byte[0x100];
            
int i;
            
for(i=0;i<bs.Length;i++)
            
{
                
byte temp = Marshal.ReadByte(address,i);
                
if(temp==0)
                    
break;
                bs[i] 
= temp;
            
            }


            Win32.UnmapViewOfFile(address);

            
return System.Text.Encoding.GetEncoding("gb2312").GetString(bs,0,i);
        }

    }


    
internal class Win32
    
{
        [DllImport(
"User32.dll",EntryPoint="FindWindow")]
        
public  static extern IntPtr FindWindow(string lpClassName,
            
string lpWindowName);

        [DllImport(
"User32.dll", EntryPoint="SendMessage")] 
        
public static extern int sendmessage (IntPtr hwnd, int wmsg, IntPtr wparam, IntPtr lparam); 

        [DllImport(
"User32.dll", EntryPoint="SendMessage")] 
        
public static extern int sendmessage (IntPtr hwnd, int wmsg, IntPtr wparam, string lparam); 

        
public static readonly IntPtr InvalidHandleValue = new IntPtr(-1);
        
public const UInt32 FILE_MAP_WRITE = 2;
        
public const UInt32 PAGE_READWRITE = 0x04;

        [DllImport(
"Kernel32")]
        
public static extern IntPtr CreateFileMapping(IntPtr hFile,
            IntPtr pAttributes, UInt32 flProtect,
            UInt32 dwMaximumSizeHigh, UInt32 dwMaximumSizeLow, String pName);

        [DllImport(
"Kernel32")]
        
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,
            UInt32 dwDesiredAccess,
            UInt32 dwFileOffsetHigh, UInt32 dwFileOffsetLow,
            IntPtr dwNumberOfBytesToMap);

        [DllImport(
"Kernel32")]
        
public static extern Boolean UnmapViewOfFile(IntPtr address);

    }

}

3.2.2模拟的客户端程序
         private   void  Form1_Load( object  sender, System.EventArgs e)
        
{
            UpdateClient.UpdateClientProxy.BeginUpdate(
                
"1.0.0.1",
                AppDomain.CurrentDomain.BaseDirectory
+AppDomain.CurrentDomain.FriendlyName,
                
this.Handle
                );
        }


        
protected   override   void  WndProc( ref  Message m)
        
{
            
switch(m.Msg)
            
{
                
case Message.WM_TEST:
                    
new System.Threading.Thread(new System.Threading.ThreadStart(DemoProc)).Start();
                    
break;
            }

            
base.WndProc (ref m);
        }


        
private   void  DemoProc()
        
{
            
this.textBox1.Text = "升级程序检测到有新版本,即将关闭应用程序并进行升级操作!";
            System.Threading.Thread.Sleep(
5000);
            
this.Close();
        }
3.2.3模拟的升级客户端程序
        [STAThread]
        
static   void  Main( string [] args)
        
{
            
            Console.WriteLine(
"模拟的升级客户端程序已经被启动");
            
            IntPtr hVersion 
= FileMapping.CreateFileMapping( "exeVersion" );
            IntPtr hPhysicalPath 
= FileMapping.CreateFileMapping( "exePhysicalPath" );
            IntPtr hHandle 
= FileMapping.CreateFileMapping( "exeHandle" );

            
string _version = FileMapping.ReadMapView( hVersion );
            
string _physicalpath = FileMapping.ReadMapView( hPhysicalPath );
            
string _handle = FileMapping.ReadMapView( hHandle );

            
if(
                _version
!=null&&_version!="" &&
                _physicalpath
!=null&&_physicalpath!="" &&
                _handle
!=null&&_handle!="" )
            
{
                Console.WriteLine(
"接收到程序传送的版本号:{0}",_version);
                Console.WriteLine(
"接收到程序传送的物理路径:{0}",_physicalpath);
                Console.WriteLine(
"正在连接升级服务器......");

                Console.WriteLine(
"连接到升级服务器.");

                Console.WriteLine(
"获取升级列表");

                Console.WriteLine(
"正在下载文件a.dll");

                Console.WriteLine(
"正在下载文件b.dll");

                Console.WriteLine(
"正在下载文件abc.xml");

                Console.WriteLine(
"已经成功下载全部所需的更新文件");

                Console.WriteLine(
"通知主程序关闭自己");            

                Win32.sendmessage(
new IntPtr(Convert.ToInt32(_handle)),Message.WM_TEST,IntPtr.Zero,IntPtr.Zero);

                System.Threading.Thread.Sleep(
5000);

                Console.WriteLine(
"主程序已经关闭,升级客户端程序将进行文件替换");

                Console.WriteLine(
"正在替换a.dll");

                Console.WriteLine(
"正在替换b.dll");

                Console.WriteLine(
"正在替换abc.xml");

                Console.WriteLine(
"所有更新文件全部替换成功");

                Console.WriteLine(
"升级客户端程序将重新启动主程序");

                System.Diagnostics.Process.Start(_physicalpath);

            }

        }

 

 

版本08.11.03,有详细的帮助文档,完全免费。整个程序才80多K,但功能却不少,非常适合免费软件和共享软件作者。有以下功能: 1. 版本号比较(考虑到可能会有数据方面的升级,因此版本号不从程序自身中取,而是根据需要在配置时设定),同一个软件中允许有多个不同版本的程序或数据。 2. 根据版本号比较结果,从服务器中下载相应程序或数据进行升级,然后使用本地与服务器版本号一致。版本号可以分级,用“.”隔开,级数不限,如10或10.01或100.001.002等等。 3. 可以指定单个文件的存放目录,指定的目录若不存在时将会自动生成。 4. 升级时,如果相关的程序在运行,会自动地将其关闭,以保证升级的顺利进行。 5. 升级文件可以事先压缩好,放到服务器上,下载到本地后再自动进行解压。目前只支持ZIP格式。 6. 可以指定升级完成后自动启动一应用程序,有两种启动模式(常规与强制),同时还可以根据具体情况决定是否需要执行此应用程序。 7. 可以自定义标题栏图标(可以为动画),窗口右侧的图像。 8. 可以自定义自己的提示语(升级启动前后的提示语)。 9. 提供了常规显示、静默显示、无显示三种进度显示方式。 10. 可以指定升级后是否修改相关项的版本号。若设置为不修改,则可以使应用程序每次启动时都能从网络获取相关内容。 11. 应用程序可以通过消息方式获取自动升级完成后的结果。 12. 从版本V08.11.01开始,本软件可以进行自我更新。以后你只要下载最新的版本,放在你的服务器指定位置即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值