/******************************************************************************* * You may amend and distribute as you like, but don't remove this header! * * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. * See http://www.codeplex.com/EPPlus for details. * * Copyright (C) 2011 Jan Källman * * All code and executables are provided "as is" with no warranty either express or implied. * The author accepts no liability for any damage or loss of business that this product may cause. * * If you want to understand this code have a look at the Office VBA File Format Structure Specification (MS-OVBA.PDF) or * http://msdn.microsoft.com/en-us/library/cc313094(v=office.12).aspx * * * Code change notes: * * Author Change Date ******************************************************************************* * Jan Källman Added 26-MAR-2012 *******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Packaging; using System.IO; using OfficeOpenXml.Utils; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; using System.Text.RegularExpressions; namespace OfficeOpenXml.VBA { /// /// Represents the VBA project part of the package /// public class ExcelVbaProject { const string schemaRelVba = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"; internal const string PartUri = @"/xl/vbaProject.bin"; #region Classes & Enums /// /// Type of system where the VBA project was created. /// public enum eSyskind { Win16 = 0, Win32 = 1, Macintosh = 2, Win64 = 3 } #endregion internal ExcelVbaProject(ExcelWorkbook wb) { _wb = wb; _pck = _wb._package.Package; References = new ExcelVbaReferenceCollection(); Modules = new ExcelVbaModuleCollection(this); var rel = _wb.Part.GetRelationshipsByType(schemaRelVba).FirstOrDefault(); if (rel != null) { Uri = PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri); Part = _pck.GetPart(Uri); GetProject(); } else { Lcid = 0; Part = null; } } internal ExcelWorkbook _wb; internal Package _pck; #region Dir Stream Properties /// /// System kind. Default Win32. /// public eSyskind SystemKind { get; set; } /// /// Name of the project /// public string Name { get; set; } /// /// A description of the project /// public string Description { get; set; } /// /// A helpfile /// public string HelpFile1 { get; set; } /// /// Secondary helpfile /// public string HelpFile2 { get; set; } /// /// Context if refering the helpfile /// public int HelpContextID { get; set; } /// /// Conditional compilation constants /// public string Constants { get; set; } /// /// Codepage for encoding. Default is current regional setting. /// public int CodePage { get; internal set; } internal int LibFlags { get; set; } internal int MajorVersion { get; set; } internal int MinorVersion { get; set; } internal int Lcid { get; set; } internal int LcidInvoke { get; set; } internal string ProjectID { get; set; } internal string ProjectStreamText { get; set; } /// /// Project references /// public ExcelVbaReferenceCollection References { get; set; } /// /// Code Modules (Modules, classes, designer code) /// public ExcelVbaModuleCollection Modules { get; set; } ExcelVbaSignature _signature = null; /// /// The digital signature /// public ExcelVbaSignature Signature { get { if (_signature == null) { _signature=new ExcelVbaSignature(Part); } return _signature; } } ExcelVbaProtection _protection=null; /// /// VBA protection /// public ExcelVbaProtection Protection { get { if (_protection == null) { _protection = new ExcelVbaProtection(this); } return _protection; } } #endregion #region Read Project private void GetProject() { var stream = Part.GetStream(); byte[] vba; vba = new byte[stream.Length]; stream.Read(vba, 0, (int)stream.Length); Document = new CompoundDocument(vba); ReadDirStream(); ProjectStreamText = Encoding.GetEncoding(CodePage).GetString(Document.Storage.DataStreams["PROJECT"]); ReadModules(); ReadProjectProperties(); } private void ReadModules() { foreach (var modul in Modules) { var stream = Document.Storage.SubStorage["VBA"].DataStreams[modul.streamName]; var byCode = CompoundDocument.DecompressPart(stream, (int)modul.ModuleOffset); string code = Encoding.GetEncoding(CodePage).GetString(byCode); int pos=0; while(pos+90) { lineSplit = code.Substring(pos + 9, linePos - pos - 9).Split('='); } else { lineSplit=code.Substring(pos+9).Split(new char[]{'='},1); } if (lineSplit.Length > 1) { lineSplit[1] = lineSplit[1].Trim(); var attr = new ExcelVbaModuleAttribute() { Name = lineSplit[0].Trim(), DataType = lineSplit[1].StartsWith("\"") ? eAttributeDataType.String : eAttributeDataType.NonString, Value = lineSplit[1].StartsWith("\"") ? lineSplit[1].Substring(1, lineSplit[1].Length - 2) : lineSplit[1] }; modul.Attributes._list.Add(attr); } pos = linePos + 2; } modul.Code=code.Substring(pos); } } private void ReadProjectProperties() { _protection = new ExcelVbaProtection(this); string prevPackage = ""; var lines = Regex.Split(ProjectStreamText, "\r\n"); foreach (string line in lines) { if (line.StartsWith("[")) { } else { var split = line.Split('='); if (split.Length > 1 && split[1].Length > 1 && split[1].StartsWith("\"")) //Remove any double qouates { split[1] = split[1].Substring(1, split[1].Length - 2); } switch (split[0]) { case "ID": ProjectID = split[1]; break; case "Document": string mn = split[1].Substring(0, split[1].IndexOf("/&H")); Modules[mn].Type = eModuleType.Document; break; case "Package": prevPackage = split[1]; break; case "BaseClass": Modules[split[1]].Type = eModuleType.Designer; Modules[split[1]].ClassID = prevPackage; break; case "Module": Modules[split[1]].Type = eModuleType.Module; break; case "Class": Modules[split[1]].Type = eModuleType.Class; break; case "HelpFile": case "Name": case "HelpContextID": case "Description": case "VersionCompatible32": break; //393222000" case "CMG": byte[] cmg = Decrypt(split[1]); _protection.UserProtected = (cmg[0] & 1) != 0; _protection.HostProtected = (cmg[0] & 2) != 0; _protection.VbeProtected = (cmg[0] & 4) != 0; break; case "DPB": byte[] dpb = Decrypt(split[1]); if (dpb.Length >= 28) { byte reserved = dpb[0]; var flags = new byte[3]; Array.Copy(dpb, 1, flags, 0, 3); var keyNoNulls = new byte[4]; _protection.PasswordKey = new byte[4]; Array.Copy(dpb, 4, keyNoNulls, 0, 4); var hashNoNulls = new byte[20]; _protection.PasswordHash = new byte[20]; Array.Copy(dpb, 8, hashNoNulls, 0, 20); //Handle 0x00 bitwise 2.4.4.3 for (int i = 0; i < 24; i++) { int bit = 128 >> (int)((i % 8)); if (i < 4) { if ((int)(flags[0] & bit) == 0) { _protection.PasswordKey[i] = 0; } else { _protection.PasswordKey[i] = keyNoNulls[i]; } } else { int flagIndex = (i - i % 8) / 8; if ((int)(flags[flagIndex] & bit) == 0) { _protection.PasswordHash[i - 4] = 0; } else { _protection.PasswordHash[i - 4] = hashNoNulls[i - 4]; } } } } break; case "GC": _protection.VisibilityState = Decrypt(split[1])[0] == 0xFF; break; } } } } /// /// 2.4.3.3 Decryption /// /// Byte hex string /// The decrypted value private byte[] Decrypt(string value) { byte[] enc = GetByte(value); byte[] dec = new byte[(value.Length - 1)]; byte seed, version, projKey, ignoredLength; seed = enc[0]; dec[0] = (byte)(enc[1] ^ seed); dec[1] = (byte)(enc[2] ^ seed); for (int i = 2; i < enc.Length - 1; i++) { dec[i] = (byte)(enc[i + 1] ^ (enc[i - 1] + dec[i - 1])); } version = dec[0]; projKey = dec[1]; ignoredLength = (byte)((seed & 6) / 2); int datalength = BitConverter.ToInt32(dec, ignoredLength + 2); var data = new byte[datalength]; Array.Copy(dec, 6 + ignoredLength, data, 0, datalength); return data; } /// /// 2.4.3.2 Encryption /// /// /// Byte hex string private string Encrypt(byte[] value) { byte[] seed = new byte[1]; var rn = RandomNumberGenerator.Create(); rn.GetBytes(seed); BinaryWriter br = new BinaryWriter(new MemoryStream()); byte[] enc = new byte[value.Length + 10]; enc[0] = seed[0]; enc[1] = (byte)(2 ^ seed[0]); byte projKey = 0; foreach (var c in ProjectID) { projKey += (byte)c; } enc[2] = (byte)(projKey ^ seed[0]); var ignoredLength = (seed[0] & 6) / 2; for (int i = 0; i < ignoredLength; i++) { br.Write(seed[0]); } br.Write(value.Length); br.Write(value); int pos = 3; byte pb = projKey; foreach (var b in ((MemoryStream)br.BaseStream).ToArray()) { enc[pos] = (byte)(b ^ (enc[pos - 2] + pb)); pos++; pb = b; } return GetString(enc, pos - 1); } private string GetString(byte[] value, int max) { string ret = ""; for (int i = 0; i <= max; i++) { if (value[i] < 16) { ret += "0" + value[i].ToString("x"); } else { ret += value[i].ToString("x"); } } return ret.ToUpper(); } private byte[] GetByte(string value) { byte[] ret = new byte[value.Length / 2]; for (int i = 0; i < ret.Length; i++) { ret[i] = byte.Parse(value.Substring(i * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier); } return ret; } private void ReadDirStream() { byte[] dir = CompoundDocument.DecompressPart(Document.Storage.SubStorage["VBA"].DataStreams["dir"]); MemoryStream ms = new MemoryStream(dir); BinaryReader br = new BinaryReader(ms); ExcelVbaReference currentRef = null; string referenceName = ""; ExcelVBAModule currentModule = null; bool terminate = false; while (br.BaseStream.Position < br.BaseStream.Length && terminate == false) { ushort id = br.ReadUInt16(); uint size = br.ReadUInt32(); switch (id) { case 0x01: SystemKind = (eSyskind)br.ReadUInt32(); break; case 0x02: Lcid = (int)br.ReadUInt32(); break; case 0x03: CodePage = (int)br.ReadUInt16(); break; case 0x04: Name = GetString(br, size); break; case 0x05: Description = GetUnicodeString(br, size); break; case 0x06: HelpFile1 = GetString(br, size); break; case 0x3D: HelpFile2 = GetString(br, size); break; case 0x07: HelpContextID = (int)br.ReadUInt32(); break; case 0x08: LibFlags = (int)br.ReadUInt32(); break; case 0x09: MajorVersion = (int)br.ReadUInt32(); MinorVersion = (int)br.ReadUInt16(); break; case 0x0C: Constants = GetUnicodeString(br, size); break; case 0x0D: uint sizeLibID = br.ReadUInt32(); var regRef = new ExcelVbaReference(); regRef.Name = referenceName; regRef.ReferenceRecordID = id; regRef.Libid = GetString(br, sizeLibID); uint reserved1 = br.ReadUInt32(); ushort reserved2 = br.ReadUInt16(); References.Add(regRef); break; case 0x0E: var projRef = new ExcelVbaReferenceProject(); projRef.ReferenceRecordID = id; projRef.Name = referenceName; sizeLibID = br.ReadUInt32(); projRef.Libid = GetString(br, sizeLibID); sizeLibID = br.ReadUInt32(); projRef.LibIdRelative = GetString(br, sizeLibID); projRef.MajorVersion = br.ReadUInt32(); projRef.MinorVersion = br.ReadUInt16(); References.Add(projRef); break; case 0x0F: ushort modualCount = br.ReadUInt16(); break; case 0x13: ushort cookie = br.ReadUInt16(); break; case 0x14: LcidInvoke = (int)br.ReadUInt32(); break; case 0x16: referenceName = GetUnicodeString(br, size); break; case 0x19: currentModule = new ExcelVBAModule(); currentModule.Name = GetUnicodeString(br, size); Modules.Add(currentModule); break; case 0x1A: currentModule.streamName = GetUnicodeString(br, size); break; case 0x1C: currentModule.Description = GetUnicodeString(br, size); break; case 0x1E: currentModule.HelpContext = (int)br.ReadUInt32(); break; case 0x21: case 0x22: break; case 0x2B: //Modul Terminator break; case 0x2C: currentModule.Cookie = br.ReadUInt16(); break; case 0x31: currentModule.ModuleOffset = br.ReadUInt32(); break; case 0x10: terminate = true; break; case 0x30: var extRef = (ExcelVbaReferenceControl)currentRef; var sizeExt = br.ReadUInt32(); extRef.LibIdExternal = GetString(br, sizeExt); uint reserved4 = br.ReadUInt32(); ushort reserved5 = br.ReadUInt16(); extRef.OriginalTypeLib = new Guid(br.ReadBytes(16)); extRef.Cookie = br.ReadUInt32(); break; case 0x33: currentRef = new ExcelVbaReferenceControl(); currentRef.ReferenceRecordID = id; currentRef.Name = referenceName; currentRef.Libid = GetString(br, size); References.Add(currentRef); break; case 0x2F: var contrRef = (ExcelVbaReferenceControl)currentRef; contrRef.ReferenceRecordID = id; var sizeTwiddled = br.ReadUInt32(); contrRef.LibIdTwiddled = GetString(br, sizeTwiddled); var r1 = br.ReadUInt32(); var r2 = br.ReadUInt16(); break; case 0x25: currentModule.ReadOnly = true; break; case 0x28: currentModule.Private = true; break; default: break; } } } #endregion #region Save Project internal void Save() { if (Validate()) { CompoundDocument doc = new CompoundDocument(); doc.Storage = new CompoundDocument.StoragePart(); var store = new CompoundDocument.StoragePart(); doc.Storage.SubStorage.Add("VBA", store); store.DataStreams.Add("_VBA_PROJECT", CreateVBAProjectStream()); store.DataStreams.Add("dir", CreateDirStream()); foreach (var module in Modules) { store.DataStreams.Add(module.Name, CompoundDocument.CompressPart(Encoding.GetEncoding(CodePage).GetBytes(module.Attributes.GetAttributeText() + module.Code))); } //Copy streams from the template, if used. if (Document != null) { foreach (var ss in Document.Storage.SubStorage) { if (ss.Key != "VBA") { doc.Storage.SubStorage.Add(ss.Key, ss.Value); } } foreach (var s in Document.Storage.DataStreams) { if (s.Key != "dir" && s.Key != "PROJECT" && s.Key != "PROJECTwm") { doc.Storage.DataStreams.Add(s.Key, s.Value); } } } doc.Storage.DataStreams.Add("PROJECT", CreateProjectStream()); doc.Storage.DataStreams.Add("PROJECTwm", CreateProjectwmStream()); if (Part == null) { Uri = new Uri(PartUri, UriKind.Relative); Part = _pck.CreatePart(Uri, ExcelPackage.schemaVBA); var rel = _wb.Part.CreateRelationship(Uri, TargetMode.Internal, schemaRelVba); } var vbaBuffer=doc.Save(); var st = Part.GetStream(FileMode.Create); st.Write(vbaBuffer, 0, vbaBuffer.Length); st.Flush(); st.Close(); //Save the digital signture Signature.Save(this); } } private bool Validate() { Description = Description ?? ""; HelpFile1 = HelpFile1 ?? ""; HelpFile2 = HelpFile2 ?? ""; Constants = Constants ?? ""; return true; } /// /// MS-OVBA 2.3.4.1 /// /// private byte[] CreateVBAProjectStream() { BinaryWriter bw = new BinaryWriter(new MemoryStream()); bw.Write((ushort)0x61CC); //Reserved1 bw.Write((ushort)0xFFFF); //Version bw.Write((byte)0x0); //Reserved3 bw.Write((ushort)0x0); //Reserved4 return ((MemoryStream)bw.BaseStream).ToArray(); } /// /// MS-OVBA 2.3.4.1 /// /// private byte[] CreateDirStream() { BinaryWriter bw = new BinaryWriter(new MemoryStream()); /****** PROJECTINFORMATION Record ******/ bw.Write((ushort)1); //ID bw.Write((uint)4); //Size bw.Write((uint)SystemKind); //SysKind bw.Write((ushort)2); //ID bw.Write((uint)4); //Size bw.Write((uint)Lcid); //Lcid bw.Write((ushort)0x14); //ID bw.Write((uint)4); //Size bw.Write((uint)LcidInvoke); //Lcid Invoke bw.Write((ushort)3); //ID bw.Write((uint)2); //Size bw.Write((ushort)CodePage); //Codepage //ProjectName bw.Write((ushort)4); //ID bw.Write((uint)Name.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Name)); //Project Name //Description bw.Write((ushort)5); //ID bw.Write((uint)Description.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Description)); //Project Name bw.Write((ushort)0x40); //ID bw.Write((uint)Description.Length*2); //Size bw.Write(Encoding.Unicode.GetBytes(Description)); //Project Description //Helpfiles bw.Write((ushort)6); //ID bw.Write((uint)HelpFile1.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(HelpFile1)); //HelpFile1 bw.Write((ushort)0x3D); //ID bw.Write((uint)HelpFile2.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(HelpFile2)); //HelpFile2 //Help context id bw.Write((ushort)7); //ID bw.Write((uint)4); //Size bw.Write((uint)HelpContextID); //Help context id //Libflags bw.Write((ushort)8); //ID bw.Write((uint)4); //Size bw.Write((uint)0); //Help context id //Vba Version bw.Write((ushort)9); //ID bw.Write((uint)4); //Reserved bw.Write((uint)MajorVersion); //Reserved bw.Write((ushort)MinorVersion); //Help context id //Constants bw.Write((ushort)0x0C); //ID bw.Write((uint)Constants.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(Constants)); //Help context id bw.Write((ushort)0x3C); //ID bw.Write((uint)Constants.Length/2); //Size bw.Write(Encoding.Unicode.GetBytes(Constants)); //HelpFile2 /****** PROJECTREFERENCES Record ******/ foreach (var reference in References) { WriteNameReference(bw, reference); if (reference.ReferenceRecordID == 0x2F) { WriteControlReference(bw, reference); } else if (reference.ReferenceRecordID == 0x33) { WriteOrginalReference(bw, reference); } else if (reference.ReferenceRecordID == 0x0D) { WriteRegisteredReference(bw, reference); } else if (reference.ReferenceRecordID == 0x0E) { WriteProjectReference(bw, reference); } } bw.Write((ushort)0x0F); bw.Write((uint)0x02); bw.Write((ushort)Modules.Count); bw.Write((ushort)0x13); bw.Write((uint)0x02); bw.Write((ushort)0xFFFF); foreach (var module in Modules) { WriteModuleRecord(bw, module); } bw.Write((ushort)0x10); //Terminator bw.Write((uint)0); return CompoundDocument.CompressPart(((MemoryStream)bw.BaseStream).ToArray()); } private void WriteModuleRecord(BinaryWriter bw, ExcelVBAModule module) { bw.Write((ushort)0x19); bw.Write((uint)module.Name.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name)); //Name bw.Write((ushort)0x47); bw.Write((uint)module.Name.Length*2); bw.Write(Encoding.Unicode.GetBytes(module.Name)); //Name bw.Write((ushort)0x1A); bw.Write((uint)module.Name.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name)); //Stream Name bw.Write((ushort)0x32); bw.Write((uint)module.Name.Length*2); bw.Write(Encoding.Unicode.GetBytes(module.Name)); //Stream Name module.Description = module.Description ?? ""; bw.Write((ushort)0x1C); bw.Write((uint)module.Description.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Description)); //Description bw.Write((ushort)0x48); bw.Write((uint)module.Description.Length*2); bw.Write(Encoding.Unicode.GetBytes(module.Description)); //Description bw.Write((ushort)0x31); bw.Write((uint)4); bw.Write((uint)0); //Module Stream Offset (No PerformanceCache) bw.Write((ushort)0x1E); bw.Write((uint)4); bw.Write((uint)module.HelpContext); //Help context ID bw.Write((ushort)0x2C); bw.Write((uint)2); bw.Write((ushort)0xFFFF); //Help context ID bw.Write((ushort)(module.Type == eModuleType.Module ? 0x21 : 0x22)); bw.Write((uint)0); if (module.ReadOnly) { bw.Write((ushort)0x25); bw.Write((uint)0); //Readonly } if (module.Private) { bw.Write((ushort)0x28); bw.Write((uint)0); //Private } bw.Write((ushort)0x2B); //Terminator bw.Write((uint)0); } private void WriteNameReference(BinaryWriter bw, ExcelVbaReference reference) { //Name record bw.Write((ushort)0x16); //ID bw.Write((uint)reference.Name.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Name)); //HelpFile1 bw.Write((ushort)0x3E); //ID bw.Write((uint)reference.Name.Length * 2); //Size bw.Write(Encoding.Unicode.GetBytes(reference.Name)); //HelpFile2 } private void WriteControlReference(BinaryWriter bw, ExcelVbaReference reference) { WriteOrginalReference(bw, reference); bw.Write((ushort)0x2F); var controlRef=(ExcelVbaReferenceControl)reference; bw.Write((uint)(4 + controlRef.LibIdTwiddled.Length + 4 + 2)); // Size of SizeOfLibidTwiddled, LibidTwiddled, Reserved1, and Reserved2. bw.Write((uint)controlRef.LibIdTwiddled.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(controlRef.LibIdTwiddled)); //LibID bw.Write((uint)0); //Reserved1 bw.Write((ushort)0); //Reserved2 WriteNameReference(bw, reference); //Name record again bw.Write((ushort)0x30); //Reserved3 bw.Write((uint)(4 + controlRef.LibIdExternal.Length + 4 + 2 + 16 + 4)); //Size of SizeOfLibidExtended, LibidExtended, Reserved4, Reserved5, OriginalTypeLib, and Cookie bw.Write((uint)controlRef.LibIdExternal.Length); //Size bw.Write(Encoding.GetEncoding(CodePage).GetBytes(controlRef.LibIdExternal)); //LibID bw.Write((uint)0); //Reserved4 bw.Write((ushort)0); //Reserved5 bw.Write(controlRef.OriginalTypeLib.ToByteArray()); bw.Write((uint)controlRef.Cookie); //Cookie } private void WriteOrginalReference(BinaryWriter bw, ExcelVbaReference reference) { bw.Write((ushort)0x33); bw.Write((uint)reference.Libid.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Libid)); //LibID } private void WriteProjectReference(BinaryWriter bw, ExcelVbaReference reference) { bw.Write((ushort)0x0E); var projRef = (ExcelVbaReferenceProject)reference; bw.Write((uint)(4 + projRef.Libid.Length + 4 + projRef.LibIdRelative.Length+4+2)); bw.Write((uint)projRef.Libid.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(projRef.Libid)); //LibAbsolute bw.Write((uint)projRef.LibIdRelative.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(projRef.LibIdRelative)); //LibIdRelative bw.Write(projRef.MajorVersion); bw.Write(projRef.MinorVersion); } private void WriteRegisteredReference(BinaryWriter bw, ExcelVbaReference reference) { bw.Write((ushort)0x0D); bw.Write((uint)(4+reference.Libid.Length+4+2)); bw.Write((uint)reference.Libid.Length); bw.Write(Encoding.GetEncoding(CodePage).GetBytes(reference.Libid)); //LibID bw.Write((uint)0); //Reserved1 bw.Write((ushort)0); //Reserved2 } private byte[] CreateProjectwmStream() { BinaryWriter bw = new BinaryWriter(new MemoryStream()); foreach (var module in Modules) { bw.Write(Encoding.GetEncoding(CodePage).GetBytes(module.Name)); //Name bw.Write((byte)0); //Null bw.Write(Encoding.Unicode.GetBytes(module.Name)); //Name bw.Write((ushort)0); //Null } bw.Write((ushort)0); //Null return CompoundDocument.CompressPart(((MemoryStream)bw.BaseStream).ToArray()); } private byte[] CreateProjectStream() { StringBuilder sb = new StringBuilder(); sb.AppendFormat("ID=\"{0}\"\r\n", ProjectID); foreach(var module in Modules) { if (module.Type == eModuleType.Document) { sb.AppendFormat("Document={0}/&H00000000\r\n", module.Name); } else if (module.Type == eModuleType.Module) { sb.AppendFormat("Module={0}\r\n", module.Name); } else if (module.Type == eModuleType.Class) { sb.AppendFormat("Class={0}\r\n", module.Name); } else { //Designer sb.AppendFormat("Package={0}\r\n", module.ClassID); sb.AppendFormat("BaseClass={0}\r\n", module.Name); } } if (HelpFile1 != "") { sb.AppendFormat("HelpFile={0}\r\n", HelpFile1); } sb.AppendFormat("Name=\"{0}\"\r\n", Name); sb.AppendFormat("HelpContextID={0}\r\n", HelpContextID); if (!string.IsNullOrEmpty(Description)) { sb.AppendFormat("Description=\"{0}\"\r\n", Description); } sb.AppendFormat("VersionCompatible32=\"393222000\"\r\n"); sb.AppendFormat("CMG=\"{0}\"\r\n", WriteProtectionStat()); sb.AppendFormat("DPB=\"{0}\"\r\n", WritePassword()); sb.AppendFormat("GC=\"{0}\"\r\n\r\n", WriteVisibilityState()); sb.Append("[Host Extender Info]\r\n"); sb.Append("&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n"); sb.Append("\r\n"); sb.Append("[Workspace]\r\n"); foreach(var module in Modules) { sb.AppendFormat("{0}=0, 0, 0, 0, C \r\n",module.Name); } string s = sb.ToString(); return Encoding.GetEncoding(CodePage).GetBytes(s); } private string WriteProtectionStat() { int stat=(_protection.UserProtected ? 1:0) | (_protection.HostProtected ? 2:0) | (_protection.VbeProtected ? 4:0); return Encrypt(BitConverter.GetBytes(stat)); } private string WritePassword() { byte[] nullBits=new byte[3]; byte[] nullKey = new byte[4]; byte[] nullHash = new byte[20]; if (Protection.PasswordKey == null) { return Encrypt(new byte[] { 0 }); } else { Array.Copy(Protection.PasswordKey, nullKey, 4); Array.Copy(Protection.PasswordHash, nullHash, 20); //Set Null bits for (int i = 0; i < 24; i++) { byte bit = (byte)(128 >> (int)((i % 8))); if (i < 4) { if (nullKey[i] == 0) { nullKey[i] = 1; } else { nullBits[0] |= bit; } } else { if (nullHash[i - 4] == 0) { nullHash[i - 4] = 1; } else { int byteIndex = (i - i % 8) / 8; nullBits[byteIndex] |= bit; } } } //Write the Password Hash Data Structure (2.4.4.1) BinaryWriter bw = new BinaryWriter(new MemoryStream()); bw.Write((byte)0xFF); bw.Write(nullBits); bw.Write(nullKey); bw.Write(nullHash); bw.Write((byte)0); return Encrypt(((MemoryStream)bw.BaseStream).ToArray()); } } private string WriteVisibilityState() { return Encrypt(new byte[] { (byte)(Protection.VisibilityState ? 0xFF : 0) }); } #endregion private string GetString(BinaryReader br, uint size) { return GetString(br, size, System.Text.Encoding.GetEncoding(CodePage)); } private string GetString(BinaryReader br, uint size, Encoding enc) { if (size > 0) { byte[] byteTemp = new byte[size]; byteTemp = br.ReadBytes((int)size); return enc.GetString(byteTemp); } else { return ""; } } private string GetUnicodeString(BinaryReader br, uint size) { string s = GetString(br, size); int reserved = br.ReadUInt16(); uint sizeUC = br.ReadUInt32(); string sUC = GetString(br, sizeUC, System.Text.Encoding.Unicode); return sUC.Length == 0 ? s : sUC; } internal CompoundDocument Document { get; set; } internal PackagePart Part { get; set; } internal Uri Uri { get; private set; } /// /// Create a new VBA Project /// internal void Create() { if(Lcid>0) { throw (new InvalidOperationException("Package already contains a VBAProject")); } ProjectID = "{5DD90D76-4904-47A2-AF0D-D69B4673604E}"; Name = "VBAProject"; SystemKind = eSyskind.Win32; //Default Lcid = 1033; //English - United States LcidInvoke = 1033; //English - United States CodePage = Encoding.Default.CodePage; MajorVersion = 1361024421; MinorVersion = 6; HelpContextID = 0; Modules.Add(new ExcelVBAModule(_wb.CodeNameChange) { Name = "ThisWorkbook", Code = "", Attributes=GetDocumentAttributes("ThisWorkbook", "0{00020819-0000-0000-C000-000000000046}"), Type = eModuleType.Document, HelpContext = 0 }); foreach (var sheet in _wb.Worksheets) { if (!Modules.Exists(sheet.Name)) { Modules.Add(new ExcelVBAModule(sheet.CodeNameChange) { Name = sheet.Name, Code = "", Attributes = GetDocumentAttributes(sheet.Name, "0{00020820-0000-0000-C000-000000000046}"), Type = eModuleType.Document, HelpContext = 0 }); } } _protection = new ExcelVbaProtection(this) { UserProtected = false, HostProtected = false, VbeProtected = false, VisibilityState = true }; } internal ExcelVbaModuleAttributesCollection GetDocumentAttributes(string name, string clsid) { var attr = new ExcelVbaModuleAttributesCollection(); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Name", Value = name, DataType = eAttributeDataType.String }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Base", Value = clsid, DataType = eAttributeDataType.String }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_GlobalNameSpace", Value = "False", DataType = eAttributeDataType.NonString }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Creatable", Value = "False", DataType = eAttributeDataType.NonString }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_PredeclaredId", Value = "True", DataType = eAttributeDataType.NonString }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Exposed", Value = "False", DataType = eAttributeDataType.NonString }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_TemplateDerived", Value = "False", DataType = eAttributeDataType.NonString }); attr._list.Add(new ExcelVbaModuleAttribute() { Name = "VB_Customizable", Value = "True", DataType = eAttributeDataType.NonString }); return attr; } //internal string GetBlankDocumentModule(string name, string clsid) //{ // string ret=string.Format("Attribute VB_Name = \"{0}\"\r\n",name); // ret += string.Format("Attribute VB_Base = \"{0}\"\r\n", clsid); //Microsoft.Office.Interop.Excel.WorksheetClass // ret += "Attribute VB_GlobalNameSpace = False\r\n"; // ret += "Attribute VB_Creatable = False\r\n"; // ret += "Attribute VB_PredeclaredId = True\r\n"; // ret += "Attribute VB_Exposed = True\r\n"; // ret += "Attribute VB_TemplateDerived = False\r\n"; // ret += "Attribute VB_Customizable = True"; // return ret; //} //internal string GetBlankModule(string name) //{ // return string.Format("Attribute VB_Name = \"{0}\"\r\n", name); //} //internal string GetBlankClassModule(string name, bool exposed) //{ // string ret=string.Format("Attribute VB_Name = \"{0}\"\r\n",name); // ret += string.Format("Attribute VB_Base = \"{0}\"\r\n", "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"); // ret += "Attribute VB_GlobalNameSpace = False\r\n"; // ret += "Attribute VB_Creatable = False\r\n"; // ret += "Attribute VB_PredeclaredId = False\r\n"; // ret += string.Format("Attribute VB_Exposed = {0}\r\n", exposed ? "True" : "False"); // ret += "Attribute VB_TemplateDerived = False\r\n"; // ret += "Attribute VB_Customizable = False\r\n"; // return ret; //} /// /// Remove the project from the package /// public void Remove() { if (Part == null) return; foreach (var rel in Part.GetRelationships()) { _pck.DeleteRelationship(rel.Id); } if (_pck.PartExists(Uri)) { _pck.DeletePart(Uri); } Part = null; Modules.Clear(); References.Clear(); Lcid = 0; LcidInvoke = 0; CodePage = 0; MajorVersion = 0; MinorVersion = 0; HelpContextID = 0; } public override string ToString() { return Name; } } }