示例#1
0
        public void Ctor_Accepts_ReferenceOptions_Param_Array()
        {
            //Act
            var sc = new ReferenceControl(ReferenceOptions.ResolveAttributes());

            //Assert
            Assert.That(sc, Is.Not.Null);
        }
示例#2
0
        public void Can_Instantiate_New_ReferenceControl()
        {
            //Act
            var asc = new ReferenceControl();

            //Assert
            Assert.That(asc, Is.Not.Null);
        }
示例#3
0
        public void Can_Generate_Api_Xml()
        {
            //Arrange
            var expected = new XElement("ReferenceControls", new XAttribute("valueOnly", "true"));
            var asc      = new ReferenceControl(ReferenceOptions.ReturnValuesOnly());

            //Act
            var actual = asc.ToAdsml();

            //Assert
            Assert.That(actual.ToString(), Is.EqualTo(expected.ToString()));
        }
示例#4
0
        public void Can_Generate_Api_Xml_With_Outer_Node_XAttributes()
        {
            //Arrange
            var expected = new XElement("ReferenceControls", new XAttribute("foo", "bar"), new XAttribute("valueOnly", "true"));
            var asc      = new ReferenceControl(ReferenceOptions.ReturnValuesOnly())
            {
                OuterNodeAttributes = new List <XAttribute> {
                    new XAttribute("foo", "bar")
                }
            };

            //Act
            var actual = asc.ToAdsml();

            //Assert
            Assert.That(actual.ToString(), Is.EqualTo(expected.ToString()));
        }
示例#5
0
        public VbaFolder(string path, IDictionary <string, string> compareModules)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentException("Path to file cannot be null or empty.", nameof(path));
            }

            FileStream fs = null;

            try {
                fs         = new FileStream(path, FileMode.Open);
                FolderPath = $"{Path.GetTempPath()}VBASync-{Guid.NewGuid()}";
                Directory.CreateDirectory(FolderPath);

                CFStorage vbaProject;
                if (Path.GetFileName(path).Equals("vbaProject.bin", StringComparison.InvariantCultureIgnoreCase))
                {
                    vbaProject = new CompoundFile(fs).RootStorage;
                }
                else
                {
                    var sig = new byte[4];
                    fs.Read(sig, 0, 4);
                    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.");
                        }
                        using (var sw = File.Create(Path.Combine(FolderPath, "vbaProject.bin"))) {
                            StreamUtils.Copy(zipFile.GetInputStream(zipEntry), sw, new byte[4096]);
                        }
                        fs.Dispose();
                        fs         = new FileStream(Path.Combine(FolderPath, "vbaProject.bin"), FileMode.Open);
                        vbaProject = new CompoundFile(fs).RootStorage;
                    }
                    else
                    {
                        fs.Seek(0, SeekOrigin.Begin);
                        vbaProject = new CompoundFile(fs).GetAllNamedEntries("_VBA_PROJECT_CUR").FirstOrDefault() as CFStorage
                                     ?? new CompoundFile(fs).GetAllNamedEntries("OutlookVbaData").FirstOrDefault() as CFStorage
                                     ?? new CompoundFile(fs).GetAllNamedEntries("Macros").FirstOrDefault() as CFStorage;
                    }
                }
                if (vbaProject == null)
                {
                    throw new ApplicationException("Cannot find VBA project storage in file.");
                }

                var projectLk = vbaProject.TryGetStream("PROJECTlk");
                if (projectLk != null)
                {
                    File.WriteAllBytes(Path.Combine(FolderPath, "LicenseKeys.bin"), projectLk.GetData());
                }

                var    projEncoding   = Encoding.Default;
                uint   projSysKind    = 1;
                var    projVersion    = new Version(1, 1);
                var    projConstants  = new List <string>();
                string currentRefName = null;
                var    references     = new List <Reference>();
                var    originalLibIds = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase);
                var    modules        = new List <Module>();
                Module currentModule  = null;
                var    projStrings    = new List <string>();
                using (var br = new BinaryReader(new MemoryStream(DecompressStream(vbaProject.GetStorage("VBA").GetStream("dir"))))) {
                    Action <int> seek = i => br.BaseStream.Seek(i, SeekOrigin.Current);
                    while (br.BaseStream.Position < br.BaseStream.Length)
                    {
                        switch (br.ReadUInt16())
                        {
                        case 0x0001:
                            // PROJECTSYSKIND
                            seek(4);     // seek past size (always 4)
                            projSysKind = br.ReadUInt32();
                            break;

                        case 0x0002:
                        case 0x0014:
                        case 0x0008:
                        case 0x0007:
                            // PROJECTLCID, PROJECTLCIDINVOKE, PROJECTLIBFLAGS, and PROJECTHELPCONTEXT
                            seek(8);     // seek past whole record (always 8 bytes long)
                            break;

                        case 0x0003:
                            // PROJECTCODEPAGE
                            seek(4);     // seek past size (always 4)
                            projEncoding = Encoding.GetEncoding(br.ReadInt16());
                            break;

                        case 0x0004:
                            // PROJECTNAME
                            seek(br.ReadInt32());     // seek past whole record, since its contents are already in PROJECT
                            break;

                        case 0x0005:
                        case 0x0006:
                            // PROJECTDOCSTRING and PROJECTHELPFILEPATH
                            seek(br.ReadInt32() + 2);     // seek past whole record, since its contents are already in PROJECT
                            seek(br.ReadInt32());
                            break;

                        case 0x0009:
                            // PROJECTVERSION
                            seek(4);     // seek past Reserved
                            projVersion = new Version(br.ReadInt32(), br.ReadInt16());
                            break;

                        case 0x000c:
                            // PROJECTCONSTANTS
                            seek(br.ReadInt32() + 2);     // seek past Constants and Reserved
                            projConstants.AddRange(Encoding.Unicode.GetString(br.ReadBytes(br.ReadInt32())).Split(':').Select(s => s.Trim()));
                            if (projConstants.Count == 1 && string.IsNullOrEmpty(projConstants[0]))
                            {
                                projConstants.RemoveAt(0);
                            }
                            break;

                        case 0x0016:
                            // REFERENCENAME
                            seek(br.ReadInt32() + 2);     // seek past Name and Reserved
                            currentRefName = Encoding.Unicode.GetString(br.ReadBytes(br.ReadInt32()));
                            break;

                        case 0x0033:
                            // REFERENCEORIGINAL
                            originalLibIds.Add(currentRefName, projEncoding.GetString(br.ReadBytes(br.ReadInt32())));
                            break;

                        case 0x002f:
                            // REFERENCECONTROL (after optional REFERENCEORIGINAL)
                            seek(4);     // seek past SizeTwiddled
                            var libIdTwiddled = projEncoding.GetString(br.ReadBytes(br.ReadInt32()));
                            seek(6);     // seek past Reserved1 and Reserved2
                            string nameRecordExtended = null;
                            if (br.PeekChar() == 0x16)
                            {
                                // an optional REFERENCENAME record
                                seek(2);                  // seek past Id
                                seek(br.ReadInt32() + 2); // seek past Name and Reserved
                                nameRecordExtended = Encoding.Unicode.GetString(br.ReadBytes(br.ReadInt32()));
                            }
                            seek(6);     // seek past Reserved3 and SizeExtended
                            var libIdExtended = projEncoding.GetString(br.ReadBytes(br.ReadInt32()));
                            seek(6);     // seek past Reserved4 and Reserved5
                            var originalTypeLib = new Guid(br.ReadBytes(16));
                            var cookie          = br.ReadUInt32();
                            var refCtl          = new ReferenceControl {
                                Name               = currentRefName,
                                Cookie             = cookie, LibIdExtended = libIdExtended, LibIdTwiddled = libIdTwiddled,
                                NameRecordExtended = nameRecordExtended, OriginalTypeLib = originalTypeLib
                            };
                            if (originalLibIds.ContainsKey(currentRefName))
                            {
                                refCtl.OriginalLibId = originalLibIds[currentRefName];
                            }
                            references.Add(refCtl);
                            break;

                        case 0x000d:
                            // REFERENCEREGISTERED
                            seek(4);     // seek past Size
                            references.Add(new ReferenceRegistered {
                                Name = currentRefName, LibId = projEncoding.GetString(br.ReadBytes(br.ReadInt32()))
                            });
                            seek(6);     // seek past Reserved1 and Reserved2
                            break;

                        case 0x000e:
                            // REFERENCEPROJECT
                            seek(4);     // seek past Size
                            references.Add(new ReferenceProject {
                                Name          = currentRefName,
                                LibIdAbsolute = projEncoding.GetString(br.ReadBytes(br.ReadInt32())),
                                LibIdRelative = projEncoding.GetString(br.ReadBytes(br.ReadInt32())),
                                Version       = new Version(br.ReadInt32(), br.ReadInt16())
                            });
                            break;

                        case 0x000f:
                            // PROJECTMODULES
                            seek(6);     // ignore entire record
                            break;

                        case 0x0013:
                            // PROJECTCOOKIE
                            seek(6);     // ignore entire record
                            break;

                        case 0x0019:
                            // MODULENAME
                            seek(br.ReadInt32());     // ignore entire record
                            break;

                        case 0x0047:
                            // MODULENAMEUNICODE
                            modules.Add(currentModule = new Module {
                                Name = Encoding.Unicode.GetString(br.ReadBytes(br.ReadInt32()))
                            });
                            break;

                        case 0x001a:
                            // MODULESTREAMNAME
                            seek(br.ReadInt32() + 2);     // seek past StreamName and Reserved
                            currentModule.StreamName = Encoding.Unicode.GetString(br.ReadBytes(br.ReadInt32()));
                            break;

                        case 0x001c:
                            // MODULEDOCSTRING - ignore since this info is already in the module itself
                            seek(br.ReadInt32() + 2); // seek past DocString and Reserved
                            seek(br.ReadInt32());     // seek past DocStringUnicode
                            break;

                        case 0x0031:
                            // MODULEOFFSET
                            seek(4);     // seek past size (always 4)
                            currentModule.Offset = br.ReadUInt32();
                            break;

                        case 0x001e:
                            // MODULEHELPCONTEXT
                            seek(8);     // seek past entire record - this information is in the module itself as well
                            break;

                        case 0x002c:
                            // MODULECOOKIE
                            seek(6);     // ignore entire record
                            break;

                        case 0x0021:
                            // MODULETYPE - procedural flag
                            seek(4);     // ignore entire record since we get information about this from the PROJECT stream
                            break;

                        case 0x0022:
                            // MODULETYPE - document, class, or designer flag
                            seek(4);     // ignore entire record since we get information about this from the PROJECT stream
                            break;

                        case 0x0025:
                            // MODULEREADONLY
                            currentModule.ReadOnly = true;
                            seek(4);     // seek past Reserved
                            break;

                        case 0x0028:
                            // MODULEPRIVATE
                            currentModule.Private = true;
                            seek(4);     // seek past Reserved
                            break;

                        case 0x002b:
                            // module terminator
                            currentModule = null;
                            seek(4);
                            break;

                        case 0x0010:
                            // global terminator
                            seek(4);
                            break;

                        default:
                            seek(-2);
                            throw new ApplicationException($"Unknown record id '0x{br.ReadInt16().ToString("X4")}'.");
                        }
                    }
                }
                using (var sr = new StreamReader(new MemoryStream(vbaProject.GetStream("PROJECT").GetData()), projEncoding)) {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        var split     = line.Split('=');
                        var breakLoop = false;
                        switch (split[0]?.ToUpperInvariant())
                        {
                        case "MODULE":
                            modules.First(m => string.Equals(m.Name, split[1], StringComparison.InvariantCultureIgnoreCase))
                            .Type = ModuleType.Standard;
                            break;

                        case "DOCUMENT":
                            var split2 = split[1].Split('/');
                            var mod    = modules.First(m => string.Equals(m.Name, split2[0], StringComparison.InvariantCultureIgnoreCase));
                            mod.Type    = ModuleType.StaticClass;
                            mod.Version = uint.Parse(split2[1].Substring(2), NumberStyles.HexNumber);
                            break;

                        case "CLASS":
                            modules.First(m => string.Equals(m.Name, split[1], StringComparison.InvariantCultureIgnoreCase))
                            .Type = ModuleType.Class;
                            break;

                        case "BASECLASS":
                            modules.First(m => string.Equals(m.Name, split[1], StringComparison.InvariantCultureIgnoreCase))
                            .Type = ModuleType.Form;
                            break;

                        default:
                            if (line.Equals("[Workspace]", StringComparison.InvariantCultureIgnoreCase))
                            {
                                breakLoop = true;     // don't output all the cruft after [Workspace]
                            }
                            else
                            {
                                projStrings.Add(line);
                            }
                            break;
                        }
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                }
                DeleteBlankLinesFromEnd(projStrings);
                projStrings.Insert(0, $"Version={projVersion}");
                projStrings.Insert(0, $"SysKind={projSysKind}");
                projStrings.Insert(0, $"CodePage={projEncoding.CodePage}");
                projStrings.Add("");
                projStrings.Add("[Constants]");
                projStrings.AddRange(projConstants.Select(s => string.Join("=", s.Split('=').Select(t => t.Trim()))));
                if (modules.Any(m => m.Version > 0))
                {
                    projStrings.Add("");
                    projStrings.Add("[DocTLibVersions]");
                    projStrings.AddRange(modules.Where(m => m.Version > 0).Select(m => $"{m.Name}={m.Version}"));
                }
                foreach (var refer in references)
                {
                    projStrings.Add("");
                    projStrings.Add($"[Reference {refer.Name}]");
                    projStrings.AddRange(refer.GetConfigStrings());
                }

                File.WriteAllLines(Path.Combine(FolderPath, "Project.ini"), projStrings, projEncoding);

                ModuleTexts = new Dictionary <string, Tuple <string, ModuleType> >();
                foreach (var m in modules)
                {
                    var moduleText = projEncoding.GetString(DecompressStream(vbaProject.GetStorage("VBA").GetStream(m.StreamName), m.Offset));
                    if (compareModules.ContainsKey(m.Name))
                    {
                        moduleText = ModuleProcessing.FixCase(compareModules[m.Name], moduleText);
                    }
                    moduleText = (GetPrepend(m, vbaProject, projEncoding) + moduleText).TrimEnd('\r', '\n') + "\r\n";
                    ModuleTexts.Add(m.Name, Tuple.Create(moduleText, m.Type));
                    File.WriteAllText(Path.Combine(FolderPath, m.Name + ModuleProcessing.ExtensionFromType(m.Type)), moduleText, projEncoding);
                }

                foreach (var m in modules.Where(mod => mod.Type == ModuleType.Form))
                {
                    var cf = new CompoundFile();
                    CopyCfStreamsExcept(vbaProject.GetStorage(m.StreamName), cf.RootStorage, "\x0003VBFrame");
                    var frxPath = Path.Combine(FolderPath, m.Name + ".frx");
                    cf.Save(frxPath);
                    var bytes = File.ReadAllBytes(frxPath);
                    var size  = new FileInfo(frxPath).Length;
                    using (var bw = new BinaryWriter(new FileStream(frxPath, FileMode.Open))) {
                        bw.Write((short)0x424c);
                        bw.Write(new byte[3]);
                        bw.Write(size >> 8);
                        bw.Write(new byte[11]);
                        bw.Write(bytes);
                    }
                }
            } finally {
                fs.Dispose();
            }
        }