/*******************************************************************************
|
* 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 HH.WMS.Utils.EPPlus.Utils;
|
using System.Security.Cryptography.Pkcs;
|
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography;
|
using System.Text.RegularExpressions;
|
|
namespace HH.WMS.Utils.EPPlus.VBA
|
{
|
/// <summary>
|
/// Represents the VBA project part of the package
|
/// </summary>
|
public class ExcelVbaProject
|
{
|
const string schemaRelVba = "http://schemas.microsoft.com/office/2006/relationships/vbaProject";
|
internal const string PartUri = @"/xl/vbaProject.bin";
|
#region Classes & Enums
|
/// <summary>
|
/// Type of system where the VBA project was created.
|
/// </summary>
|
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
|
/// <summary>
|
/// System kind. Default Win32.
|
/// </summary>
|
public eSyskind SystemKind { get; set; }
|
/// <summary>
|
/// Name of the project
|
/// </summary>
|
public string Name { get; set; }
|
/// <summary>
|
/// A description of the project
|
/// </summary>
|
public string Description { get; set; }
|
/// <summary>
|
/// A helpfile
|
/// </summary>
|
public string HelpFile1 { get; set; }
|
/// <summary>
|
/// Secondary helpfile
|
/// </summary>
|
public string HelpFile2 { get; set; }
|
/// <summary>
|
/// Context if refering the helpfile
|
/// </summary>
|
public int HelpContextID { get; set; }
|
/// <summary>
|
/// Conditional compilation constants
|
/// </summary>
|
public string Constants { get; set; }
|
/// <summary>
|
/// Codepage for encoding. Default is current regional setting.
|
/// </summary>
|
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; }
|
/// <summary>
|
/// Project references
|
/// </summary>
|
public ExcelVbaReferenceCollection References { get; set; }
|
/// <summary>
|
/// Code Modules (Modules, classes, designer code)
|
/// </summary>
|
public ExcelVbaModuleCollection Modules { get; set; }
|
ExcelVbaSignature _signature = null;
|
/// <summary>
|
/// The digital signature
|
/// </summary>
|
public ExcelVbaSignature Signature
|
{
|
get
|
{
|
if (_signature == null)
|
{
|
_signature=new ExcelVbaSignature(Part);
|
}
|
return _signature;
|
}
|
}
|
ExcelVbaProtection _protection=null;
|
/// <summary>
|
/// VBA protection
|
/// </summary>
|
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+9<code.Length && code.Substring(pos,9)=="Attribute")
|
{
|
int linePos=code.IndexOf("\r\n",pos);
|
string[] lineSplit;
|
if(linePos>0)
|
{
|
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;
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 2.4.3.3 Decryption
|
/// </summary>
|
/// <param name="value">Byte hex string</param>
|
/// <returns>The decrypted value</returns>
|
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;
|
}
|
/// <summary>
|
/// 2.4.3.2 Encryption
|
/// </summary>
|
/// <param name="value"></param>
|
/// <returns>Byte hex string</returns>
|
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;
|
}
|
|
/// <summary>
|
/// MS-OVBA 2.3.4.1
|
/// </summary>
|
/// <returns></returns>
|
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();
|
}
|
/// <summary>
|
/// MS-OVBA 2.3.4.1
|
/// </summary>
|
/// <returns></returns>
|
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; }
|
/// <summary>
|
/// Create a new VBA Project
|
/// </summary>
|
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;
|
//}
|
/// <summary>
|
/// Remove the project from the package
|
/// </summary>
|
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;
|
}
|
}
|
}
|