My goal is to add macros to an excel workbook without having to enable "Trust Access to the VBA Project Object Module" in the Excel Trust Center. (Enabling access seems a security risk).
Found random pieces of puzzle on my initial fact finding search:
-I see that VBA script is stored in the zipped .xlsm file as vbaProject.bin.
-There are several free/commercial resources that can work with excel files:
Create excel without interop and template with or without row and columnspan
It would be nice to simply have a file of VBA script in the C# project that the C# code pulls from and injects into the Excel document without VBA interop. Any quick/fast/simple/straightforward way to do this or should I play around with the free/commercial resources linked to above?
Using OpenXML SDK 2.0:
Create your macro code and save it in .xlsm format, say snorehorse.xlsm.
Open snorehorse.xlsm in the OpenXML Productivity Toolkit and do a Reflect Code on the tree root.
Find the macro's binary code. It's in a string format and looks like random characters.
In your IDE, add a reference to OpenXML SDK, and programmatically create or open the excel file you want to inject the macro code into.
Copy the macro string found in step #3 into your code.
Add a new vba part to the destination.
Feed the string data into the new vba part.
Save and run and be pleased you bypassed the Trust Center.
Example code:
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
private string partData = "...";
public void vbaInjector{
[code to create / open spreadsheet using OpenXML omitted]
VbaProjectPart vbaProjectPart1 = snoreSpreadsheetDoc.WorkbookPart.AddNewPart<VbaProjectPart>("rId8");
System.IO.Stream data = GetBinaryDataStream(partData);
vbaProjectPart1.FeedData(data);
data.Close();
[code to close spreadsheet and cleanup omitted]
}
private System.IO.Stream GetBinaryDataStream(string base64String)
{
return new System.IO.MemoryStream(System.Convert.FromBase64String(base64String));
}
I chose to add the OpenXML SDK dll into the project's local build so the end users won't have to install the SDK themselves.
I think this can be done on a lower level, working with the XML, without using the OpenXML SDK, but I haven't attempted to learn how to do this. If anyone can post the code, I'll accept that Answer over mine.
Also, if one had a programmatic way to convert VBA script, in an embedded resource file, into a binary string of the format excel expects, one could bypass having to copy and paste in a new string of binary data every time you wanted to change the macro code. That would be a superior answer to mine.
Thanks.
If you use EPPlus, you can now include VBA programmatically. See here:
Writing and Executing VBA Macros on Excel without using Excel.Interop
Related
I want to import an excel file into c#. But problem is, when I want to add reference, so I can use these commands
using Microsoft.Office.Interoop;
using Excel=Microsoft.Office.Interop.Excel;
there is no such reference.
If you don't want to use (or don't have) Excel on your machine, use OpenXML for reading Excel content (only new format, xlsx and xlsm). It is a bit more complicated for first look but small and very fast.
I've been asked to strip an Excel file of macros, leaving only the data. I've been asked to do this by converting the Excel file to XML and then reading that file back into Excel using C#. This seems a bit inefficient to me and I was thinking that it would be easier to simply load the source Excel file into C# and then create a new target Excel file and add the sheets from the source back into the target.
I don't know where macros live inside an Excel file, so I'm not sure if this would accomplish the task or not. So, will this work? Will simply copying the sheets from one file to another strip it of it's macros or are they actually stored at the worksheet level?
As always, any and all suggestions are welcome, including alternate suggestions or even "why are you even doing this???". :)
To do this programmatically, you can use the ZipFile class from the System.IO.Compression library in .NET from C#. (.NET Framework 4.5)
Rename the file to add a ".zip" extension, and then open the file as a ZIP archive. Look for an element in the resultant "xl" folder called "vbproject.bin", and delete it. Remove the .zip extension. Macros gone.
Your best bet is to save the workbook as an xlsx, close it, open it, then save as a format of your choice.
This will strip the macros and is robust. It will also work if the VBA is locked for viewing.
Closing and reopening the workbook is necessary otherwise the macros are retained.
If you're needing to use C# to do this, I agree that it would be easier to load the source Excel file into C# and create a new target file only copying over the cells and sheets you need. Especially if you're doing this for a large amount of excel files I would recommend just creating a small console app that, when given an excel sheet, will automatically generate a new excel sheet with just the data for you.
One tool that I've found extremely useful and easy to use for such tasks is EPPlus.
I am using ClosedXML to generate spreadsheets from C# (asp.net-mvc) and it works great. I have one additional requirement so I wanted to get some feedback on how I could achieve this.
I want to save as a macro enabled workbook and when I just give it a "xlsm" extension it doesn't seem to open (versus a xlsx). Here is my code:
public ActionResult ExportExcel()
{
MemoryStream stream = nModel.GenerateSS();
return File(stream, #"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "MySS.xlsx");
}
but if I try to do this:
public ActionResult ExportExcel()
{
MemoryStream stream = nModel.GenerateSS();
return File(stream, #"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "MySS.xlsm");
}
Excel complains when trying to open.
Then, assuming I can do #1, I need to be able to take some VBA (assuming just a hard-coded function) and insert that into a module or workbook, so when someone opens the spreadsheet and clicks on Macros, they can run the macros. From googling, that doesn't seem supported by ClosedXML so I was curious if anyone has any alternative ways to achieve this?
The only way I know for inserting code to a VBA Project is to use Office.Interop.Excel along with Microsoft.Vbe.Interop (not sure about ClosedXML and other 3rd party...but still, wanted to share)
But since you are asking for an alternative this is how I would go about;
Creating a workbook and inserting a macro to it using C#
You need to allow programmatic access to VBA Project in Excel.
(Excel) File -> Options -> Trust Center -> Trust Center Settings -> Macro Settings -> Trust Access to the VBA Project Object Model
In your C# Solution add COM references to
Microsoft Visual Basic for Applications Extensibility 5.3
Microsoft Excel 14.0 Object Library
Add using directives to your C# code behind file
using Microsoft.Office.Interop.Excel;
using System.Reflection;
using Microsoft.Vbe.Interop;
using System.Runtime.InteropServices;
Now follow the code and comments
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.Visible = true;
Workbook wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
VBProject vbProj = wb.VBProject; // access to VBAProject
VBComponent vbComp = vbProj.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule); // adding a standard coding module
vbComp.CodeModule.DeleteLines(1, vbComp.CodeModule.CountOfLines); //emptying it
// this is VBA code to add to the coding module
string code = "Public Sub HelloWorld() \n" +
" MsgBox \"hello world!\" \n" +
"End Sub";
// code should be added to the module now
vbComp.CodeModule.AddFromString(code);
// location to which you want to save the workbook - desktop in this case
string fullPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\macroWorkbook.xlsm";
// run the macro
xlApp.Run("HelloWorld");
// save as macro enabled workbook
wb.SaveAs(Filename: fullPath, FileFormat: XlFileFormat.xlOpenXMLWorkbookMacroEnabled);
xlApp.Quit();
In the above code;
you create an host Excel application, add a workbook. Access VBA Project Object module, add a new standard coding module to the VBA Project, write a VBA macro to that module. Application.Run("HelloWorld") simply calls the macro - note you can comment out this section if you don't want the box to pop-up. Then in the end you save the workbook as a Macro Enabled Workbook to a location specified in fullPath variable.
PS. Please note I haven't added any error handling.
For additional tips on C# and VBA see this blog
Hope this helps :)
ClosedXML currently doesn't support writing VBA directly.
However you can manually add in a .bin file containing the predefined VBA macros/functions to an existing .XLSX spreadsheet programmatically. Write all of your VBA macros beforehand and save the file as a .XLSM file. Extract the contents to a folder and you'll have a vbaProject.bin containing all of your code. To get the VBA project to load properly, the workbook.xml requires the following additions:
<Default Extension="bin" ContentType="application/vnd.ms-office.vbaProject"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.ms-excel.sheet.macroEnabled.main+xml"/>
The vbaProject.bin will need to be placed under the 'xl' folder, and the workbook will require a codeName GUID and alias ('ThisWorkbook' by default). All worksheets will also need a codeName (matching the sheet name) so that calls in the macros actually work when referencing named sheets.
John McNamara has created a working proof-of-concept in Perl that's worth checking out here.
Scanning over the assembly in IlSpy, this should be fairly straightforward to implement in ClosedXML, therefore if you have success here it would be a good idea to push your contribution over to the main project.
return File(stream, #"application/vnd.ms-excel.sheet.macroEnabled.12", "MySS.xlsm");
According to this link, the MIME type for Office Excel 2007 macro-enabled workbook (.xlms) is
application/vnd.ms-excel.sheet.macroEnabled.12.
Have you seen this post on the ClosedXml codeplex project regarding support for macros.
I have to merge two excel files containing one sheet in each of them and I have to generate a third file containing two sheets corresponding to the two original sheets.
This task can be done using "interop" and the code works but when the same code is run in a system that does not contain MS Office, the process fails and an error comes up.
Can you please guide me as to what dll files to be included or how this merging could be done without using interop?
Thanks in advance.
From what I've experienced, there is unfortunately no framework way of doing this (without writing your own excel file reader). I happened across this interesting library which does just that.
http://exceldatareader.codeplex.com/
So far it has worked for our needs and requires no interop.
You should use an external component to work with excel files. I use the syncfusion xslIo.
If you only have raw data (no formulas, etc) you could also just save the files using the XML Spreadsheet 2003 (*.xml) format (its very easy to read) and process the data using standard XML tools.
I have an Excel file which is generated by a machine. I need to create an application using WPF and C# which can import the excel to see its contents, do some calculations and create some new columns and save back as new excel file. Which is the way to go?
I recommend using Microsoft's Open XML library (as it doesn't require Excel to be installed).
There are various 3rd party libraries that sit on top of it to make it easier to use; such as ClosedXML or Simple OOXML.
Consider ODBC Excel drivers. If your input file is built by machine AND you do not want anything fancy as output, this should work just fine.
You can try Spreadsheet control to open excel http://www.syncfusion.com/products/user-interface-edition/wpf/spreadsheet
Sample location: http://silverlight.syncfusion.com/samples/WPF/Samples/WPFSampleBrowser/UI/Spreadsheet/Spreadsheet.htm