I am trying to an Add-in directly from C# so that when I open a workbook, and do a Workbook.Calculate() the UDF's (User Defined Functions) that are defined in an external addin correctly calculate in the worksheet. Currently, I am looping through each adding and simple setting:
AddIn.Installed = true
This does not work. C# does not load add-in at all, and I want to avoid using VBA. I want to open a workbook an excel workbook with the specific add in loaded, do a full calculated, and should have all values of the worksheet updated, including cells with UDF's.
Thanks for any help....
Some code:
Excel.Workbook wkbk = ExcelApp.ActiveWorkbook;
Excel.XlFixedFormatType paramExportFormat = Excel.XlFixedFormatType.xlTypePDF;
Excel.XlFixedFormatQuality paramExportQuality = Excel.XlFixedFormatQuality.xlQualityStandard;
bool paramOpenAfterPublish = false;
bool paramIncludeDocProps = true;
bool paramIgnorePrintAreas = true;
object paramFromPage = Type.Missing;
object paramToPage = Type.Missing;
ExcelApp.Visible = true;
//foreach (Excel.AddIn aiTemp in ExcelApp.AddIns)
//{
// if (aiTemp.Name.Contains(""))
// {
// aiTemp.Installed = false;
// aiTemp.Installed = true;
// }
//}
while (ExcelApp.CalculationState == Microsoft.Office.Interop.Excel.XlCalculationState.xlCalculating)
{
Thread.Sleep(50);
}
ExcelApp.CalculateFull();
var wksht = wkbk.ActiveSheet;
Excel.Range rng = ((Excel.Worksheet)wksht).get_Range("B1", "B1");
rng.Calculate();
//EnsureCalcFinished();
ExcelApp.Visible = false;
wkbk.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, PathToDocument.Replace(".xlsx", ".pdf"), paramExportQuality, true, false, Type.Missing, Type.Missing, true,Type.Missing);
UPDATE:
I found a link with the method I use to register UDFs.
Ref: http://msdn.microsoft.com/en-us/library/ms173189(v=vs.80).aspx
In Excel, you need to go to Options -> Add-Ins => Excel Add-in (Go..) => automation = select the library and hit OK. Once you do this once, it'll be auto-loaded each time you open excel.
Here is the algorithm to load excel AddIns so that when you open excel workbook, a calculation will include UDF's defined in Add-in:
1)Initiliaze Excel Application
ExcelApp = new Excel.Application();
2)Load AddIns *
foreach (Excel.AddIn ai in ExcelApp.AddIns)
{
ai.Installed = false;
ai.Installed = true;
ExcelApp.Wait(50);
}
**The Key is to load add-ins before you open Excel Workbook.
3)Open Excel Workbook, which will trigger calculations
4)Set Calculation Mode to manual so that any changes in Interop do not trigger lengthy recalc
ExcelApp.Calculation = Microsoft.Office.Interop.Excel.XlCalculation.xlCalculationManual;
5)Perform any manipulations, and perform calc
ExcelApp.CalculateFull();
6)Dispose of Excel Objects appropriately
Hope this helps someone with a similar issue.. Ended up being a simple fix for a simple problem. Just remember to load add-ins before opening the workbook. Otherwise, opening an excel workbook with UDF's dependent on AddIn will fail.
Related
I wrote a program in C# that is supposed to convert each worksheet in my excel workbook to a csv and save it in their own files. The problem I'm having is that when I open each file, they all have the same content as the very last worksheet. Here is my code:
public void Main()
{
Excel.Application excelApp = new Excel.Application();
Excel.Workbook workbook = excelApp.Workbooks.Open(#"C:\Users\user\Desktop\Book1.xlsx");
foreach (Excel.Worksheet sht in workbook.Worksheets)
{
sht.Select();
System.Diagnostics.Debug.WriteLine(sht.Name.ToString());
workbook.SaveAs(string.Format("{0}{1}.csv", #"C:\Users\user\Desktop\", sht.Name), Excel.XlFileFormat.xlCSV, Excel.XlSaveAsAccessMode.xlNoChange);
}
workbook.Close(false);
Dts.TaskResult = (int)ScriptResults.Success;
}
Any help would be great, thanks!
Update 1
I don't know if it's worth mentioning that I'm trying to do this through a script task in SSIS. So it's just one script task that I run that contains the code above.
Trying to figure out the issue
In normal cases, the code you provided will work perfectly. It may encounter some issue in case that the excel application has shown a message box, need permissions to enable editing, there are permissions issue to access other worksheets since they are protected ...
First of all, open the excel manually and check that you can access all worksheets and perform save operations manually. If you didn't encountered any issue, then you should prevent excel from showing message boxes or other promotion while using Interop.Excel library.
In addition, check that the Csv does not already exists in the destination path.
Try using a similar code:
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Visible = false;
excelApp.DisplayAlerts = false;
Microsoft.Office.Interop.Excel.Workbook workbook = excelApp.Workbooks.Open(#"D:\Book1.xlsx");
workbook.DoNotPromptForConvert = true;
workbook.CheckCompatibility = false;
foreach (Microsoft.Office.Interop.Excel.Worksheet sht in workbook.Worksheets)
{
sht.Select();
System.Diagnostics.Debug.WriteLine(sht.Name.ToString());
if (System.IO.File.Exists(string.Format("{0}{1}.csv", #"D:\", sht.Name)))
{
System.IO.File.Delete(string.Format("{0}{1}.csv", #"D:\", sht.Name);
}
workbook.SaveAs(string.Format("{0}{1}.csv", #"D:\", sht.Name),
Microsoft.Office.Interop.Excel.XlFileFormat.xlCSV, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange);
}
//workbook.Close(false);
workbook.Close(false, Type.Missing, Type.Missing);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
I tested the following code and it converted all Worksheets successfully.
Simple code, and I thought it should kill Excel. But the task manager says this leaves an instance of Excel running. What am I missing? Thank you.
using Microsoft.Office.Interop.Excel;
//Stuff...
// Launch dialog picker that return path to Excel file, in string
// called excelTemplate
Microsoft.Office.Interop.Excel.Application xlTemp = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = xlTemp.Workbooks.Open(excelTemplate);
xlTemp.DisplayAlerts = false;
// Poke through individual sheets, get some info from them
workbook.Close();
xlTemp.Quit();
Application xlTemp = new Application();
Workbooks workbooks = xlTemp.Workbooks;
Workbook workbook = workbooks.Open(excelTemplate);
// Do stuff.
workbook.Close();
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
xlTemp.Quit();
Marshal.ReleaseComObject(xlTemp);
Aside from needing to release each COM object, note also that by doing xlTemp.Workbooks.Open, you're leaking a COM reference by not assigning xlTemp.Workbooks to a variable that can be released later.
You can used #using to automatically dispose the application.
using(Microsoft.Office.Interop.Excel.Application xlTemp = new
Microsoft.Office.Interop.Excel.Application())
{
Workbook workbook = xlTemp.Workbooks.Open(excelTemplate);
xlTemp.DisplayAlerts = false;
// Poke through individual sheets, get some info from them
workbook.Close();
}
I have linked an application that gets data from an API, I open the sheet when a new contract is loaded to the program. Now I am trying to write new data to the excel sheet later in the program when i collect new data.
I know how to write data to the excel sheet and how to open the sheet I want to write on. The problem is I don't know how to write to the sheet once its already open, all I can get it to do is open another instance of the sheet.
I need to be able to open the sheet in one void and then update the now open sheet in a later void. How do I check to see if the sheet is open and if it is then access it again to write more data to it?
Here is how I have opened Excel,
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Console.WriteLine("Opening Excel...");
if (xlApp == null)
{
Console.WriteLine("EXCEL could not be started. Check that your office installation and project references are correct.");
return;
}
xlApp.Visible = true;
Workbook wb = xlApp.Workbooks.Open(#"C:\Users\Craig Key\Desktop\AppExports\TestExport.xlsx");
Console.WriteLine("Opening Currently Linked Workbook...");
Worksheet ws = (Worksheet)wb.ActiveSheet;
Console.WriteLine("Opening Active Worksheet...");
if (ws == null)
{
Console.WriteLine("Worksheet could not be created. Check that your office installation and project references are correct.");
}
Now later I need to find xlApp later in the program and write to it again, without opening another instance of the file.
I figured this out after searching for a while, I needed to use the marsh to try to bind the the open instance and then work with it.
Microsoft.Office.Interop.Excel.Application xlApp = null;
//Microsoft.Office.Interop.Excel.Workbooks wbs = null;
Microsoft.Office.Interop.Excel.Workbook wb = null;
Microsoft.Office.Interop.Excel.Worksheet ws = null;
bool wasFoundRunning = false;
Microsoft.Office.Interop.Excel.Application tApp = null;
//Checks to see if excel is opened
Console.WriteLine("CDC is looking for Excel...");
try
{
tApp = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
wasFoundRunning = true;
}
catch (Exception)//Excel not open
{
wasFoundRunning = false;
}
if (true == wasFoundRunning)
{
//Control Excel
}
I wrote a C# application that queries a DB and puts the results in an excel file. The program itself works fine. However if I open a second completely unrelated excel file while the application is running, an exception occurs and the process stops.
Now, in the program itself I've set the visibility to false, and after opening the second unrelated Excel file, the one that was being generated is suddenly open and visible, and then I get the exception.
Anyone know what is causing the problem or how to resolve it?
The relevant parts of the code is as follows, the exception occurs at the line worksheet.get_Range(currCol.GetString() + excelRow, Missing.Value).Formula = item.ToString();
The exception itself is : "Exception from HRESULT: 0x800AC472"
Application exc = new Application();
//Makes the Excel file not visible
exc.Visible = false;
exc.UserControl = false;
exc.DisplayAlerts = false;
Workbooks workbooks = exc.Workbooks;
Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
Sheets sheets = workbook.Worksheets;
Worksheet worksheet = (Worksheet)sheets.get_Item(1);
int excelRow = 1;
ExcelChar currCol = new ExcelChar('A');
System.Data.DataTable testTable = dbConnection.searchQuery("Select * from testTable").Copy();
if (worksheet == null)
{
Console.WriteLine("ERROR: worksheet == null");
}
foreach (System.Data.DataRow row in testTable.Rows)
{
foreach (var item in row.ItemArray)
{
worksheet.get_Range(currCol.GetString() + excelRow, Missing.Value).Formula = item.ToString();
currCol.Add(1);
}
excelRow++;
currCol = new ExcelChar('A');
}
Take a look at this thread.
It looks like your error is VBA_E_IGNORE, in which case you need to register an IMessageFilter implementation so you can implement retry logic.
I've seen this issue in the past, when using the same instance of Excel with interop and interactively - for example when instatiating an Excel Application object using:
Marshal.GetActiveObject("Excel.Application")
In your case, you're creating a new instance of Excel using:
exc = new Application();
What you should try to do is to make sure you close this instance as quickly as possible. This is not always easy, because of the problem described in this KB article. Otherwise you might consider something other than COM Interop to write to Excel (e.g. OLEDB or a third party library such as Aspose or EPPlus).
When Excel is busy - e.g. has a modal dialog displayed, or is busy loading a workbook, it will not respond to incoming COM messages, so it returns an error which is translated into this exception. An IMessageFilter implementation (specifically: RetryRejectedCall) will typically retry a few times, then either fail or prompt the user to retry ("Server busy").
When using the Office InterOp services, you must close the objects you've created in reverse order.
private static void Excel_FromDataTable(DataTable dt)
{
// Global missing variable.
object missing = System.Reflection.Missing.Value;
// Creates an excel object,
Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
// Then a workbooks object,
Excel.Workbooks workbooks = excel.Workbooks;
// Then adds a workbook object,
Excel.Workbook workbook = workbooks.Add(true);
// Then adds a worksheet object,
Excel.Worksheet activeSheet = workbook.ActiveSheet;
// Then names the worksheet to what we need.
activeSheet.Name = "scbyext";
// Add column headings,
int iCol = 0;
// for each row of data,
int iRow = 0;
foreach (DataRow r in dt.Rows)
{
iRow++;
// Then add each row's cell data.
iCol = 0;
foreach (DataColumn c in dt.Columns)
{
iCol++;
excel.Cells[iRow, iCol] = r[c.ColumnName];
}
}
// Disable Excel prompts.
excel.DisplayAlerts = false;
// Save the workbook to the correct folder.
workbook.SaveAs("C:\\Escaped\\Path",
Excel.XlFileFormat.xlExcel8, missing, missing,
false, false, Excel.XlSaveAsAccessMode.xlNoChange,
missing, missing, missing, missing, missing);
// Release the objects we made, in reverse order, to allow Excel to quit correctly.
ReleaseObj(activeSheet);
ReleaseObj(workbook);
ReleaseObj(workbooks);
excel.Quit();
ReleaseObj(excel);
}
If you don't, the process will stay stuck open. I'm not sure what it's doing when it stays stuck, but by the end of the day the CPU Time used by it can get quite high.
I had a similar problem. I was creating an excel file using C# with a lot of charts that took a long time to create. If a user opened an existing excel file while my C# file was still being written it caused an exception to be thrown and my application would crash.
I fixed it with the following:
xlApp = new Application();
xlApp.IgnoreRemoteRequests = true;
I am writing a program in C# for analysing a structure details in an earth quake.I have a lot of data and want to put them in a excel file.
I mean i need a function like this :
public void excel(int sheet_no,int row,int column,string value)
Is there any way to parse the text file and Put this data in Excel sheet ?
use this :
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
Workbooks workbooks;
_Workbook workbook;
_Workbook workbook2;
Sheets sheets;
_Worksheet worksheet;
app.Visible = false;
workbooks = app.Workbooks;
workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
sheets = workbook.Worksheets;
worksheet = (_Worksheet)sheets.get_Item(1);
worksheet.Cells[row, column] = value;
workbook.Saved = true;
workbook.SaveAs(output_file);
app.UserControl = false;
app.Quit();
Take a look at ClosedXml
Code sample:
var workbook = new XLWorkbook();
var worksheet = workbook.Worksheets.Add("Sample Sheet");
worksheet.Cell("A1").Value = "Hello World!";
workbook.SaveAs("HelloWorld.xlsx");
But if you are dealing with a lot of data, consider using a database like sqlserver.
It provides a lot of analysis tools.
You can connect it as a datasource to excel too.
For now, I am providing links for you to read # Excel Tasks and code samples # Excel controls in ASP.net. If you're still confused, I may be able to find code samples from my projects or the Internet, which are not hard to find.
Look at link Workbook Open (create) method to create Workbooks, and link Workbook Open method to open Workbooks.