public static IList <KeyValuePair <string, Tuple <string, ModuleType> > > GetFolderModules(string folderPath) { var modulesText = new Dictionary <string, Tuple <string, ModuleType> >(); var extensions = new[] { ".bas", ".cls", ".frm" }; var projIni = new ProjectIni(Path.Combine(folderPath, "Project.INI")); if (File.Exists(Path.Combine(folderPath, "Project.INI.local"))) { projIni.AddFile(Path.Combine(folderPath, "Project.INI.local")); } var projEncoding = Encoding.GetEncoding(projIni.GetInt("General", "CodePage") ?? Encoding.Default.CodePage); foreach (var filePath in Directory.GetFiles(folderPath, "*.*").Where(s => extensions.Any(s.EndsWith)).Select(s => Path.Combine(folderPath, Path.GetFileName(s)))) { var moduleText = File.ReadAllText(filePath, projEncoding).TrimEnd('\r', '\n') + "\r\n"; modulesText[Path.GetFileNameWithoutExtension(filePath)] = Tuple.Create(moduleText, ModuleProcessing.TypeFromText(moduleText)); } return(modulesText.ToList()); }
public void Write(string filePath) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentException("Path to file cannot be null or empty.", nameof(filePath)); } File.Delete(Path.Combine(FolderPath, "vbaProject.bin")); var cf = new CompoundFile(); var vbaProject = cf.RootStorage; var projIni = new ProjectIni(Path.Combine(FolderPath, "Project.INI")); if (File.Exists(Path.Combine(FolderPath, "Project.INI.local"))) { projIni.AddFile(Path.Combine(FolderPath, "Project.INI.local")); } var projEncoding = Encoding.GetEncoding(projIni.GetInt("General", "CodePage") ?? Encoding.Default.CodePage); if (!projEncoding.Equals(Encoding.Default)) { projIni = new ProjectIni(Path.Combine(FolderPath, "Project.INI"), projEncoding); if (File.Exists(Path.Combine(FolderPath, "Project.INI.local"))) { projIni.AddFile(Path.Combine(FolderPath, "Project.INI.local")); } } var projSysKind = (uint)(projIni.GetInt("General", "SysKind") ?? 1); var projVersion = projIni.GetVersion("General", "Version") ?? new Version(1, 1); var refs = new List <Reference>(); foreach (var refName in projIni.GetReferenceNames()) { var refSubj = $"Reference {refName}"; if (projIni.GetString(refSubj, "LibIdTwiddled") != null) { refs.Add(new ReferenceControl { Cookie = (uint)(projIni.GetInt(refSubj, "Cookie") ?? 0), LibIdExtended = projIni.GetString(refSubj, "LibIdExtended"), LibIdTwiddled = projIni.GetString(refSubj, "LibIdTwiddled"), Name = refName, NameRecordExtended = projIni.GetString(refSubj, "NameRecordExtended"), OriginalLibId = projIni.GetString(refSubj, "OriginalLibId"), OriginalTypeLib = projIni.GetGuid(refSubj, "OriginalTypeLib") ?? Guid.Empty }); } else if (projIni.GetString(refSubj, "LibIdAbsolute") != null) { refs.Add(new ReferenceProject { LibIdAbsolute = projIni.GetString(refSubj, "LibIdAbsolute"), LibIdRelative = projIni.GetString(refSubj, "LibIdRelative"), Name = refName, Version = projIni.GetVersion(refSubj, "Version") }); } else { refs.Add(new ReferenceRegistered { LibId = projIni.GetString(refSubj, "LibId"), Name = refName }); } } var mods = new List <Module>(); var modExts = new[] { ".bas", ".cls", ".frm" }; foreach (var modPath in Directory.GetFiles(FolderPath).Where(s => modExts.Contains(Path.GetExtension(s) ?? "", StringComparer.InvariantCultureIgnoreCase))) { var modName = Path.GetFileNameWithoutExtension(modPath); var modText = File.ReadAllText(modPath, projEncoding); var modType = ModuleProcessing.TypeFromText(modText); projIni.RegisterModule(modName, modType, (uint)(projIni.GetInt("DocTLibVersions", modName) ?? 0)); mods.Add(new Module { DocString = GetAttribute(modText, "VB_Description"), HelpContext = GetAttributeUInt(modText, "VB_HelpID"), Name = modName, StreamName = modName, Type = modType }); } vbaProject.AddStream("PROJECT"); vbaProject.GetStream("PROJECT").SetData(projEncoding.GetBytes(projIni.GetProjectText())); vbaProject.AddStream("PROJECTlk"); vbaProject.GetStream("PROJECTlk").SetData(File.Exists(Path.Combine(FolderPath, "LicenseKeys.bin")) ? File.ReadAllBytes(Path.Combine(FolderPath, "LicenseKeys.bin")) : new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }); var projectWm = new List <byte>(); foreach (var modName in mods.Select(m => m.Name)) { projectWm.AddRange(projEncoding.GetBytes(modName)); projectWm.Add(0x00); projectWm.AddRange(Encoding.Unicode.GetBytes(modName)); projectWm.Add(0x00); projectWm.Add(0x00); } projectWm.AddRange(new byte[] { 0x00, 0x00 }); vbaProject.AddStream("PROJECTwm"); vbaProject.GetStream("PROJECTwm").SetData(projectWm.ToArray()); vbaProject.AddStorage("VBA"); var vbaProjectVba = vbaProject.GetStorage("VBA"); vbaProjectVba.AddStream("_VBA_PROJECT"); vbaProjectVba.GetStream("_VBA_PROJECT").SetData(new byte[] { 0xCC, 0x61, 0xFF, 0xFF, 0x00, 0x00, 0x00 }); var dirUc = new List <byte>(); // uncompressed dir stream dirUc.AddRange(BitConverter.GetBytes((short)0x0001)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(projSysKind)); dirUc.AddRange(BitConverter.GetBytes((short)0x0002)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(0x00000409)); // LCID dirUc.AddRange(BitConverter.GetBytes((short)0x0014)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(0x00000409)); // LCIDINVOKE dirUc.AddRange(BitConverter.GetBytes((short)0x0003)); dirUc.AddRange(BitConverter.GetBytes(2)); dirUc.AddRange(BitConverter.GetBytes((short)projEncoding.CodePage)); var projNameBytes = projEncoding.GetBytes(projIni.GetString("General", "Name") ?? ""); dirUc.AddRange(BitConverter.GetBytes((short)0x0004)); dirUc.AddRange(BitConverter.GetBytes(projNameBytes.Length)); dirUc.AddRange(projNameBytes); var projDocStringBytes = projEncoding.GetBytes(projIni.GetString("General", "Description") ?? ""); var projDocStringUtfBytes = Encoding.Unicode.GetBytes(projIni.GetString("General", "Description") ?? ""); dirUc.AddRange(BitConverter.GetBytes((short)0x0005)); dirUc.AddRange(BitConverter.GetBytes(projDocStringBytes.Length)); dirUc.AddRange(projDocStringBytes); dirUc.AddRange(BitConverter.GetBytes((short)0x0040)); dirUc.AddRange(BitConverter.GetBytes(projDocStringUtfBytes.Length)); dirUc.AddRange(projDocStringUtfBytes); var projHelpFileBytes = projEncoding.GetBytes(projIni.GetString("General", "HelpFile") ?? ""); dirUc.AddRange(BitConverter.GetBytes((short)0x0006)); dirUc.AddRange(BitConverter.GetBytes(projHelpFileBytes.Length)); dirUc.AddRange(projHelpFileBytes); dirUc.AddRange(BitConverter.GetBytes((short)0x003D)); dirUc.AddRange(BitConverter.GetBytes(projHelpFileBytes.Length)); dirUc.AddRange(projHelpFileBytes); dirUc.AddRange(BitConverter.GetBytes((short)0x0007)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(projIni.GetInt("General", "HelpContextID") ?? 0)); dirUc.AddRange(BitConverter.GetBytes((short)0x0008)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(0)); // LIBFLAGS dirUc.AddRange(BitConverter.GetBytes((short)0x0009)); dirUc.AddRange(BitConverter.GetBytes(4)); dirUc.AddRange(BitConverter.GetBytes(projVersion.Major)); dirUc.AddRange(BitConverter.GetBytes((short)projVersion.Minor)); var projConstantsBytes = projEncoding.GetBytes(projIni.GetConstantsString()); var projConstantsUtfBytes = Encoding.Unicode.GetBytes(projIni.GetConstantsString()); dirUc.AddRange(BitConverter.GetBytes((short)0x000C)); dirUc.AddRange(BitConverter.GetBytes(projConstantsBytes.Length)); dirUc.AddRange(projConstantsBytes); dirUc.AddRange(BitConverter.GetBytes((short)0x003C)); dirUc.AddRange(BitConverter.GetBytes(projConstantsUtfBytes.Length)); dirUc.AddRange(projConstantsUtfBytes); foreach (var rfc in refs) { dirUc.AddRange(rfc.GetBytes(projEncoding)); } dirUc.AddRange(BitConverter.GetBytes((short)0x000F)); dirUc.AddRange(BitConverter.GetBytes(2)); dirUc.AddRange(BitConverter.GetBytes((short)mods.Count)); dirUc.AddRange(BitConverter.GetBytes((short)0x0013)); dirUc.AddRange(BitConverter.GetBytes(2)); dirUc.AddRange(BitConverter.GetBytes((short)-1)); // PROJECTCOOKIE foreach (var mod in mods) { dirUc.AddRange(mod.GetBytes(projEncoding)); } dirUc.AddRange(BitConverter.GetBytes((short)0x0010)); dirUc.AddRange(BitConverter.GetBytes(0)); // global terminator vbaProjectVba.AddStream("dir"); vbaProjectVba.GetStream("dir").SetData(CompoundDocumentCompression.CompressPart(dirUc.ToArray())); foreach (var mod in mods) { switch (mod.Type) { case ModuleType.Class: case ModuleType.StaticClass: vbaProjectVba.AddStream(mod.StreamName); var fileText = File.ReadAllText(Path.Combine(FolderPath, mod.Name + ModuleProcessing.ExtensionFromType(mod.Type)), projEncoding); vbaProjectVba.GetStream(mod.StreamName).SetData(CompoundDocumentCompression.CompressPart(projEncoding.GetBytes(SeparateClassCode(fileText)))); break; case ModuleType.Form: string vbFrame; vbaProjectVba.AddStream(mod.StreamName); vbaProjectVba.GetStream(mod.StreamName).SetData(CompoundDocumentCompression.CompressPart( projEncoding.GetBytes(SeparateVbFrame(File.ReadAllText(Path.Combine(FolderPath, mod.Name + ".frm"), projEncoding), out vbFrame)))); vbaProject.AddStorage(mod.StreamName); var frmStorage = vbaProject.GetStorage(mod.StreamName); frmStorage.AddStream("\x03VBFrame"); frmStorage.GetStream("\x03VBFrame").SetData(projEncoding.GetBytes(vbFrame)); var b = File.ReadAllBytes(Path.Combine(FolderPath, mod.Name + ".frx")); using (var ms = new MemoryStream(b, 24, b.Length - 24)) { var frx = new CompoundFile(ms); CopyCfStreamsExcept(frx.RootStorage, frmStorage, "\x03VBFrame"); } break; default: vbaProjectVba.AddStream(mod.StreamName); vbaProjectVba.GetStream(mod.StreamName).SetData(CompoundDocumentCompression.CompressPart( File.ReadAllBytes(Path.Combine(FolderPath, mod.Name + ModuleProcessing.ExtensionFromType(mod.Type))))); break; } } cf.Save(Path.Combine(FolderPath, "vbaProject.bin")); if (Path.GetFileName(filePath).Equals("vbaProject.bin", StringComparison.InvariantCultureIgnoreCase)) { File.Copy(Path.Combine(FolderPath, "vbaProject.bin"), filePath, true); } else { FileStream fs = null; try { fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite); var sig = new byte[4]; fs.Read(sig, 0, 4); fs.Seek(0, SeekOrigin.Begin); if (sig.SequenceEqual(new byte[] { 0x50, 0x4b, 0x03, 0x04 })) { var zipFile = new ZipFile(fs); var zipEntry = zipFile.Cast <ZipEntry>().FirstOrDefault(e => e.Name.EndsWith("vbaProject.bin", StringComparison.InvariantCultureIgnoreCase)); if (zipEntry == null) { throw new ApplicationException("Cannot find 'vbaProject.bin' in ZIP archive."); } zipFile.BeginUpdate(); zipFile.Add(Path.Combine(FolderPath, "vbaProject.bin"), zipEntry.Name); zipFile.CommitUpdate(); zipFile.Close(); } else { var destCf = new CompoundFile(fs, CFSUpdateMode.Update, CFSConfiguration.Default); var destVbaProject = destCf.GetAllNamedEntries("_VBA_PROJECT_CUR").FirstOrDefault() as CFStorage ?? destCf.GetAllNamedEntries("OutlookVbaData").FirstOrDefault() as CFStorage ?? destCf.GetAllNamedEntries("Macros").FirstOrDefault() as CFStorage; var ls = new List <string>(); destVbaProject.VisitEntries(i => ls.Add(i.Name), true); while (ls.Count > 0) { DeleteStorageChildren(destVbaProject); ls.Clear(); destVbaProject.VisitEntries(i => ls.Add(i.Name), true); } CopyCfStreamsExcept(cf.RootStorage, destVbaProject, null); destCf.Commit(); //destCf.Save(fs); destCf.Close(); } } finally { fs?.Dispose(); } } }