• C# 之Asp.net 安装包的创建,打包数据库


    [RunInstaller(true)]
        public partial class JSWyglInstaller : Installer
        {
            public JSWyglInstaller()
            {
               
                InitializeComponent();
            }
     
            #region 程序代码

        
            /// <summary>
            /// 数据库的操作
            /// </summary>
            #region  数据库的操作属性集合
            private System.Data.SqlClient.SqlConnection sqlConn;

            private System.Data.SqlClient.SqlCommand Command;

            private System.Data.SqlClient.SqlTransaction Trans;

            private string DBName;

            private string ServerName;

            private string AdminName;

            private string AdminPwd;
            ///dbname=[DBNAME] /server=[DBSERVERNAME] /user=[USERNAME] /pwd=PASSWORD] /iis=[IISSERVER] /port=[PORT] /targetdir="[TARGETDIR]\"
            #endregion
            /// <summary>
            ///IIS的操作
            /// </summary>
            #region IIS操作属性
            private string iis;

            private string port;

            private string dir;

            public static string VirDirSchemaName = "IIsWebVirtualDir";

            private string _target;

            private DirectoryEntry _iisServer;

            private ManagementScope _scope;

            private ConnectionOptions _connection;

            #endregion

           //连接数据库服务器到方法:
           #region ConnectDatabase 连接数据库

            private bool ConnectDatabase()
            {
                if (Command.Connection.State != ConnectionState.Open)
                {
                    try
                    {
                        Command.Connection.Open();
                    }

                    catch(Exception e)
                    {
                       MessageBox.Show("打开数据库失败:"+e.Message);
                       return false;
                    }

                }
                return true;
            }
            /// <summary>
            /// 开启事物
            /// </summary>
            private void BeginTrans()
            {
                if (Trans == null)
                    Trans = sqlConn.BeginTransaction();
                if(Command.Transaction==null)
                Command.Transaction = Trans;
            }
            /// <summary>
            /// 提交事物
            /// </summary>
            private void CommitTrans()
            {
                if (Trans != null)
                    Trans.Commit();
             
            }

            private void RollBack()
            {
                if (Trans != null)
                    Trans.Rollback();
            }

            #endregion

           //如果不能正确连接数据库服务器,请检查你的连接字符串,或者将连接字符串写入文件查看
           //读取SQL文件的方法:
           #region GetSql 从文件中读取SQL,在读取包含SQL脚本的文件时需要用到,参考自MSDN

                private string GetSql(string Name)
                {

                    try
                    {
                        //MessageBox.Show("GetSql"+Name);
                        Assembly Asm = Assembly.GetExecutingAssembly();

                  Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "."  Name);

                        StreamReader reader = new StreamReader(strm);

                        return reader.ReadToEnd();
                    }
                    catch (Exception getException)
                    {
                        MessageBox.Show("读取文件错误:" + getException.Message);
                        return string.Empty;
                        //throw new ApplicationException("读取文件错误:"+getException.Message);
                    }
                }

                #endregion

           //可以将此需要执行的SQL脚本放在此文本中
           //执行SQL语句的方法:

            #region ExecuteSql 执行SQL语句,参考自MSDN

            private void ExecuteSql(string DataBaseName, string sqlstring)
            {
                //MessageBox.Show("ExecuteSql:DataBaseName"+DataBaseName +"-SqlString:"+sqlstring);
                Command = new System.Data.SqlClient.SqlCommand(sqlstring, sqlConn);
                if (ConnectDatabase())
                {
                    try
                    {
                        Command.Connection.ChangeDatabase(DataBaseName);
                        Command.ExecuteNonQuery();
                        //MessageBox.Show("ChangeDataBaseSuccess");

                    }
                    finally
                    {
                        Command.Connection.Close();
                    }
                }
            }

            private void ExecuteSqlForTrans(string DataBaseName, string sqlstring)
            {
                //MessageBox.Show("ExecuteSql:DataBaseName"+DataBaseName +"-SqlString:"+sqlstring);
                if(Command==null)
                Command = new System.Data.SqlClient.SqlCommand(sqlstring, sqlConn);
                if (ConnectDatabase())
                {
                    try
                    {
                        BeginTrans();
                        Command.Connection.ChangeDatabase(DataBaseName);
                        Command.ExecuteNonQuery();
                        //MessageBox.Show("ChangeDataBaseSuccess");

                    }
                    catch(Exception ex)
                    {
                        if (Trans != null)
                            Trans.Rollback();
                        Command.Connection.Close();
                        //throw new Exception("创建数据库 [" + DBName + "] 错误:" + ex.Message);
                        MessageBox.Show("创建数据库 [" + DBName + "] 错误:" + ex.Message);
                    }
                }
            }


            #endregion

            //创建数据库及数据库表:
            #region CreateDBAndTable 创建数据库及数据库表,参考自MSDN

            protected bool CreateDBAndTable(string DBName)
            {
                bool Restult = false;

                try
                {
                    //MessageBox.Show("CreateDBAndTable");
                    if (string.IsNullOrEmpty(DBName))
                        return false;
                    ExecuteSqlForTrans("master", "USE MASTER IF EXISTS (SELECT NAME FROM SYSDATABASES WHERE NAME='" + DBName + "') DROP DATABASE " + DBName);
                    ExecuteSqlForTrans("master", "CREATE DATABASE " + DBName);
                    ExecuteSqlForTrans(DBName, GetSql("DBSQL.txt"));
                    CommitTrans();
                    Restult = true;
                }

                catch (Exception ex)
                {
                    MessageBox.Show("CreateDBAndTable"+ex.Message);
                    //throw new Exception("创建数据库 [" + DBName + "] 错误:" + ex.Message);
                }
                finally
                {
                    if(Command!=null)
                    Command.Connection.Close();
                }

                return Restult;

            }

            #endregion


            //从备份文件恢复数据库及数据库表
            #region RestoreDB 从备份文件恢复数据库及数据库表

            ///
            /// 从备份文件恢复数据库及数据库表
            ///
            ///数据库名
            ///配件中数据库脚本资源的名称
            ///

            protected bool RestoreDB(string DBName)
            {
               string dir = this.Context.Parameters["targetdir"];

                //MessageBox.Show("DBDir:" + dir);

                dir += @"DBInit\";

                bool Restult = false;

                string MSQL = "RESTORE DATABASE " + DBName +

                    " FROM DISK = N'" + dir + @"wygl.bak' " +

                    " WITH MOVE 'TXFee_Data' TO  N'" + @"c:\" + DBName + ".mdf', " +

                    " MOVE 'TXFee_log' TO N'" + @"c:\" + DBName + ".ldf' ";

                try
                {

                    ExecuteSql("master", "USE MASTER IF EXISTS (SELECT NAME FROM SYSDATABASES WHERE NAME='" + DBName + "') DROP DATABASE " + DBName);
                    ExecuteSql("master", MSQL);
                    //CommitTrans();
                    Restult = true;

                }
                catch(Exception ex)
                {
                    MessageBox.Show("创建数据库 [" + DBName + "] 错误:" + ex.Message);
                    //throw new Exception("创建数据库 [" + DBName + "] 错误:" + ex.Message);
                }
                finally
                {

                    if (Command != null)
                        Command.Connection.Close();
                    #region 作废
                    // 删除备份文件
                    //try
                    //{

                    //    File.Delete(dir + @"wygl.bak");

                    //}
                    //catch
                    //{

                    //}
                    #endregion


                }
                return Restult;

            }

            #endregion

            //这里可以到注册表读取SQL Server的安装路径,把恢复后的数据库文件放到data目录地下。
            //在本例中,只是实现了恢复,并未进行标准的操作。其中Test和Test_log时备份时数据库的文件信息。
            //如果想要从备份文件中恢复,请把文件包含到项目里并且设置和DBSQL.txt一样,嵌入到程序里。最后执行删除。
            //不过我想应该有办法不把文件先安装到目标机器上,而是有方法想读取DBSQL.txt文件一样,直接恢复数据库,不过确实没想到办法,失败!
            //网站安装好后,需要设置web.config文件,这里只涉及到连接字符串到设置,其他的可以同理修改。

            #region WriteWebConfig 修改web.config的连接数据库的字符串

            private bool WriteWebConfig()
            {
          
                System.IO.FileInfo FileInfo = new System.IO.FileInfo(this.Context.Parameters["targetdir"] + "/web.config");

                if (!FileInfo.Exists)
                {

                    throw new InstallException("未找到Web.Config文件 :" + this.Context.Parameters["targetdir"] + "/web.config");

                }
                System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();

                xmlDocument.Load(FileInfo.FullName);
            
                bool FoundIt = false,FondReportServer=false;

                foreach (System.Xml.XmlNode Node in xmlDocument["configuration"]["appSettings"])
                {
                    if (Node.Name == "add")
                    {
                        //根据安装设置连接字符串
                        //if (Node.Attributes.GetNamedItem("key").Value == "ConnectionString")
                        //{

                        //    Node.Attributes.GetNamedItem("value").Value = String.Format("Persist Security Info=False;Data Source={0};database={1};User ID={2};Password={3};Packet Size=4096;Pooling=true;Max Pool Size=100;Min Pool Size=1", ServerName, DBName, AdminName, AdminPwd);
                        //    FoundIt = true;                                           
                        //}
                        //根据安装设置连接字符串
                        if (Node.Attributes.GetNamedItem("key").Value == "SqlConnectionString")
                        {

                            Node.Attributes.GetNamedItem("value").Value = String.Format("Persist Security Info=False;Data Source={0};database={1};User ID={2};Password={3};Packet Size=4096;Pooling=true;Max Pool Size=100;Min Pool Size=1", ServerName, DBName, AdminName, AdminPwd);
                            FoundIt = true;
                        }
                        if (Node.Attributes.GetNamedItem("key").Value == "SqlConnectionADO")
                        {

                            Node.Attributes.GetNamedItem("value").Value = String.Format("Provider=SQLOLEDB.1;Password={3};Persist Security Info=False;User ID={2};Initial Catalog={1};Data Source={0}", ServerName, DBName, AdminName, AdminPwd);
                            FoundIt = true;
                        }
                        //根据安装设置连接字符串
                        if (Node.Attributes.GetNamedItem("key").Value == @"auV8CdDyCk/67slOZw5fh1KU7XuAjzSUq7ZCaBKfXsgHpdZ7RgCJHL0TWqbjkpIG")
                        {
                            string str= String.Format("Persist Security Info=False;Data Source={0};database={1};User ID={2};Password={3};Packet Size=4096;Pooling=true;Max Pool Size=100;Min Pool Size=1", ServerName, DBName, AdminName, AdminPwd);
                            Node.Attributes.GetNamedItem("value").Value = WYDAL.FangChan.StringUtils.CryptographyUtility(str);

                            FoundIt = true;
                        }
                        if (Node.Attributes.GetNamedItem("key").Value == "WebReportServer")
                        {
                            string [] dir=this.Context.Parameters["targetdir"].ToString().TrimEnd('\\').Split('\\');
                            //string dirRootString = this.Context.Parameters["targetdir"].ToString();
                            string dirRootName=string.Empty;
                            if(dir!=null && dir.Length>0)
                            {
                                dirRootName=dir[dir.Length-1];
                            }
                            //Node.Attributes.GetNamedItem("value").Value = String.Format("http://{0}/{1}/ReprotServer/YaWebReportService.asmx?WSDL",this.Context.Parameters["iis"] ,dirRootName );
                            Node.Attributes.GetNamedItem("value").Value = String.Format("http://{0}/WebReportServer/YaWebReportService.asmx?WSDL", this.Context.Parameters["iis"]);
                           // MessageBox.Show(this.Context.Parameters["targetdir"]);
                            FondReportServer = true;
                        }
                     
                    }
                }
                if (!FoundIt )
                {
                    throw new InstallException("查找配置文件的数据库服务信息出错!");
                }

                if( !FondReportServer)
                {
                     throw new InstallException("查找配置文件的报表服务器出错!");
                }
                xmlDocument.Save(FileInfo.FullName);

                return FoundIt;

            }
            /// <summary>
            /// 从WEBCONFIG 中获取数据库连接字符串
            /// </summary>
            /// <returns></returns>
            private string GetDBNameFromWebConfig()
            {

                //MessageBox.Show("webconfig");
                System.IO.FileInfo FileInfo = new System.IO.FileInfo(this.Context.Parameters["targetdir"] + "/web.config");

                if (!FileInfo.Exists)
                {
                    throw new InstallException("Missing config file :" + this.Context.Parameters["targetdir"] + "/web.config");

                }
                System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();

                xmlDocument.Load(FileInfo.FullName);

               // bool FoundIt = false, FondReportServer = false;

                foreach (System.Xml.XmlNode Node in xmlDocument["configuration"]["appSettings"])
                {
                    if (Node.Name == "add")
                    {
                        if (Node.Attributes.GetNamedItem("key").Value == "SqlConnectionString")
                        {

                           string strNodeConnection= Node.Attributes.GetNamedItem("value").Value ;//= String.Format("Persist Security Info=False;Data Source={0};database={1};User ID={2};Password={3};Packet Size=4096;Pooling=true;Max Pool Size=100;Min Pool Size=1", ServerName, DBName, AdminName, AdminPwd);
                           if (string.IsNullOrEmpty(strNodeConnection))
                           {
                               int dataBaseIndex=strNodeConnection.IndexOf("database=")+10;
                               int dataBaseEnd = strNodeConnection.Substring(dataBaseIndex, strNodeConnection.Length - dataBaseIndex - 1).IndexOf(";");
                               MessageBox.Show(strNodeConnection.Substring(dataBaseIndex, dataBaseEnd));
                               return strNodeConnection.Substring(dataBaseIndex,dataBaseEnd);
                           }
                        }
                      
                    }
                }
                return string.Empty;

            }


            #endregion


            #region WriteRegistryKey 写注册表。安装部署中,直接有一个注册表编辑器,可以在那里面设置。

            private void WriteRegistryKey()
            {
                // 写注册表

                RegistryKey hklm = Registry.LocalMachine;

                RegistryKey qzg = hklm.OpenSubKey("SOFTWARE", true);

                RegistryKey F = qzg.CreateSubKey("QzgWygl");

                //F.SetValue("FilePath", "kkkk"); old
                F.SetValue("FilePath", "QzgFxWygl");

            }

            #endregion

            //操作IIS,建立网站等。可参考:

            //用VS2005制作网页对IIS进行操作
            #region Connect 连接IIS服务器
            public bool Connect()
            {
                if (iis == null)

                    return false;

                try

                {

                    _iisServer = new DirectoryEntry("IIS://" + iis + "/W3SVC/1");

                    _target = iis;

                    _connection = new ConnectionOptions();

                    _scope = new ManagementScope(@"\\" + iis + @"\root\MicrosoftIISV2", _connection);

                    _scope.Connect();

                }

                catch

                {

                   
                    return false;

                }

                return IsConnected();

            }

            public bool IsConnected()

            {

                if (_target == null || _connection == null || _scope == null) return false;

                return _scope.IsConnected;

            }

            #endregion


            #region IsWebSiteExists 判断网站是否已经存在

            public bool IsWebSiteExists(string serverID)

            {

                try

                {

                    string siteName = "W3SVC/" + serverID;

                    ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery("SELECT * FROM IIsWebServer"), null);

                    ManagementObjectCollection webSites = searcher.Get();

                    foreach (ManagementObject webSite in webSites)

                    {

                        if ((string)webSite.Properties["Name"].Value == siteName)

                            return true;

                    }

                    return false;

                }

                catch

                {

                    return false;

                }

            }

            #endregion


            #region GetNextOpenID 获得一个新的ServerID

            private int GetNextOpenID()

            {

                DirectoryEntry iisComputer = new DirectoryEntry("IIS://localhost/w3svc");

                int nextID = 0;

                foreach (DirectoryEntry iisWebServer in iisComputer.Children)
                {

                    string sname = iisWebServer.Name;

                    try

                    {

                        int name = int.Parse(sname);

                        if (name > nextID)

                        {

                            nextID = name;

                        }

                    }

                    catch

                    {

                    }

                }

                return ++nextID;

            }

            #endregion


            #region CreateWebsite 添加网站

            public string CreateWebSite(string serverID, string serverComment, string defaultVrootPath, string HostName, string IP, string Port)

            {

                try

                {

                    ManagementObject oW3SVC = new ManagementObject(_scope, new ManagementPath(@"IIsWebService='W3SVC'"), null);

                    if (IsWebSiteExists(serverID))

                    {

                        return "Site Already Exists...";

                    }

                    ManagementBaseObject inputParameters = oW3SVC.GetMethodParameters("CreateNewSite");

                    ManagementBaseObject[] serverBinding = new ManagementBaseObject[1];

                    serverBinding[0] = CreateServerBinding(HostName, IP, Port);

                    inputParameters["ServerComment"] = serverComment;

                    inputParameters["ServerBindings"] = serverBinding;

                    inputParameters["PathOfRootVirtualDir"] = defaultVrootPath;

                    inputParameters["ServerId"] = serverID;

                   

                    ManagementBaseObject outParameter = null;

                    outParameter = oW3SVC.InvokeMethod("CreateNewSite", inputParameters, null);

                   

                    // 启动网站

                    string serverName = "W3SVC/" + serverID;

                    ManagementObject webSite = new ManagementObject(_scope, new ManagementPath(@"IIsWebServer='" + serverName + "'"), null);

                    webSite.InvokeMethod("Start", null);

                    return (string)outParameter.Properties["ReturnValue"].Value;

                }
                catch (Exception ex)
                {

                    return ex.Message;

                }

            }


            //创建网站管理服务
            public ManagementObject CreateServerBinding(string HostName, string IP, string Port)

            {

                try

                {

                    ManagementClass classBinding = new ManagementClass(_scope, new ManagementPath("ServerBinding"), null);

                    ManagementObject serverBinding = classBinding.CreateInstance();

                    serverBinding.Properties["Hostname"].Value = HostName;

                    serverBinding.Properties["IP"].Value = IP;

                    serverBinding.Properties["Port"].Value = Port;

                    serverBinding.Put();

                    return serverBinding;

                }

                catch

                {

                    return null;

                }

            }

            #endregion

            //好了,准备工作已经做完,现在开始写最重要的Install方法了

            //整个方法写完后如下:

            #region Install 安装

            ///

            /// 安装数据库

            ///

            ///

            public override void Install(IDictionary stateSaver)

            {
                base.Install(stateSaver);

                dir = this.Context.Parameters["targetdir"];

                DBName = this.Context.Parameters["dbname"].ToString();
              
              
                ServerName = this.Context.Parameters["server"].ToString();
             

                AdminName = this.Context.Parameters["user"].ToString();
         

                AdminPwd = this.Context.Parameters["pwd"].ToString();
            

                iis = this.Context.Parameters["iis"].ToString();
            

                port = this.Context.Parameters["port"].ToString();
             
                //MessageBox.Show("Initial-"+DBName+"-"+ServerName+"-"+AdminName+"-"+AdminPwd+"-iis:"+iis+"-"+port+"-"+dir+"}");

                //写入获取的安装程序中的变量,此段代码为调试用可以不添加
                if(sqlConn==null)
                sqlConn = new SqlConnection();
                this.sqlConn.ConnectionString = "Packet size=4096;User ID=" + AdminName + ";Data Source=" + ServerName + ";Password=" + AdminPwd + ";Persist Security Info=False;Integrated Security=false";


                //本安装程序采用附加数据库的方式,所以去掉本段代码
                // 执行SQL 安装数据库 可选择时恢复或者时直接创建
                //if (!CreateDBAndTable(DBName))
                //{
                //    MessageBox.Show("qzg-dbname");
                //    throw new ApplicationException("创建数据库时出现严重错误!");

                //}


                // 从备份数据库文件恢复数据库
                if (!RestoreDB(DBName))
                {
                    MessageBox.Show("恢复数据库时出现严重错误,请与管理员联系!");
                }
              
                 //部署桌面快捷方式*******************#region//部署桌面快捷方式*******************
                string strDeskTop = System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                StreamWriter sw2 = System.IO.File.CreateText(strDeskTop + "\\亿联物业管理系统.url");
                stateSaver.Add("DeskQuick", strDeskTop + "\\亿联物业管理系统.url"); //保存桌面快诫方式文件绝对地址
             
                string[] aryTh = dir.TrimEnd('\\').Split('\\');
                string webdirs = aryTh[aryTh.Length-1];

                 string Urls = @"URL=http://"+iis+"/" + webdirs + @"/default.aspx";
                 sw2.WriteLine("[InternetShortcut]");
                 sw2.WriteLine(Urls);
                 sw2.WriteLine("modified=228928983");
                 sw2.WriteLine("IconIndex=0");
                 sw2.WriteLine("IconFile=" + dir + "http://www.cnblogs.com/bingyun84/admin/file://wygl.ico/");
                 sw2.Flush();
                 sw2.Close();
                 //#endregio
                 ////部署开始菜单 ***************#region 部署开始菜单 ***************
                 string startMenue=Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
                
                 if(File.Exists(startMenue+"\\亿联物业管理系统.url"))
                 {
                      if(MessageBox.Show("开始菜单已经存在,是否有覆盖它?","安装开始菜单",MessageBoxButtons.YesNo,MessageBoxIcon.Question,
                       MessageBoxDefaultButton.Button1) == DialogResult.Yes)
                      {

                          File.Copy(strDeskTop + "\\亿联物业管理系统.url", startMenue + "\\亿联物业管理系统.url", true);
                      }
                      else
                      {
                        throw new InstallException("开始菜单已经存在,安装取消");
                      }
                 }
                 else
                 {
                   
                     File.Copy(strDeskTop + "\\亿联物业管理系统.url", startMenue + "\\亿联物业管理系统.url", false);
                 }
                 stateSaver.Add("StartMenueFile",startMenue+"\\亿联物业管理系统.url"); //保存开始菜单文件绝对地址
     
                // 添加网站
                Connect();
                // 下面的信息为测试,可以自己编写文本框来接收用户输入信息

                string serverID = "5555";

                string serverComment = "QZG";

                string defaultVrootPath = this.Context.Parameters["targetdir"];

                if (defaultVrootPath.EndsWith(@"\"))
                {
                    defaultVrootPath = defaultVrootPath.Substring(0, defaultVrootPath.Length-1);

                }

                string HostName = "";

                string IP = "";

                string Port = port;

                string sReturn = CreateWebSite(serverID, serverComment, defaultVrootPath, HostName, IP, Port);

                // 修改web.config
                if (!WriteWebConfig())
                {

                    //throw new ApplicationException("设置数据库连接字符串时出现错误");
                    MessageBox.Show("设置数据库连接字符串时出现错误");
                 

                }
                // 写注册表
                WriteRegistryKey();
            }

            #endregion

            //删除时的方法。在本文中未详细操作,比如删除站点,删除数据库等。如果需要,请你自己补足

            #region Uninstall 删除

            //QZG
            private bool DeleteDatabase()
            {
                try
                {
                    DBName = this.Context.Parameters["dbname"].ToString();
                    if (string.IsNullOrEmpty(DBName))
                    {
                      DBName=  GetDBNameFromWebConfig();
                    }
                    if (string.IsNullOrEmpty(DBName))
                        return false;
                    ExecuteSql("master", "USE MASTER IF EXISTS (SELECT NAME FROM SYSDATABASES WHERE NAME='" + DBName + "') DROP DATABASE  "+DBName+" ");
                    return true;

                }
                catch
                {
                    return false;
                }
            }
            public override void Uninstall(IDictionary savedState)
            {
                //MessageBox.Show("卸载过程....");
                if (savedState == null)
                    throw new ApplicationException("卸载数据库失败,未能卸载![qzg]");
                else
                {
                     base.Uninstall(savedState);
                     //删除快捷键,删除开始菜单#region 删除快捷键,删除开始菜单
                     //删除快捷键
                     if(File.Exists(savedState["DeskQuick"].ToString()))
                     {
                      File.Delete(savedState["DeskQuick"].ToString());
                     }
                     //删除开始菜单
                     if(File.Exists(savedState["StartMenueFile"].ToString()))
                     {
                      File.Delete(savedState["StartMenueFile"].ToString());
                     }
                     //#endregion
                    if (!DeleteDatabase())
                    {
                        MessageBox.Show("卸载数据库错误,请手动删除物业管理系统的数据库!");
                        //throw new ApplicationException("卸载过程中发生错误,未能卸载!");
                    }
                       
                }

            }
            //END

            #endregion

            //end
            #endregion
        }

  • 相关阅读:
    怎么使用git来管理项目版本?
    《我的四季》 张浩
    [代码片段]读取BMP文件(二)
    [代码片段]读取BMP文件
    《构建之法》阅读笔记02
    二维数组
    学习进度二
    《构建之法》阅读笔记01
    数组
    软件工程第一周开课博客
  • 原文地址:https://www.cnblogs.com/bingyun84/p/1681566.html
Copyright © 2020-2023  润新知