OpenXML SDK将VBA注入excel工作簿

我可以成功地将一段VBA代码注入到生成的excel工作簿中,但我要做的是使用Workbook_Open()事件,以便在文件打开时执行VBA代码。 我将sub添加到我的xlsm模板文件中的“ThisWorkbook”对象。 然后,我使用openxml生产力工具来反映代码并获取编码的VBA数据。

生成文件并查看VBA时,我看到“ThisWorkbook”和“ThisWorkbook1”对象。 我的VBA位于“ThisWorkbook”对象中,但代码永远不会在打开时执行。 如果我将我的VBA代码移动到“ThisWorkbook1”并重新打开该文件,它可以正常工作。 为什么要创建一个额外的“ThisWorkbook”? 是否无法使用Workbook_Open()子系统注入excel电子表格? 这是我正在使用的C#代码的片段:

private string partData = "..."; //base 64 encoded data from reflection code //open workbook, myWorkbook VbaProjectPart newPart = myWorkbook.WorkbookPart.AddNewPart("rId1"); System.IO.Stream data = GetBinaryDataStream(partData); newPart.FeedData(data); data.Close(); //save and close workbook 

有人有想法吗?

根据我的研究,没有办法以您可以在C#中操作的格式插入项目部件数据。 在OpenXML格式中,VBA项目仍以二进制格式存储。 但是,将VbaProjectPart从一个Excel文档复制到另一个Excel文档应该可行。 因此,您必须事先确定项目部分要求您说出的内容。

如果您对此没有问题,则可以将以下代码添加到“ThisWorkbook”Microsoft Excel对象中的模板Excel文件中,以及相应的宏代码:

 Private Sub Workbook_Open() Run "Module1.SomeMacroName()" End Sub 

要将VbaProjectPart对象从一个文件复制到另一个文件,您可以使用如下代码:

 public static void InsertVbaPart() { using(SpreadsheetDocument ssDoc = SpreadsheetDocument.Open("file1.xlsm", false)) { WorkbookPart wbPart = ssDoc.WorkbookPart; MemoryStream ms; CopyStream(ssDoc.WorkbookPart.VbaProjectPart.GetStream(), ms); using(SpreadsheetDocument ssDoc2 = SpreadsheetDocument.Open("file2.xlsm", true)) { Stream stream = ssDoc2.WorkbookPart.VbaProjectPart.GetStream(); ms.WriteTo(stream); } } } public static void CopyStream(Stream input, Stream output) { byte[] buffer = new byte[short.MaxValue + 1]; while (true) { int read = input.Read(buffer, 0, buffer.Length); if (read <= 0) return; output.Write(buffer, 0, read); } } 

希望有所帮助。

在使用宏提供VbaProjectPart之后,需要在“xl / workbook..xml”对象中指定“codeName”属性。 添加此代码:

 var workbookPr = spreadsheetDocument.WorkbookPart.Workbook.Descendants().FirstOrDefault(); workbookPr.CodeName = "ThisWorkBook"; 

打开文件后,一切都应该工作了。

因此,要添加宏,您需要:

  1. 将文档类型更改为启用宏

  2. 添加VbaProjectPart并使用之前创建的宏提供它

  3. 在xl / workbook..xml中添加workbookPr codeName attr,其值为“ThisWorkBook”

  4. 另存为.xlsm ext。

我发现其他答案仍然导致重复的“工作表”对象。 我使用了与@ZlotaMoneta所说的相似的解决方案,但是在这里找到了不同的语法 :

 List newParts = new List(); using (var originalDocument = SpreadsheetDocument.Open("file1.xlsm"), false)) { newParts = originalDocument.WorkbookPart.GetPartsOfType().ToList(); using (var document = SpreadsheetDocument.Open("file2.xlsm", true)) { document.WorkbookPart.DeleteParts(document.WorkbookPart.GetPartsOfType()); foreach (var part in newParts) { VbaProjectPart vbaProjectPart = document.WorkbookPart.AddNewPart(); using (Stream data = part.GetStream()) { vbaProjectPart.FeedData(data); } } //Note this prevents the duplicate worksheet issue spreadsheetDocument.WorkbookPart.Workbook.WorkbookProperties.CodeName = "ThisWorkbook"; } }