经常看一些的程序,有些一个解决方案带有多个项目,由于代码比较多,多人开发,所以好多vs.net下的工程是用source safe进行版本控制的。而用source safe进行版本控制需要局域网路径共享,因此好多项目换一台机器打开会出现一些问题,比如“解决方案看起来是受源代码管理,但无法找到它的绑定信息……”之类的提示信息很多。有时候修改了代码还保存不了,所以想把他去掉,下面是对项目管理前后的一些对比。 一、工程项目比较 同没有受Source Safe代码管理的工程相比: 1. 多出了.scc、.vssscc和.vspscc文件; 2. C#项目文件(.csproj)里面添加了几行标签: SccProjectName = "SAK" SccLocalPath = "SAK" SccAuxPath = "SAK" SccProvider = "SAK" 3.在解决方案文件(.sln)中,中增加了如下节点原素: GlobalSection(SourceCodeControl) = preSolution SccNumberOfProjects = 4 SccLocalPath0 = . …… SccLocalPath3 = SUBSCRIBE_TOOLS CanCheckoutShared = false EndGlobalSection 二、编写实现的类 既然文件增多了,还有有些文件被修改,所以想通过编程把他修改回原样,这样可能可以去掉那些提示信息,所以就写了下面的代码。 功能:去除C#.net的原代码Source safe 管理 using System; using System.IO; using System.Text; using System.Threading; namespace ZZ { /// /// 操作信息事件代理 /// public delegate void OperateNotifyHandler(object sender,VssEventArgs e); /// /// VssConverter 处理解决方案或项目的SourceSafe关联。 /// public class VssConverter { //操作根目录 private string operatePath; /// /// 操作信息事件 /// public event OperateNotifyHandler OperateNotify; /// /// 线程结束通知事件 /// public event EventHandler ThreadCompleted; /// /// 构造函数 /// /// 项目路径 public VssConverter(string operatePath) { this.operatePath = operatePath; } OperatePath属性,用来设置或获取当前需要处理的工程路径,不过在运行时最好不要设置他, /// /// 设置解决方案工程路径 /// public string OperatePath { get{return this.operatePath;} set{this.operatePath = value;} } 下面是一个public 修饰符的函数,也是类实例的惟一对外公开的方法,里面用了两个线程来分别删除文件和修改文件。 /// /// 去除Source Safe代码管理 /// public void RemoveVss() { Thread deleteThread = new Thread(new ThreadStart(DeleteVssFile)); Thread RemoveVssIdentifyThread = new Thread(new ThreadStart(RemoveVssIdentify)); deleteThread.Start(); RemoveVssIdentifyThread.Start(); } 后来测试了一下deleteThread的完成要比RemoveVssIdentifyThread快一些,当然也可以再开一个线程来分担文件的修改,不过这里需要注意的是好多文件是带只读属性的,所以还要把文件属性设置成Normal才能顺利完成操作,否则会抛出异常。 这里使用了递归来删除相关文件,由三个函数构成: /// /// 线程委托函数,完成删除"*.scc","*.vssscc"以及*.vspscc文件功能。 /// private void DeleteVssFile() { DeleteVssFile(this.operatePath); //通知删除文件结束 OnThreadCompleted(this,new EventArgs()); } /// /// 递归函数,删除"*.scc","*.vssscc"以及*.vspscc文件。 /// /// 当前处理路径 private void DeleteVssFile(string path) { DeleteFile(Directory.GetFiles(path,"*.scc")); DeleteFile(Directory.GetFiles(path,"*.vssscc")); DeleteFile(Directory.GetFiles(path,"*.vspscc")); foreach(string dir in Directory.GetDirectories(path)) DeleteVssFile(dir); } /// /// 删除文件,真正删除文件 /// /// private void DeleteFile(string [] files) { foreach(string file in files) { FileInfo fi = new FileInfo(file); fi.Attributes = FileAttributes.Normal; File.Delete(file); OnOperateNotify(this,new VssEventArgs(file+"删除完成")); } } 对于".sln"解决方案文件和".csproj’"C#项目文件的修改也采用了递归实现: /// /// 线程委托函数,去除"*.sln"解决方案文件和"*.csproj"C#项目文件的.Vss关联标签。 /// private void RemoveVssIdentify() { RemoveVssTag(this.operatePath); //通知去除标签结束 OnThreadCompleted(this,new EventArgs()); } /// /// 去除"*.sln"解决方案文件和"*.csproj"C#项目文件的.Vss关联标签。 /// /// 当前处理路径 private void RemoveVssTag(string path) { RemoveTagContent(Directory.GetFiles(path,"*.sln")); RemoveTagContent(Directory.GetFiles(path,"*.csproj")); foreach(string dir in Directory.GetDirectories(path)) RemoveVssTag(dir); } 下面的函数用来分析处理文件的修改,因为都是做删除部分文件内容的工作,所以把处理函数写成了一个, /// /// 去除"*.sln"解决方案文件和"*.csproj"C#项目文件的.Vss关联标签。 /// /// 当前处理文件 private void RemoveTagContent(string [] files) { foreach(string file in files) { string strStart; //Vss标签文本开始内容 string strEnd; //标签文本结束内容 int offSet;//结束标签文本的偏移量 FileInfo fi = new FileInfo(file); fi.Attributes =FileAttributes.Normal; if(fi.Extension == ".sln")//如果是解决方案文件 { strStart = "GlobalSection(SourceCodeControl)"; strEnd = "EndGlobalSection"; offSet = 19;//包含/r/n和空格 } else//如果是项目文件 { strStart = "SccProjectName"; strEnd = ">"; offSet = 0; } try { int start;//Vss标签文本开始索引 int end;//Vss标签文本结束索引 string content;//文件内容 using(FileStream fs = new FileStream(file,FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite)) { StreamReader sr = new StreamReader(fs); content = sr.ReadToEnd(); sr.Close(); start = content.IndexOf(strStart); } if(start!=-1)//文件需要去除标签 { using(FileStream fs = new FileStream(file,FileMode.Truncate,FileAccess.Write,FileShare.Read)) { end = start+content.Substring(start).IndexOf(strEnd)+offSet; content = content.Substring(0,start)+content.Substring(end); StreamWriter sw = new StreamWriter(fs); sw.Write(content); sw.Close(); } OnOperateNotify(this,new VssEventArgs(file+"去除标签完成")); } } catch(Exception ex) { OnOperateNotify(this,new VssEventArgs(file+"操作错误:"+ex.ToString())); } } } 当此为止,上面的程序实现了主要的功能,不过上面定义的事件,下面就是关于事件的函数, /// /// 操作信息事件通知 /// /// VssConverter /// 参数, protected virtual void OnOperateNotify(object sender,VssEventArgs e) { if(OperateNotify!=null) OperateNotify(sender,e); } /// /// 线程结束事件通知 /// /// VssConverter /// 参数 protected virtual void OnThreadCompleted(object sender,EventArgs e) { if(ThreadCompleted!=null) ThreadCompleted(sender,e); } } 相对于事件中的参数,这里定义了一个类从EventArgs继承,里面只包含一个字段用来保存信息, /// /// 消息通知事件参数类 /// public class VssEventArgs : EventArgs { private string message; /// /// 构造函数 /// /// public VssEventArgs(string message) { this.message = message; } /// /// 消息内容 /// public string Message { get{return this.message;} } } }//命名空间 三、测试使用 程序测试运行界面, 界面部分代码大多数由设计器生成,下面列出了主要添加代码, //委托,更新文本框 private delegate void AppendTextHandler(string content); //标记转换操作是否完成 private int convertOK =0; private System.Windows.Forms.TextBox textBoxFolder;//路径文本框 private System.Windows.Forms.Button buttonFolder;//浏览按钮 private System.Windows.Forms.TextBox textBoxInfo;//信息显示框 private System.Windows.Forms.Button buttonOK;//运行按钮 private System.Windows.Forms.Button buttonCancel;//退出按钮 按钮处理函数用来打开一个路径选择框, private void buttonFolder_Click(object sender, System.EventArgs e) { FolderBrowserDialog myDialog = new FolderBrowserDialog(); myDialog.ShowNewFolderButton = false; myDialog.Description = "选择需要处理的解决方案或项目目录"; if(myDialog.ShowDialog()==DialogResult.OK) this.textBoxFolder.Text = myDialog.SelectedPath; myDialog.Dispose(); } 运行函数,在这里面实例化VssConverter类,并调用了RemoveVss方法,运行时把几个按钮禁了,里面注册了两个事件,起信息传递作用,不过对于直接在地址栏中输入非法路径没有做具体判断, private void buttonOK_Click(object sender, System.EventArgs e) { if(this.textBoxFolder.Text.Length>1) { this.textBoxInfo.Clear(); this.convertOK = 0; this.buttonOK.Enabled = false; this.buttonFolder.Enabled = false; this.buttonCancel.Enabled = false; this.textBoxFolder.Enabled = false; VssConverter vssConverter = new VssConverter(this.textBoxFolder.Text); vssConverter.OperateNotify += new OperateNotifyHandler(vssConverter_OperateNotify); vssConverter.ThreadCompleted += new EventHandler(vssConverter_ThreadCompleted); vssConverter.RemoveVss(); } else MessageBox.Show("请输入解决方案或项目路径!"); } 下面是两个事件处理函数,第一个是用来在前台即时显示当前处理的文件信息,第二个函数是用来通知线程的执行结果。函数如下, // 信息通知 private void vssConverter_OperateNotify(object sender, VssEventArgs e) { AppendTextHandler ath = new AppendTextHandler(this.textBoxInfo.AppendText); this.textBoxInfo.BeginInvoke(ath,new object[]{e.Message+Environment.NewLine}); } // 线程结束通知 private void vssConverter_ThreadCompleted(object sender, EventArgs e) { if(this.convertOK==0) this.convertOK++; else { this.buttonOK.Enabled = true; this.buttonFolder.Enabled = true; this.buttonCancel.Enabled = true; this.textBoxFolder.Enabled = true; this.textBoxInfo.AppendText("#### 转换完成 ####"); } } 总结,程序通过搜索指定目录下的文件,根据扩展名进行相应的操作来完成处理,其中为了加快运行速度增加了线程来处理。 |