上次做的那个导入器不够强大,限制还不小,用起来不方便。于是就再做一个这样的工具。代码基本上不同了,思想还是差不多,但功能肯定比之前的强大。经过了这次编写工具,对vdporj文件的了解又深一层了。
在vs的文件系统编辑器使用过程中,发现有两个不方便:第一是删除文件夹不方便,如果一个文件夹不是空的话,是删不成功的;第二是添加文件夹时,只能添加该文件夹的文件,如果指定的文件夹含有子文件夹,那些子文件夹就要手动一个个去添加,不会一同添加进去。
针对第二个问题,我就写出了之前的那个导入器,针对第一个问题,我就写了这个浏览器。这回就先看一下界面
功能大致有一下几个
同步文件夹:把“应用程序文件夹”里的某个文件夹与磁盘上的某个文件夹同步,使得与磁盘上的文件夹有相同的文件和子文件夹。
添加文件夹:把磁盘上某个文件夹的添加进来,包括了文件夹的文件和它的所有子文件夹的文件。
删除文件夹:把该文件夹的内容(包括文件和文件夹)删除。
添加文件:把一个或多个文件添加到文件夹中。
删除文件:把一个或多个文件删除。
不过像vs里面的复制,剪切,粘贴,拖拽这些功能就没有实现了。
介绍了工具的功能到介绍工具的实现了
下面则是所用到的类和它的体系结构图
这里的类大致上与之前的差不多,GUIDCreater是生成GUID值的;SpecialCharacter是记录处理一些特殊字符的;RegexCollection是存放着一些公共的正则表达式。GUIDCreater和SpecialCharacter的源码跟上次的一样。SetupFileInfo,SetupHierarchy,SetupDirectoryInfo这三个类又要唠叨一下。
带Setup开头的几个类,就是vdproj文件里面几个数据项的类,这次就新增了一个SetupHierachy类。这些类存放着各种对象的信息,以及一些自身的方法。
由平时对文件系统的理解和对vdproj文件的分析,得出这三个类的关系是这样的:
- SetupDirectoryInfo对象包含有若干个SetupDirectoryInfo对象和若干个SetupFileInfo对象;
- 一个SetupFileInfo对象只对应这一个SetupHierarchy对象;
因此SetupDirectoryInfo中应该要有一个存放SetupDirectoryInfo对象的集合和一个SetupFileInfo对象的集合;SetupFileInfo中有一个字段存放与之对应的SetupHierarchy对象。
这三个类的字段与属性定义如下
SetupHierarchy类的
1 public string MsmKey { get; set; } 2 3 private string _ownerKey; 4 public string OwnerKey 5 { 6 get 7 { 8 if (string.IsNullOrEmpty(_ownerKey)) 9 _ownerKey = "_UNDEFINED"; 10 return _ownerKey; 11 } 12 set 13 { 14 _ownerKey = value; 15 } 16 } 17 18 private string _msnSig; 19 public string MsmSig 20 { 21 get 22 { 23 if (string.IsNullOrEmpty(_msnSig)) 24 _msnSig = "_UNDEFINED"; 25 return _msnSig; 26 } 27 set 28 { 29 _msnSig = value; 30 } 31 }
SetupFileInfo类的
1 public string GUID_1 2 { get { return "1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE"; } } 3 public string SourcePath { get; set; } 4 public string TargetName { get; set; } 5 public string Folder { get; set; } 6 public string GUID_2 { get; set; } 7 public SetupHierarchy Hierachy { get; set; }
SetupDirectoryInfo类的
1 public static string GUID_1 2 { get { return "9EF0B969-E518-4E46-987F-47570745A589"; } } 3 public string GUID_2 { get; set; } 4 public string Property { get; set; } 5 public string Name { get; set; } 6 public List<SetupDirectoryInfo> Folders { get; set; } 7 public List<SetupFileInfo> Files { get; set; }
对这三个类的其他成员,就是把vdproj文件提取出来构造对象的方法还有把对象转换成字符串的方法。
整个工具的核心就是一个ExplorerCore类,它处理着整个各种业务,包括对vdproj的分析,增删改文件夹;它还管理所有文件夹,文件对象;
对于管理文件夹和文件,ExplorerCore这个核心类使用了6个集合和2个对象,分别是
- DirDictionary(文件夹字典集):利用文件夹对象SetupDirecotryInfo中GUID2值作为键值的字典集,目的在于能通过GUID值方便获取到文件夹对象
- DirFile(文件字典集):利用文件对象SetupFileInfo中GUID2值作为键值的字典集,目的在于能通过GUID值方便获取到文件对象
- FileList(文件列表):文件对象SetupFileInfo的列表,在生成所有文件的字符串时遍历使用。
- OrgHierachyList(无对应文件的Hierachy列表):存放着未与文件对应的Hierachy对象,刚从vdproj文件分析出来的Hierachy对象都先存放在此集合。
- OwnerHierachyList(有对应文件的Hierachy列表):存放着已于文件对应的Hierachy对象。
- GarbageGUID(已经废弃的GUID列表):存放着已被删除的文件或文件夹的GUID值
- OtherFiles(一些解析不成功的文件信息):以字符串的形式存放着解析不出来的文件信息
- RootFolder(根文件夹对象):整个文件系统树形结构的根,也就是“应用程序文件夹”的对象。
经过多次的实践得出,这个文件或文件夹的唯一标识码GUID值在vdproj文件中有很大的作用,文件和文件夹通过它进行关联,程序的快捷方式通过它跟指定的文件、文件夹进行关联。因此在删除一个文件或文件夹之后,一定要被删除的对象的GUID值记录起来。等到保存成vdproj的之后统一把文件存在的废弃GUID码删除替换掉。否则保存之后如果vdproj还存在着废弃的GUID码,vs会打不开那个vdproj文件的。
ExplorerCore这个核心类的方法就不逐一介绍了,到本文最后就会把整个类的代码粘贴出来。工具写出来了,毛病还是有的,不过至少满足现在的使用需求,能让我打包方便点儿,对类的分析好像还很混乱,有些地方为啥要这样做我也说不清楚(就比如那个SetupHierarchy的对象啥要分开成与文件关联和未与文件关联两个列表存放),还有一个弊病就是产生的字符串太多了,对GC不怎么友好。还有本文的思路很不清晰,呵呵,莫怪。
1 class ExplorerCore 2 { 3 private string FileFullName { get; set; } 4 private SetupDirectoryInfo RootFolder { get; set; } 5 6 private string _otherFiles; 7 private string OtherFiles 8 { 9 get 10 { 11 if (string.IsNullOrEmpty(_otherFiles)) 12 { 13 string vdpproj = File.ReadAllText(FileFullName, Encoding.UTF8); 14 _otherFiles = Regex.Match(vdpproj, RegexCollection.RegFilesInner).Value; 15 _otherFiles = Regex.Replace(_otherFiles, RegexCollection.RegFileString, ""); 16 _otherFiles = Regex.Replace(_otherFiles, @"s+ ", ""); 17 if (string.IsNullOrEmpty(_otherFiles)) 18 _otherFiles = " "; 19 } 20 return " "+_otherFiles+" "; 21 } 22 } 23 24 private Dictionary<string, SetupDirectoryInfo> _dirDictionary; 25 private Dictionary<string, SetupDirectoryInfo> DirDictionary 26 { 27 get 28 { 29 if (_dirDictionary == null) 30 _dirDictionary = new Dictionary<string, SetupDirectoryInfo>(); 31 return _dirDictionary; 32 } 33 } 34 35 36 private Dictionary<string, SetupFileInfo> _dirFile; 37 private Dictionary<string, SetupFileInfo> DirFile 38 { 39 get 40 { 41 if (_dirFile == null) 42 _dirFile =new Dictionary<string, SetupFileInfo>(); 43 return _dirFile; 44 } 45 } 46 47 private List<SetupFileInfo> _fileList; 48 private List<SetupFileInfo> FileList 49 { 50 get 51 { 52 if (_fileList == null) 53 _fileList = new List<SetupFileInfo>(); 54 return _fileList; 55 } 56 } 57 58 private List<SetupHierarchy> _orgHierachyList; 59 private List<SetupHierarchy> OrgHierachyList 60 { 61 get 62 { 63 if (_orgHierachyList == null) 64 _orgHierachyList = new List<SetupHierarchy>(); 65 return _orgHierachyList; 66 } 67 } 68 69 private List<SetupHierarchy> _ownerHierachyList; 70 private List<SetupHierarchy> OwnerHierachyList 71 { 72 get 73 { 74 if (_ownerHierachyList == null) 75 _ownerHierachyList = new List<SetupHierarchy>(); 76 return _ownerHierachyList; 77 } 78 } 79 80 private List<string> _garbageGUID; 81 private List<string> GarbageGUID 82 { 83 get 84 { 85 if (_garbageGUID == null) 86 _garbageGUID = new List<string>(); 87 return _garbageGUID; 88 } 89 } 90 91 private void GCGUID(string guid) 92 { 93 if (GarbageGUID.Contains(guid)) return; 94 GarbageGUID.Add(guid); 95 } 96 97 /// <summary> 98 /// 增加文件夹到集合中 99 /// </summary> 100 /// <param name="info"></param> 101 private void AddDirectory(SetupDirectoryInfo info) 102 { 103 if (!DirDictionary.ContainsKey(info.GUID_2)) 104 DirDictionary.Add(info.GUID_2, info); 105 } 106 107 /// <summary> 108 /// 增加文件到其所属文件夹中 109 /// </summary> 110 /// <param name="info"></param> 111 private void AddFileToDircotory(SetupFileInfo info) 112 { 113 if (DirDictionary.ContainsKey(info.Folder) && 114 !DirDictionary[info.Folder].Files.Contains(info)) 115 DirDictionary[info.Folder].Files.Add(info); 116 } 117 118 /// <summary> 119 /// 增加文件到文件列表中 120 /// </summary> 121 /// <param name="info"></param> 122 private void AddFile(SetupFileInfo info) 123 { 124 FileList.Add(info); 125 DirFile.Add(info.GUID_2, info); 126 } 127 128 /// <summary> 129 /// 把文件夹 文件 Hierachy组合在一起 130 /// </summary> 131 private void CombineObjects() 132 { 133 foreach (SetupFileInfo item in FileList) 134 AddFileToDircotory(item); 135 136 for (int i = 0; i < _orgHierachyList.Count; i++) 137 { 138 if (DirFile.ContainsKey(OrgHierachyList[i].MsmKey)) 139 { 140 DirFile[OrgHierachyList[i].MsmKey].Hierachy = OrgHierachyList[i]; 141 OwnerHierachyList.Add(OrgHierachyList[i]); 142 OrgHierachyList.RemoveAt(i); 143 i--; 144 } 145 } 146 } 147 148 /// <summary> 149 /// 初始化读入文档内容,分析并构建起树 150 /// </summary> 151 /// <param name="fileName"></param> 152 /// <returns></returns> 153 public SetupDirectoryInfo InitExplorer(string fileName) 154 { 155 FileFullName = fileName; 156 string vdprojContent = File.ReadAllText(fileName, Encoding.UTF8); 157 158 string strFolder = Regex.Match(vdprojContent, RegexCollection.RegFoldersInner).Value; 159 SetupDirectoryInfo root = new SetupDirectoryInfo() 160 { 161 GUID_2 = Regex.Match(vdprojContent, RegexCollection.RegAppFolderGuid2).Value, 162 Name = "应用程序文件夹", 163 Files = new List<SetupFileInfo>(), 164 Folders = new List<SetupDirectoryInfo>() 165 }; 166 RootFolder = root; 167 AddDirectory(root); 168 SetupDirectoryInfo.CreateSetupDirectoryInfo(strFolder, root,this.DirDictionary); 169 170 string strFiles = Regex.Match(vdprojContent, RegexCollection.RegFilesInner).Value; 171 MatchCollection fileMatch = Regex.Matches(strFiles, RegexCollection.RegFileString); 172 foreach (Match item in fileMatch) 173 AddFile(SetupFileInfo.CreateSetupFileInfo(item.Value)); 174 175 string strHierachy = Regex.Match(vdprojContent, RegexCollection.RegHierachyInner).Value; 176 MatchCollection hierachyMatch = Regex.Matches(strHierachy, RegexCollection.RegHierarchy); 177 foreach (Match item in hierachyMatch) 178 OrgHierachyList.Add(SetupHierarchy.CreateHierarchy(item.Value)); 179 180 CombineObjects(); 181 return root; 182 } 183 184 /// <summary> 185 /// 可视化界面 删除文件 186 /// </summary> 187 /// <param name="file"></param> 188 public void DeleteFile(SetupFileInfo file) 189 { 190 FileList.Remove(file); 191 DirFile.Remove(file.GUID_2); 192 OwnerHierachyList.Remove(file.Hierachy); 193 DirDictionary[file.Folder].Files.Remove(file); 194 GCGUID(file.GUID_2); 195 } 196 197 /// <summary> 198 /// 可视化界面 增加文件 199 /// </summary> 200 /// <param name="dir"></param> 201 /// <param name="fileInfo"></param> 202 public void AddFile(SetupDirectoryInfo dir, FileInfo fileInfo) 203 { 204 SetupFileInfo file = new SetupFileInfo(); 205 206 file.GUID_2 = GUIDCreater.CreateGUID2(); 207 file.SourcePath = fileInfo.FullName.Replace("\", "\\"); 208 file.TargetName = fileInfo.Name; 209 file.Folder = dir.GUID_2; 210 file.Hierachy = new SetupHierarchy(file); 211 212 AddFile(file); 213 AddFileToDircotory(file); 214 OwnerHierachyList.Add(file.Hierachy); 215 } 216 217 /// <summary> 218 /// 可视化界面 删除文件夹 219 /// </summary> 220 /// <param name="dir"></param> 221 public void DeleteDircetory(SetupDirectoryInfo dir,SetupDirectoryInfo parent) 222 { 223 //foreach (SetupFileInfo file in dir.Files) 224 while (dir.Files.Count>0) 225 DeleteFile(dir.Files[0]); 226 //foreach (SetupDirectoryInfo subDir in dir.Folders) 227 while (dir.Folders.Count>0) 228 DeleteDircetory(dir.Folders[0],dir); 229 parent.Folders.Remove(dir); 230 DirDictionary.Remove(dir.GUID_2); 231 GCGUID(dir.GUID_2); 232 } 233 234 /// <summary> 235 /// 可视化界面 增加文件夹 236 /// </summary> 237 /// <param name="dirInfo"></param> 238 /// <param name="dir"></param> 239 public void AddDirectory(SetupDirectoryInfo dirInfo, DirectoryInfo dir) 240 { 241 242 SetupDirectoryInfo currDir = new SetupDirectoryInfo(); 243 currDir.Name = dir.Name; 244 currDir.GUID_2 = GUIDCreater.CreateGUID2(); 245 currDir.Property = GUIDCreater.CreateGUID2(); 246 currDir.Files = new List<SetupFileInfo>(); 247 currDir.Folders = new List<SetupDirectoryInfo>(); 248 249 DirectoryInfo[] dirs = dir.GetDirectories(); 250 List<SetupDirectoryInfo> folderList = new List<SetupDirectoryInfo>(dirs.Length); 251 foreach (DirectoryInfo item in dirs) 252 AddDirectory(currDir, item); 253 AddDirectory(currDir); 254 dirInfo.Folders.Add(currDir); 255 256 FileInfo[] files = dir.GetFiles(); 257 List<SetupFileInfo> fileList = new List<SetupFileInfo>(files.Length); 258 foreach (FileInfo file in files) 259 AddFile(currDir, file); 260 } 261 262 /// <summary> 263 /// 可视化界面 同步文件夹 264 /// </summary> 265 /// <param name="dirInfo"></param> 266 /// <param name="dir"></param> 267 public void SynDirectory(SetupDirectoryInfo dirInfo, DirectoryInfo dir) 268 { 269 DirectoryInfo[] dirArry = dir.GetDirectories(); 270 List<SetupDirectoryInfo> newDirList = new List<SetupDirectoryInfo>(dirArry.Length); 271 SetupDirectoryInfo newDir = null; 272 foreach (DirectoryInfo item in dirArry) 273 { 274 newDir = dirInfo.ExistSubDirectory(item.Name); 275 if (newDir != null) 276 { 277 newDirList.Add(newDir); 278 dirInfo.Folders.Remove(newDir); 279 } 280 else 281 { 282 newDir = new SetupDirectoryInfo(); 283 newDir.Name = item.Name; 284 newDir.GUID_2 = GUIDCreater.CreateGUID2(); 285 newDir.Property = GUIDCreater.CreateGUID2(); 286 newDir.Files = new List<SetupFileInfo>(); 287 newDir.Folders = new List<SetupDirectoryInfo>(); 288 289 AddDirectory(newDir); 290 newDirList.Add(newDir); 291 } 292 SynDirectory(newDir, item); 293 } 294 while (dirInfo.Folders.Count > 0) 295 DeleteDircetory(dirInfo.Folders[0], dirInfo); 296 dirInfo.Folders = newDirList; 297 298 FileInfo[] fileArry = dir.GetFiles(); 299 List<SetupFileInfo> newFileList = new List<SetupFileInfo>(fileArry.Length); 300 SetupFileInfo newFile = null; 301 foreach (FileInfo item in fileArry) 302 { 303 newFile = dirInfo.ExistFile(item.Name); 304 if (newFile != null) 305 { 306 newFile.SourcePath = item.FullName.Replace("\", "\\"); 307 newFileList.Add(newFile); 308 dirInfo.Files.Remove(newFile); 309 } 310 else 311 { 312 SetupFileInfo file = new SetupFileInfo(); 313 314 file.GUID_2 = GUIDCreater.CreateGUID2(); 315 file.SourcePath = item.FullName.Replace("\", "\\"); 316 file.TargetName = item.Name; 317 file.Folder = dirInfo.GUID_2; 318 file.Hierachy = new SetupHierarchy(file); 319 320 AddFile(file); 321 //AddFileToDircotory(file); 322 newFileList.Add(file); 323 OwnerHierachyList.Add(file.Hierachy); 324 } 325 //AddFile(dirInfo, item); 326 } 327 while (dirInfo.Files.Count > 0) 328 DeleteFile(dirInfo.Files[0]); 329 dirInfo.Files = newFileList; 330 331 } 332 333 public void SaveFile() 334 { 335 Initial(); 336 StringBuilder sbFile = new StringBuilder(); 337 foreach (SetupFileInfo item in FileList) 338 sbFile.Append(item.CreateFileItemInfo()); 339 sbFile.Append(OtherFiles); 340 341 StringBuilder sbHierachy=new StringBuilder(); 342 foreach (SetupHierarchy item in OwnerHierachyList) 343 sbHierachy.Append(item.ToString()); 344 foreach (SetupHierarchy item in OrgHierachyList) 345 sbHierachy.Append(item.ToString()); 346 347 //string strFolders = RootFolder.CreateFoldersString(); 348 StringBuilder sbFolder = new StringBuilder(); 349 foreach (SetupDirectoryInfo item in RootFolder.Folders) 350 sbFolder.Append(item.CreateFoldersString()); 351 352 string vdprojContent = SpecialCharacter.ChangeSpecialCharacter(File.ReadAllText(FileFullName, Encoding.UTF8)); 353 File.Copy(FileFullName, FileFullName + ".bak", true); 354 355 vdprojContent =SpecialCharacter.RecoverSpecialCharacter( OverWriteContent(vdprojContent, 356 SpecialCharacter.ChangeSpecialCharacter( sbHierachy.ToString()), 357 SpecialCharacter.ChangeSpecialCharacter( sbFile.ToString()), 358 SpecialCharacter.ChangeSpecialCharacter( sbFolder.ToString()) 359 )); 360 vdprojContent= SafeCheckGUID(vdprojContent); 361 File.WriteAllText(FileFullName, vdprojContent, Encoding.UTF8); 362 Finish(); 363 } 364 365 366 private string SafeCheckGUID(string vdprojContent) 367 { 368 if (_garbageGUID == null) return vdprojContent; 369 foreach (string guidItem in GarbageGUID) 370 vdprojContent= vdprojContent.Replace(guidItem, ""); 371 return vdprojContent; 372 } 373 374 private string OverWriteContent(string vdprojContent, string strHierarchy, string strFiles, string strFolders) 375 { 376 vdprojContent = Regex.Replace(vdprojContent, RegexCollection.RegFilesInner, strFiles); 377 vdprojContent = Regex.Replace(vdprojContent, RegexCollection.RegFoldersInner, strFolders); 378 vdprojContent = Regex.Replace(vdprojContent, RegexCollection.RegHierachyInner, strHierarchy); 379 380 return vdprojContent; 381 } 382 383 private void Initial() 384 { 385 SpecialCharacter.AddCharacter("$"); 386 } 387 388 private void Finish() 389 { 390 SpecialCharacter.ClearRecord(); 391 GC.Collect(); 392 } 393 }