簡體   English   中英

OpenXML SDK將VBA注入excel工作簿

[英]OpenXML SDK Inject VBA into excel workbook

我可以成功地將一段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<VbaProjectPart>("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<WorkbookProperties>().FirstOrDefault();
workbookPr.CodeName = "ThisWorkBook";

打開文件后,一切都應該正常工作。

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

  1. 將文檔類型更改為啟用宏

  2. 添加VbaProjectPart並使用之前創建的宏提供它

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

  4. 另存為.xlsm ext。

我發現其他答案仍然導致重復的“工作表”對象。 我使用了與@ZlotaMoneta所說的類似的解決方案,但是在這里找到不同的語法

List<VbaProjectPart> newParts = new List<VbaProjectPart>();
using (var originalDocument = SpreadsheetDocument.Open("file1.xlsm"), false))
{
    newParts = originalDocument.WorkbookPart.GetPartsOfType<VbaProjectPart>().ToList();

    using (var document = SpreadsheetDocument.Open("file2.xlsm", true))
    {
        document.WorkbookPart.DeleteParts(document.WorkbookPart.GetPartsOfType<VbaProjectPart>());

        foreach (var part in newParts)
        {
            VbaProjectPart vbaProjectPart = document.WorkbookPart.AddNewPart<VbaProjectPart>();
            using (Stream data = part.GetStream())
            {
                vbaProjectPart.FeedData(data);
            }                    
        }

        //Note this prevents the duplicate worksheet issue
        spreadsheetDocument.WorkbookPart.Workbook.WorkbookProperties.CodeName = "ThisWorkbook";
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM