I`m trying to get copy of one worksheet to another worhsheet,below is my code..I tried to release instances created of excel but still I see one instace of it in TaskManager.
C# Code:
try
{
wBook = xCel.Workbooks.Open(filePath);
xCel.Visible = false;
this.xCel.DisplayAlerts = false;
wBook = (Excel.Worksheet)wBook.Worksheets.get_Item(1);
wBook.Copy(Type.Missing, Type.Missing);
wBook = (Excel.Worksheet)wBook.Sheets[1];
wBook.SaveAs(strFileCopyPath);
}
finally
{
if (wBook != null)
{ wBook.Close();
Thread.Sleep(500);
}
Marshal.ReleaseComObject(wBook);
Marshal.ReleaseComObject(wBook);
}
Please some one tell what wrong i`m doing here?
thanks
You are doing nothing SANE wrong - but the concept of closing exel is very harebrained.
Thats because:
1: Excel creates sometimes intermediate objects, which are NOT visible for you, especially if you do something like this: DIm bla = excel.worksheet.range("A1").value. Then there are intermediate objects for excel.
2: The excel objects have to be closed in a very specific order.
This code SHOULD help:
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
this has to be double due to the fact that pass 1 only marks intermediate objects, but does not destroy them. It is garbage collection.
Also destroy your objects in this order:
Ranges etc.
Worksheets
Workbook
APP
like this:
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlSheet)
xlsheet = nothing
xlBook .Close()
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlBook )
xlbook = nothing
xlapp.quit
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlapp)
xlapp = nothing
use try catch to catch errors in this section, if an object is nothing..
You forgot to call Quit() method of Excel.Application object. Ususally I use the following code to close Excel (VB.Net code snippet):
xlApp.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
xlApp = Nothing
xlBook = Nothing
xlSheet = Nothing
Related
I'm attempting to run excel vba macro and get back result but I'm always getting back null (excuse my ignorance, I'm new to this macros)
the macro
Public Function TestMacro() As Boolean
If Len(Range("A1").Value) <> 9 Then
TestMacro = False
Else
TestMacro = True
End If
End Function
the c# code invoking it
Excel.Application excelApp = new Excel.Application { DisplayAlerts = false };
object misValue = Missing.Value;
excelApp.Visible = false;
Excel.Workbook ExcelWorkBook = excelApp.Workbooks.Open(filePath);
try
{
var result = excelApp.Run("Sheet1.TestMacro");
ExcelWorkBook.Close(false, misValue, misValue);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
excelApp.Quit();
if (ExcelWorkBook != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(ExcelWorkBook); }
if (excelApp != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp); }
}
The Function to be used as a UDF (User Defined Function) must be in a Module, not a workbook object (like a Worksheet).
In addition, while it appears possible, your UDF should not trigger any user interaction (such as a MessageBox). Remember that your UDFs can be recalculated at any stage (well, with well understood and sometimes less understood triggers) and you do not want your user to be inundated with message boxes on an apparent endless loop (e.g. try having a message box in an UDF that is then replicated over a 1000 cells!)
When I do VBA coding, I create a model that is named (imaginatively!) "UDF" in which I store all my UDFs. This makes it easier for me to understand what functions are part of the back end code, and which functions are designed specifically to be used in the front end.
The bottom line is, an UDF represent a function - simple inputs and only one output. The following example should be put in a module:
Option Explicit
Public Function TestMacro(inputcell as range) As Boolean
If Len(inputcell.Value) <> 9 Then
TestMacro = False
Else
TestMacro = True
End If
End Function
In the cell (not A1, the one where you want the results):
=TestMacro(A1)
You can do some more complex things, and include additional error checking and return Excel errors such as #Value, #Ref, #Name - but that is a question for another day.
I've been trying to modify an Excel file called "Hoja de Resultados" adding three different variables each time (Puntuacion = Score, Fecha = Date, Hora = Time) inside of Visual Studio (C#). The excel sheet is linked to a DataGridView window which updates everytime the values are changed. The problem is, when there are less than 3 rows on the excel sheet, the program stops modifying the XLSX and sometimes it throws a seemingly random exception on "Program.cs".
string Direccion = "G:\\Archivos\\Programación\\Visual Basic\\UVG\\Microcontroladores - Proyecto Final Comunicación Serial\\Hoja de Resultados.xlsx";
Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook Hoja = ExcelApp.Workbooks.Open(Direccion);
Microsoft.Office.Interop.Excel.Worksheet HojaActiva = ExcelApp.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;
Excel.Range Rango = HojaActiva.UsedRange;
int Posicion = Rango.Rows.Count;
int NuevaPosicion = Posicion + 1;
HojaActiva.Cells[NuevaPosicion, 1] = Puntuacion;
HojaActiva.Cells[NuevaPosicion, 2] = Fecha;
HojaActiva.Cells[NuevaPosicion, 3] = Hora;
Hoja.Saved = true;
ExcelApp.DisplayAlerts = false;
ExcelApp.ScreenUpdating = false;
ExcelApp.Visible = false;
ExcelApp.UserControl = false;
ExcelApp.Interactive = false;
Hoja.Close(true, Type.Missing, Type.Missing);
Marshal.ReleaseComObject(Hoja);
Marshal.ReleaseComObject(HojaActiva);
ExcelApp.Quit();
Sorry if it's partially in Spanish. But for some guidance:
Hoja = Workbook
HojaActiva = ActiveWorkbook
The reason that your Excel file is not updating is because you're not saving it anywhere in code. To solve your saving problem,
Change this line:
Hoja.Saved = true;
To
Hoja.Save();
When you write Hoja.Saved = true;, it tells Excel that there are no changes to be saved, whether that is the case or not, and that it can safely exit. Thus it does not actually save anything. You have to call the Save() method explicitly.
Another possible issue in your code is if the active range does not begin at the very top. The line int NuevaPosicion = Posicion + 1; assumes that your input starts on row one, which may not be the case. I would recommend changing that line to this:
//This way it doesn't matter where the range starts.
// Rows.Row returns the row on which the active range begins.
int NuevaPosicion = Posicion + Rango.Rows.Row;
Now new data will always be appended to the end of the sheet.
Other than the two points above, I cannot replicate your problem with less than 3 rows on the excel sheet, the code you posted seems to run with no issues under all conditions I could think of. Try making the change above and see if that solves your other issues. If not, please update your question to describe the seemingly random exception on "Program.cs" a bit more explicitly.
Hello I have been trying to use the PasteSpecial paste values on another sheet but every thing I've tried I keep receiving the same error.
PasteSpecial method of Range class failed
xl.Application xlApp = Globals.ThisWorkbook.Application;
xl.Workbook wb = xlApp.ActiveWorkbook;
xl.Worksheet data = wb.Sheets["Data"];
xl.Worksheet datasheet = wb.Sheets["DataSheet"];
xl.Worksheet pivot = wb.Sheets["Pivot"];
xl.Range dataRng = data.Range["A1:C" +
xlApp.WorksheetFunction.CountA(data.Range["A:A"])];
xl.Range pivotRng = pivot.Range["A1"];
addPivot ap = new addPivot();
ap.DataRange = dataRng;
ap.DestRng = pivotRng;
ap.PivotSheet = pivot;
ap.createPivot("FullName","Date");
pivot.PivotTables("PivotTable1").NullString = "0";
pivot.UsedRange.Copy();
datasheet.Range["A2:GT999"].ClearContents();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
datasheet.Range["2:2"].ClearContents();
I have Tried
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone)
and what you see above.
thanks!
Do the ClearContents before the Copy. Excel is removing your copied range (same as hitting ESC in the program) when you clear those cells out. Then you are trying to PasteSpecial but nothing is in the clipboard any longer.
//...code above
datasheet.Range["A2:GT999"].ClearContents();
pivot.UsedRange.Copy();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
//code below...
If you are debugging this step-by-step, you would see the dashed border around the copied range disappear after the ClearContents call. Excel is a bit odd (but predictable) about when the copied range is forgotten. Editing cells anywhere in the Workbook is one sure way to make it forget.
I use following code to copy one Excel sheet to a different workbook. The source Excel sheet contains chart with series in hidden rows. (theese series are hidden by code in c# before following code)
Worksheet w; //My source worksheet with chart
w.Activate();
w.Name = CreateValidWorksheetName(targetSheetName);
w.get_Range("a1").EntireRow.EntireColumn.Copy();
w.get_Range("a1").EntireRow.EntireColumn.PasteSpecial(XlPasteType.xlPasteValues);
w.Range["A1:A1"].Select();
if (targetWorkbook != null)
{
w.Copy(targetWorkbook.Sheets[1], Type.Missing);
targetWorkbook.RefreshAll();
}
....
targetWorkbook.SaveAs(...
Now the visible series are copied correctly, but hidden series are copied as external links, e.g.:
='C:\X[sourceWorkbook.xlsx]PDK 0-32 kv'!$D$23:$D$100
Now comes the problematic part. When I open the "targetWorkbook" I see the ugly chart including hidden series. But as soon as I open manually in Excel also the "sourceWorkbook", the charts gets automatically fixed and hidden series disapear.
How to achieve this programatically?
I have now lost several hours of my life working around this bug in Worksheet.Copy method. Sometimes I wonder why I was not worse student and I could be doing something more useful now...
First I tried:
//Break links
System.Array links = (System.Array) ((object)targetWorkbook.LinkSources(XlLink.xlExcelLinks));
if (links != null)
{
for (int i = 1; i <= links.Length; i++)
{
try
{
targetWorkbook.UpdateLink((string)links.GetValue(i),
XlLinkType.xlLinkTypeExcelLinks);
targetWorkbook.BreakLink((string)links.GetValue(i),
XlLinkType.xlLinkTypeExcelLinks);
}
catch (Exception ex)
{
Tools.LogException(ex, "targetWorkbook.BreakLink");
}
}
}
No success and I got even HRESULT error. It was not possible to delete external links even using visual interface of Excel, which probably only swallowed this exception.
Finally this did the trick:
w.Activate();
w.Name = CreateValidWorksheetName(targetSheetName);
w.get_Range("a1").EntireRow.EntireColumn.Copy();
w.get_Range("a1").EntireRow.EntireColumn.PasteSpecial(XlPasteType.xlPasteValues);
w.Range["A1:A1"].Select();
if (targetWorkbook != null)
{
//Unhide all rows and then copy!!!
w.get_Range("a1").EntireRow.EntireColumn.Hidden = false;
w.get_Range("a1").EntireColumn.EntireRow.Hidden = false;
w.Copy(targetWorkbook.Sheets[1], Type.Missing);
//Then hide all rows again
HideRows(true, targetWorkbook.Sheets[1].UsedRange, "***HIDETHISROW***");
HideRows(false, targetWorkbook.Sheets[1].UsedRange, "***HIDETHISCOL***");
It is also important to check that the filetypes of the source workbook the target workbook match (both are xlsx), otherwise you may get:
"Excel cannot insert the sheets into the destination workbook, because it contains fewer rows and columns than the source workbook. To move or copy the data to the destination workbook, you can select the data, and then use the Copy and Paste commands to insert it into the sheets of another workbook."
_ExcelApp.DefaultSaveFormat = Microsoft.Office.Interop.Excel.XlFileFormat.xlOpenXMLWorkbook;
I'm trying to move column B in front of column Q in an excel sheet as part of a report I'm working on. I have experience in VBA but relatively little in c# so I've spent the last hour on Google and can't find a solution, I feel like this should be simple but I can't quite get it.
Method one, which results in a “Insert method of Range class failed” msg.
Excel.Range rngCut1 = JobLabourSheet.get_Range("B:B", Type.Missing);
Excel.Range rngPaste1 = JobLabourSheet.get_Range("Q:Q", Type.Missing);
rngCut1.Columns.Cut(rngPaste1.EntireColumn.Insert(Excel.XlInsertShiftDirection.xlShiftToRight, rngCut1));
Method two results in a “Unable to get a Cut property of the Range class” msg.
Excel.Range rngCut1 = JobLabourSheet.get_Range("B:B", Type.Missing);
Excel.Range rngPaste1 = JobLabourSheet.get_Range("Q:Q", Type.Missing);
rngCut1.Columns.Cut(rngPaste1.EntireColumn.Insert(Excel.XlInsertShiftDirection.xlShiftToRight, Missing.Value));
In the second method when I omit the CopyOrigin I get the msg but it does insert a blank column in front of column Q.
In VBA I would use the following:
Columns("B:B").Cut
Columns("Q:Q").Insert Shift:=xlToRight
But like I said, my c# experience is limited at the moment so I have no idea how to go about translating it to c#
This isn't very intuitive, but this is how I got it to work. I took an "insert" range and used the Insert() method and passed a "range.Cut()" method as the "Copy Origin" parameter.
Reference docs:
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range.insert.aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.xlinsertshiftdirection.aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range.cut.aspx
Here is a sample app (be sure to add a reference to Microsoft.Office.Interop.Excel):
using System;
using System.Collections.Generic;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
namespace ExcelCutAndInsertColumn
{
class Program
{
static void Main(string[] args)
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWb = xlApp.Workbooks.Open(#"C:\stackoverflow.xlsx");
Excel.Worksheet xlWs = (Excel.Worksheet)xlWb.Sheets[1]; // Sheet1
xlApp.Visible = true;
// cut column B and insert into A, shifting columns right
Excel.Range copyRange = xlWs.Range["B:B"];
Excel.Range insertRange = xlWs.Range["A:A"];
insertRange.Insert(Excel.XlInsertShiftDirection.xlShiftToRight, copyRange.Cut());
}
}
}