C# Run Excel Macro Through IIS Server - c#

I have a C# (using Razor) Web Application that runs through my company's intranet.
I have been using ClosedXML to manage all of my Excel needs and it works great, however, now I need to run Macros inside Excel Files and apparently ClosedXML cannot do this, so I must use Excel Interop.
My code below gives the dreaded:
Microsoft Excel cannot access the file ... There are several possible
reasons: • The file name or path does not exist. • The file is being
used by another program. • The workbook you are trying to save has the
same name as a currently open workbook.
The file name and path DOES exist
I made sure that Excel is not even running on the server
The workbook is not open (see above).
Things I've tried.
I've tried to create security provisions for the C:\Windows\System32\config\systemprofile and SysWOW64\config\systemprofile.
(Side Note: I could not find IIS_USRS when I tried to add the security provisions - this may be the problem.)
I've also tried editing the Excel Application properties via dcomcnfg and I still get the same error.
here's what I'm trying to do:
//C# with Razor Syntax
#using MSExcel = Microsoft.Office.Interop.Excel
#using System.Runtime.InteropServices
#using System.Web
#using System.IO
string worksheetName = "Sheet1";
string[] macros = new string[] { "Module1.Reset_List()", "Module1.Run_Setup()"};
string workbookPath = HttpContext.Current.Server.MapPath(#"~/uploads/test.xlsm");
FileInfo xlsFile = new FileInfo(workbookPath);
string msg = (File.Exists(xlsFile.FullName)) ? "Found It!" : "Can't Find It...";
<p>#msg</p>; // <-- This always returns "Found It!"
//Create the Excel Object
MSExcel.Application xlsApp = new MSExcel.Application();
xlsApp.Visible = true;
try
{
//Identify the workbook (open the file)
// *** the error occurs on the line below *** //
MSExcel.Workbook xlsBook = xlsApp.Workbooks.Open(xlsFile.FullName);
xlsBook.Activate();
//Identify the worksheet
MSExcel.Worksheet xlsSheet = (MSExcel.Worksheet)xlsBook.Sheets[worksheetName];
xlsSheet.Activate();
foreach (string macro in macros)
{
xlsApp.Run(macro);
}
xlsBook.Save();
xlsBook.Close(false, "", false);
xlsApp.Quit();
}
catch (Exception e)
{
<p>#e.Message</p>
Marshal.ReleaseComObject(xlsApp);
xlsApp = null;
}
if (xlsApp != null)
{
Marshal.ReleaseComObject(xlsApp);
xlsApp = null;
}

Related

C# code that remove all macros from Excel file

I have a Windows form app project (C#).
I am trying unsuccessfully to write code so that when I click a button I created, it will load an excel file that actually has macros in it and delete them all.
I know you can delete macros manually in the excel file itself but I need a way to do it programmatically (I just delete them all for the user).
I know that the macros in Excel files are written in VBA language, so I try to use the related libraries in c# but I get an error when I try to use VBProject, VBComponent.
This what I tried so far:
using Microsoft.Office.Interop.Excel;
namespace MacroRemover
{
public partial class Main : Form
{
private void Btn_Click(object sender, EventArgs e)
{
string filePath = "path\\to\\file.xlsm";
Application excel = new Application();
Workbook workbook = excel.Workbooks.Open(filePath);
VBProject vbProject = workbook.VBProject;
foreach (VBComponent component in vbProject.VBComponents)
{
vbProject.VBComponents.Remove(component);
}
workbook.Save();
workbook.Close();
excel.Quit();
}
}
}
Any way that works will help me, I would appreciate the help
Thanks in advance guys!!!
If you don't need to save xlsm format of the file, you can save this file to xlsx format and all scripts will be removed.
using Aspose.Cells;
var workbook = new Workbook("input.xlsm");
workbook.Save("Output.xlsx");
Thanks for all the replies, #tttony's response helped me to understand more deeply and #Bushuev's response here is definitely a possible and simple solution for deleting the macros.
I finally managed to delete all the macros like this:
string filePath = Path_TxtBox.Text;
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = excel.Workbooks.Open(filePath);
VBProject project = workbook.VBProject;
for (int i = project.VBComponents.Count; i >= 1; i--)
{
VBComponent component = project.VBComponents.Item(i);
try
{
project.VBComponents.Remove(component);
}
catch (ArgumentException)
{
continue;
}
}
for (int i = project.VBComponents.Count; i >= 1; i--)
{
VBComponent component = project.VBComponents.Item(i);
component.CodeModule.DeleteLines(1, component.CodeModule.CountOfLines);
}
workbook.Save();
workbook.Close();
excel.Quit();
MessageBox.Show("Macros Removed");
It should only be noted that I encountered an error: 'Programmatic access to Visual Basic Project is not trusted'
It was solved after I realized that I had to change the option to access the VBA Project object model in the Trust Center settings.

C# : Close Excel file without popup

So basically my app triggers an excel macro, from a file, that updates the file and then closes it.
When I open the file I set the "DisplayAlerts = false" variable in order to ignore all popups and it works as expected in my computer... however, a colleague of mine tried to use it and for every file, he gets the popup asking if he wants to save all changes...
Checked other questions about the popups in excel but all suggested solutions use "oBook.Saved = true;" or "oBook.Close(false);", but these did not work for me.
my code is as follows:
using Microsoft.Office.Interop.Excel;
public static bool Trigger_Macro_From_File(string path)
{
ApplicationClass oExcel = null;
Workbook oBook = null;
try
{
string filename = Path.GetFileName(path);
string macro_name = "!some_macro";
string macro = #"'" + filename + #"'" + macro_name;
// Create an instance of Microsoft Excel
oExcel = new ApplicationClass
{
DisplayAlerts = false,
Visible = false
};
oBook = oExcel.Workbooks.Open(path);
RunMacro(oExcel, new Object[] { macro });
oBook.Save();
oBook.Saved = true;
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
oBook?.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
oBook = null;
oExcel?.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
oExcel = null;
GC.Collect();
}
}
Does anyone know anything about this?
Thanks in advance.
You could double-check that no other "Microsoft Excel" process is running in the Task Manager.
Let's say at some point in your development process you started your program and open the workbook with something like
xlWorkbook = xlApp.Workbooks.Open(filePath);
Then you encountered an exception for some reason, and killed the program without closing the file properly (workbook.Close(..), app.Quit(..) and so on).
The Microsoft Excel process is still running in the background, and has a handle on the file you want to edit. So you cannot execute an instruction that saves the file under the same name. This is why the popup is appearing.
This scenario is taken from the point of view of the developer, but the same behavior could have happened on your coworker's computer if your app crashed without quitting properly, and gets re-started.
Also, be careful that finally statement might not always be executed, so double-check which scenario could cause your app to close without releasing the COM object.

Excel Interop C# Can't add VBProject Error 800A03EC on Workbook.VBProject.VBComponents.Add(..)

I) Global Explanation
Hello,
I'm developing an app in C# using Microsoft.Office.Interop.Excel. In my case I implement some olds macros stored in a file to an Excel Workbook, opened by C#, to upgrade them without re-coding macros in my app.
Globally there is my process in my app :
The user select an updated macro,
I check if the macro require something :
Here I set the register Key CURRENT_USER\SOFTWARE\Microsoft\Office\[office_ID].0\Excel\Security\AccessVBOM to 1 for allows macro/VBProject modifications
I instantiate an Excel app with a new workbook
I set optimization Excel execution parameters (disable refresh... that kind of stuff)
I add my macro, stored in a file, to the workbook
I execute the macro through Excel
I remove the macro from the Excel
I unset optimizations Excel executions parameters
I reset registers keys (.../AccessVBOM = 0)
II) My problem and my research result
In the "I add my macro, stored in a file, to the workbook" part, when I used the following instruction to add a VBProject to the workbook (required to add the macro) I get an error 800A03EC.
/// wb is a workbook object
VBComponent module = wb.VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
This error is send with a message (grossly traduce) : "The access to VB Project are not trust."
As I see on the Internet, this error occurs when an option in Excel isn't correctly set. This option can by handle with the CURRENT_USER\SOFTWARE\Microsoft\Office\[office_ID].0\Excel\Security\AccessVBOM register key.
And I'm building my app with a register key management for avoid this problem.
And as you will see on the debug display part, we can see the option is correctly set on the execution.
My question : there is another case when this error occurs?
III) Debug analyse
When I catch the ComException, I use the following to display registers keys values.
/// RequirementManager is my own class, use to set and unset endpoints computers parameters, before and after each user action
RegistryKey reg;
for (int i = RequirementManager.EXCEL_ID_HANDLED_MIN; i < RequirementManager.EXCEL_ID_HANDLED_MAX; i++)
{
reg = Registry.CurrentUser.CreateSubKey(#"SOFTWARE\Microsoft\Office\" + i + #".0\Excel\Security\", RegistryKeyPermissionCheck.ReadSubTree);
Debug.WriteLine(String.Format(#"Value of CURRENT_USER\SOFTWARE\Microsoft\Office\{0}.0\Excel\Security\AccessVBOM = {1}", i, reg.GetValue("AccessVBOM")), "Debug");
reg.Close();
}
reg = null;
And here is the console output of this lines :
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\11.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\12.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\13.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\14.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\15.0\Excel\Security\AccessVBOM = 1
Debug: Value of CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Excel\Security\AccessVBOM = 1
As you can see, the option is correctly set when the error occurs. And all instance of Excel are closed before the modification of register keys, for avoid problem. I use similar algorithm to modify this range of register keys, like a lazy boy.
Any idea are welcome
Regards, Dykoine
IV) Answer Reaction
#prizm1 This parameter if macro are allowed or not (display orange warning message), and on my computer (while test) I set it to allow all macro.
After test, I can see this option doesn't protect the user, because we can open an Excel file with C# and execute a macro in it without be block by this option even if you use "disabled all without notification".
Here the code which haven't made any problems with putting VBA into an Excel file. Maybe you need to save it to disk first before entering the Code, since the Project is determined by the filename. Just an addition, and no real solution:
public bool injectVBA(String scriptText, String target)
{
VBProject found = null;
Access.Application currApplication = this.currentInstance.Application;
if (target.Equals("") || scriptText.Equals(""))
return false;
foreach (VBProject vb in currApplication.VBE.VBProjects)
{
if (currApplication.CurrentDb().Name.Equals(vb.FileName))
{
found = vb;
break;
}
}
if (found != null)
{
foreach (VBComponent foundComponent in found.VBComponents)
{
if (foundComponent.Name.Equals(target))
{
return true;
}
}
VBComponent module = found.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
module.Name = target;
module.CodeModule.AddFromString(scriptText);
module.Activate();
//currApplication.DoCmd.OpenModule(target, Type.Missing);
currApplication.DoCmd.Save(Access.AcObjectType.acModule, target);
return true;
}
else
{
return false;
}
}

Microsoft.Office.Interop.Excel for Office 2007

I want to try the Windows form application that converts a office file (Excel, Word, Powerpoint) into a PDF file.
My client's PC will not install Visual Studio and Office version is 2007.
My application uses Microsoft.Office.Iterop.Excel.dll to covert to the PDF format.
This dll file cannot be found on my client's PC and an error has occurred as following.
System.AugumentException: Value does not fall within the expected range.
at Microsoft.Office.Interop.Excel._Workbook.ExportAsFixedFromat(.......)
How can I solve this problem?
My code is following
public bool ExportWorkbookToPdf(string workbookPath, string outputPath)
{
// If either required string is null or empty, stop and bail out
if (string.IsNullOrEmpty(workbookPath) || string.IsNullOrEmpty(outputPath))
{
return false;
}
// Create COM Objects
Microsoft.Office.Interop.Excel.Application excelApplication;
Microsoft.Office.Interop.Excel.Workbook excelWorkbook;
// Create new instance of Excel
excelApplication = new Microsoft.Office.Interop.Excel.Application();
// Make the process invisible to the user
excelApplication.ScreenUpdating = false;
// Make the process silent
excelApplication.DisplayAlerts = false;
// Open the workbook that you wish to export to PDF
excelWorkbook = excelApplication.Workbooks.Open(workbookPath);
MessageBox.Show(workbookPath);
// If the workbook failed to open, stop, clean up, and bail out
if (excelWorkbook == null)
{
excelApplication.Quit();
excelApplication = null;
excelWorkbook = null;
MessageBox.Show("in null");
return false;
}
var exportSuccessful = true;
try
{
// Call Excel's native export function (valid in Office 2007 and Office 2010, AFAIK)
excelWorkbook.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, outputPath);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
// Mark the export as failed for the return value...
exportSuccessful = false;
// Do something with any exceptions here, if you wish...
// MessageBox.Show...
}
finally
{
// Close the workbook, quit the Excel, and clean up regardless of the results...
excelWorkbook.Close();
excelApplication.Quit();
excelApplication = null;
excelWorkbook = null;
}
// You can use the following method to automatically open the PDF after export if you wish
// Make sure that the file actually exists first...
if (System.IO.File.Exists(outputPath))
{
MessageBox.Show(outputPath);
System.Diagnostics.Process.Start(outputPath);
}
return exportSuccessful;
}
As already said in the comments, each client needs to install the 2007 Microsoft Office Add-in: Microsoft Save as PDF. It doesn't matter if you are on Windows 8.1 or have any PDF reader installed. You need that Add-in to write PDFs with Office 2007. (You don't need any Add-In with Office 2010 or 2013.)

Change macros text in Excel programmatically

I've got about thousand of Excel (xls, old format) files spread out across many folders on the hard-drive. These files have the same macros that connects to a database. And the macros contains connection string. Macros is password protected, but luckily I know the password.
Question: what is the best way to change the connection string in macros inside all the files?
I have experience of working with NPOI to create/modify xls files. I have seen Interop libraries unlocking password-protected Word files and doing some editing. But I have never seen examples of programmatically changing of the macros text inside Excel file. Is that even possible?
p.s. I don't have problems writing code. The problem is choosing the right tools.
You might want use the following code as a starting point. This code uses COM Interop to extract the VBA script and perform a find-replace. I tried this out on a password-protected spreadsheet with a very basic script and it worked well. It is, admittedly, basic, but you may be able to extract what you need.
string filename = "Test.xls";
string password = "password";
Excel._Application app = new Excel.Application();
Excel._Workbook workbook = app.Workbooks.Open(Filename: filename, Password: password);
if (workbook.HasVBProject)
{
VBProject project = workbook.VBProject;
foreach (VBComponent component in project.VBComponents)
{
if (component.Type == vbext_ComponentType.vbext_ct_StdModule ||
component.Type == vbext_ComponentType.vbext_ct_ClassModule)
{
CodeModule module = component.CodeModule;
string[] lines =
module.get_Lines(1, module.CountOfLines).Split(
new string[] { "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("A1"))
{
lines[i] = lines[i].Replace("A1", "D1");
module.ReplaceLine(i + 1, lines[i]);
}
}
}
}
}
workbook.Save();
workbook.Close();
app.Quit();

Categories