I am not sure why I am getting a System.OutOfMemoryException in SharpDevelop for my program in C#. My program opens up an Excel worksheet and processes some of the data in the worksheet to check for duplicates.
Here is the complete Exception error message:
System.Runtime.InteropServices.COMException: See inner exception(s) for details. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.OutOfMemoryException: Not enough storage is available to complete this operation. (Exception from HRESULT: 0x8007000E (E_OUTOFMEMORY))
at static Object NetOffice.Invoker.PropertyGet(NetOffice.COMObject comObject, System.String name, System.Object[] paramsArray)
at Object NetOffice.ExcelApi.Range.get_Value()
at System.Void excelApp.Program.markDuplicates() in c:\Users\HP\Documents\SharpDevelop Projects\excelApp\excelApp\Program.cs:line 80
at static System.Void excelApp.Program.Main(System.String[] args) in c:\Users\HP\Documents\SharpDevelop Projects\excelApp\excelApp\Program.cs:line 41
Here is my complete program:
It is pointing at the following line in the markDuplicates() method:
Line 80:
Object[,] valuesArray = (Object[,])tableRange.Value;
I have no idea why I am getting this exception.
I am using .NET Framework 4.5.1.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input.Manipulations;
using NetOffice.ExcelApi;
using NetOffice.ExcelApi.Enums;
using Excel = NetOffice.ExcelApi.Application;
namespace excelApp
{
class Program
{
Excel excelApplication;
Workbook workbook;
Worksheet sheet;
HashSet<int> mpanHashCodeList;
[STAThreadAttribute]
public static void Main(string[] args)
{
Program p = new Program();
p.openWorkSheet(#"C:\Users\HP\Desktop\Book1.xlsx", 2);
p.markDuplicates();
Console.ReadKey(true);
}
private void openWorkSheet(string path, int worksheet)
{
excelApplication = new Excel
{
Visible = true,
ScreenUpdating = true
};
try
{
workbook = excelApplication.Workbooks.Open(path);
sheet = (Worksheet)workbook.Worksheets[worksheet];
}
catch
{
Console.WriteLine("File does not exist");
}
}
private void markDuplicates()
{
Range range = sheet.Cells[2,2];
Range rngLastCell = range.get_End(XlDirection.xlToRight)
.get_End(XlDirection.xlDown);
// holds the range of cells in the worksheet
Range tableRange = sheet.Range(range, rngLastCell);
// holds all the values of the range of cells in the worksheet
Object[,] valuesArray = (Object[,])tableRange.Value;
mpanHashCodeList = new HashSet<int>();
int count = 0;
for(var i = 1; i <= valuesArray.GetUpperBound(0); i++)
{
// create a new string for each row
var rowIdBuilder = new StringBuilder(10);
for(var j = 1; j <= valuesArray.GetUpperBound(1); j++)
{
switch(j)
{
case 1:
rowIdBuilder.Append(valuesArray[i,j].ToString());
break;
case 3:
rowIdBuilder.Append(valuesArray[i,j].ToString());
break;
case 4:
rowIdBuilder.Append(valuesArray[i,j].ToString());
break;
case 6:
rowIdBuilder.Append(valuesArray[i,j].ToString());
break;
}
}
Console.WriteLine(rowIdBuilder.ToString());
int hashcode = rowIdBuilder.ToString().GetHashCode();
if(mpanHashCodeList.Contains(hashcode))
{
count++;
mpanHashCodeList.Remove(hashcode);
}
else
{
mpanHashCodeList.Add(hashcode);
}
}
Console.WriteLine(count + " duplicates found");
}
}
}
It's quite common for native code to return error code 0x8007000E (E_OUTOFMEMORY), which is then mapped to OutOfMemory exception in managed code. For example, this is common returned when a native heap allocations fails, or a handle allocation fails.
Such problem is best diagnosed with mixed mode debugging in Visual Studio. Pros would normally use tools like WinDbg/cdb for such tasks with the right symbols properly resolved.
In short: no simple answer. Dirty work expected.
Related
I am coding in C# .NET Core 2.2. I am trying to replace Excel Interop with EPPlusCore in my code for reliability and for portablilty. I have a series of invoices I am opening with EPPlus, but some of them throw a "Null Reference" exception when accessing the Workbook property of the Excel package.
This only happens when running the code without debugging it. When debugging, if I hover over the ExcelPackage item, it refreshes the reference to the Workbook and I am able to run the rest of the code.
public object[,] GetExcelDataEpplus(string filePath, int index,
bool sheetByName = false, string name = null, string password = null)
{
var remoteFileInfo = new FileInfo(filePath);
if (!remoteFileInfo.Exists)
{
throw new Exception("File does not exist: " + filePath);
}
var currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp-excel");
if (!Directory.Exists(currentPath))
{
Directory.CreateDirectory(currentPath);
}
var localFileInfo = new FileInfo(Path.Combine(currentPath, remoteFileInfo.Name));
if (!localFileInfo.Exists)
{
File.Copy(filePath, localFileInfo.FullName);
}
object[,] values = null;
try
{
if (!File.Exists(localFileInfo.FullName))
{
_logger.LogInformation(DbLog, "Cannot find file : " + localFileInfo.FullName);
return null;
}
else
{
_logger.LogInformation(DbLog, "Found file : " + localFileInfo.FullName);
}
_logger.LogInformation(DbLog, "Initializing EPPlus...");
using (var package = string.IsNullOrEmpty(password)
? new ExcelPackage(localFileInfo)
: new ExcelPackage(localFileInfo, password))
{
_logger.LogInformation(DbLog, "Opening Workbook...");
//todo Error Thrown Here
try
{
package.Workbook.FormulaParserManager.LoadFunctionModule(new ImporterFunctionModule());
}
catch (Exception e)
{
_logger.LogWarning(DbLog, e, $"Could not load workbook : Loading file again...");
try
{
System.Threading.Thread.Sleep(1000);
package.Workbook.FormulaParserManager.LoadFunctionModule(new ImporterFunctionModule());
}
catch (Exception ex)
{
_logger.LogError(DbLog, ex, "Could not load workbook");
throw;
}
}
var workbook = package.Workbook;
_logger.LogInformation(DbLog, $"Calculating formulas...");
workbook.Calculate();
_logger.LogInformation(DbLog, "Finding Worksheet...");
var worksheet = sheetByName ? workbook.Worksheets.FirstOrDefault(x => x.Name == name) : workbook.Worksheets[index];
if (worksheet == null)
{
throw new Exception($"Could not find worksheet : {name}");
}
_logger.LogInformation(DbLog, $"Reading from worksheet : {worksheet.Name}...");
var start = worksheet.Dimension.Start;
var end = worksheet.Dimension.End;
values = worksheet.Cells[start.Row, start.Column, end.Row, end.Column].ToMultiDimensionalArray();
}
}
catch (Exception e)
{
_logger.LogError(DbLog, e, $"GetExcelInvoiceDataEpplus from {filePath} ({localFileInfo.FullName})"); //todo propogate error
}
finally
{
File.Delete(localFileInfo.FullName);
}
var rowCount = values?.GetLength(0) ?? 0;
_logger.LogInformation(DbLog, $"EPPLus found {rowCount} rows in the spreadsheet");
return values;
}
On most files, this works correctly, and I get a multidimensional array of the values from the specified worksheet tab. However, on some files, it does not work and I am at a loss as to why.
The closest similar problem I've been able to find is this: https://github.com/JanKallman/EPPlus/issues/416
But if this is accurate, how would I know what worksheet names have bad references without accessing the workbook first?
I found the solution. The excel files that were having problems were ones that were larger than average.
The solution was just to wait longer for them.
try
{
package.Workbook.FormulaParserManager.LoadFunctionModule(new ImporterFunctionModule());
}
catch (Exception e)
{
for (var i=1; i < 6; i++)
{
_logger.LogWarning(DbLog, e, $"Could not load workbook : Loading file again (Attempt #{i})...");
try
{
System.Threading.Thread.Sleep(2000);
package.Workbook.FormulaParserManager.LoadFunctionModule(new ImporterFunctionModule());
break;
}
catch (Exception ex)
{
if (i < 5) continue;
_logger.LogError(DbLog, ex, "Could not load workbook after 5 attempts");
throw;
}
}
}
I'm trying to compile the code that Microsoft provides (SHA-256 hashing) however I get a myriad of issues. Namely these include error CS1065 (Unexpected character '$') and error C2061 (syntax error: identifier 'class', ';'). I'm not familiar with compiling C# programs however I have followed multiple guides to no avail.
Thank you in advance.
using System;
using System.IO;
using System.Security.Cryptography;
public class HashDirectory
{
public static void Main(String[] args)
{
if (args.Length < 1)
{
Console.WriteLine("No directory selected.");
return;
}
string directory = args[0];
if (Directory.Exists(directory))
{
// Create a DirectoryInfo object representing the specified directory.
var dir = new DirectoryInfo(directory);
// Get the FileInfo objects for every file in the directory.
FileInfo[] files = dir.GetFiles();
// Initialize a SHA256 hash object.
using (SHA256 mySHA256 = SHA256.Create())
{
// Compute and print the hash values for each file in directory.
foreach (FileInfo fInfo in files)
{
try {
// Create a fileStream for the file.
FileStream fileStream = fInfo.Open(FileMode.Open);
// Be sure it's positioned to the beginning of the stream.
fileStream.Position = 0;
// Compute the hash of the fileStream.
byte[] hashValue = mySHA256.ComputeHash(fileStream);
// Write the name and hash value of the file to the console.
Console.Write($"{fInfo.Name}: ");
PrintByteArray(hashValue);
// Close the file.
fileStream.Close();
}
catch (IOException e) {
Console.WriteLine($"I/O Exception: {e.Message}");
}
catch (UnauthorizedAccessException e) {
Console.WriteLine($"Access Exception: {e.Message}");
}
}
}
}
else
{
Console.WriteLine("The directory specified could not be found.");
}
}
// Display the byte array in a readable format.
public static void PrintByteArray(byte[] array)
{
for (int i = 0; i < array.Length; i++)
{
Console.Write($"{array[i]:X2}");
if ((i % 4) == 3) Console.Write(" ");
}
Console.WriteLine();
}
}
Can someone provide a link with a tutorial about exporting data to an excel file using c# in an asp.net web application.I searched the internet but I didn't find any tutorials that will explain how they do it.
You can use Interop http://www.c-sharpcorner.com/UploadFile/Globalking/datasettoexcel02272006232336PM/datasettoexcel.aspx
Or if you don't want to install Microsoft Office on a webserver
I recommend using CarlosAg.ExcelXmlWriter which can be found here: http://www.carlosag.net/tools/excelxmlwriter/
code sample for ExcelXmlWriter:
using CarlosAg.ExcelXmlWriter;
class TestApp {
static void Main(string[] args) {
Workbook book = new Workbook();
Worksheet sheet = book.Worksheets.Add("Sample");
WorksheetRow row = sheet.Table.Rows.Add();
row.Cells.Add("Hello World");
book.Save(#"c:\test.xls");
}
}
There is a easy way to use npoi.mapper with just below 2 lines
var mapper = new Mapper();
mapper.Save("test.xlsx", objects, "newSheet");
Pass List to below method, that will convert the list to buffer and then return buffer, a file will be downloaded.
List<T> resultList = New List<T>();
byte[] buffer = Write(resultList, true, "AttendenceSummary");
return File(buffer, "application/excel", reportTitle + ".xlsx");
public static byte[] Write<T>(IEnumerable<T> list, bool xlsxExtension = true, string sheetName = "ExportData")
{
if (list == null)
{
throw new ArgumentNullException("list");
}
XSSFWorkbook hssfworkbook = new XSSFWorkbook();
int Rowspersheet = 15000;
int TotalRows = list.Count();
int TotalSheets = TotalRows / Rowspersheet;
for (int i = 0; i <= TotalSheets; i++)
{
ISheet sheet1 = hssfworkbook.CreateSheet(sheetName + "_" + i);
IRow row = sheet1.CreateRow(0);
int index = 0;
foreach (PropertyInfo property in typeof(T).GetProperties())
{
ICellStyle cellStyle = hssfworkbook.CreateCellStyle();
IFont cellFont = hssfworkbook.CreateFont();
cellFont.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
cellStyle.SetFont(cellFont);
ICell cell = row.CreateCell(index++);
cell.CellStyle = cellStyle;
cell.SetCellValue(property.Name);
}
int rowIndex = 1;
// int rowIndex2 = 1;
foreach (T obj in list.Skip(Rowspersheet * i).Take(Rowspersheet))
{
row = sheet1.CreateRow(rowIndex++);
index = 0;
foreach (PropertyInfo property in typeof(T).GetProperties())
{
ICell cell = row.CreateCell(index++);
cell.SetCellValue(Convert.ToString(property.GetValue(obj)));
}
}
}
MemoryStream file = new MemoryStream();
hssfworkbook.Write(file);
return file.ToArray();
}
You can try the following links :
http://www.codeproject.com/Articles/164582/8-Solutions-to-Export-Data-to-Excel-for-ASP-NET
Export data as Excel file from ASP.NET
http://codeissue.com/issues/i14e20993075634/how-to-export-gridview-control-data-to-excel-file-using-asp-net
I've written a C# class, which lets you write your DataSet, DataTable or List<> data directly into a Excel .xlsx file using the OpenXML libraries.
http://mikesknowledgebase.com/pages/CSharp/ExportToExcel.htm
It's completely free to download, and very ASP.Net friendly.
Just pass my C# function the data to be written, the name of the file you want to create, and your page's "Response" variable, and it'll create the Excel file for you, and write it straight to the Page, ready for the user to Save/Open.
class Employee;
List<Employee> listOfEmployees = new List<Employee>();
// The following ASP.Net code gets run when I click on my "Export to Excel" button.
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
// It doesn't get much easier than this...
CreateExcelFile.CreateExcelDocument(listOfEmployees, "Employees.xlsx", Response);
}
(I work for a finanical company, and we'd be lost without this functionality in every one of our apps !!)
Recently we have encounter this issue about exporting reports to excel file using infragistics library.
Our report table contains more or less 1.26 million records as of now. We are trying to create an excel(2007) file for this, so approximately this will be 2 worksheets because only about 1million rows is supported by excel 2007 per sheet.
We have no problem during the creation of worksheets and during the filling of data into the worksheet, memory is manageable at this point.
Our problem is when writing the excel data into the stream(file, memory).
The memory consumption is just too high and when it reaches 1.8GB it will no longer continue.
P.S. This is a summary report, the client wants this in a single excel file.
Your recommendations are much appreciated!!!
Below is the code that i have pulled out from the actual source but i have replaced the data retrieval part and instead use a dummy data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Runtime;
using System.Diagnostics;
using Infragistics.Excel;
namespace ConsoleApplication1
{
class Program
{
static bool tip = true;
static void Main(string[] args)
{
var xls = Workbook.MaxExcelRowCount;
var xlsx =Workbook.MaxExcel2007RowCount;
var wb = new Workbook(WorkbookFormat.Excel2007, WorkbookPaletteMode.StandardPalette);
var ws = wb.Worksheets.Add("Sheet1");
var sheetCtr = 1;
var limit = 1256898;
var rr = 0;
for (var row = 0; row < limit; row++)
{
if (rr >= xlsx - 1)
{
//create new worksheet if row exceeded the limit
ws = wb.Worksheets.Add("Sheet" + sheetCtr.ToString());
rr = 0;
ClearMemory();
}
for (var col = 0; col < 10; col++)
{
ws.Rows[rr].Cells[col].Value = string.Format("data{0}-{1}", row, col);
}
Console.Write("Row=");
Console.Write(row.ToString());
Console.WriteLine();
rr++;
}
Console.Write("Saving worksheet...");
//this part uses too much memory
wb.Save("wb2.xlsx");
Console.WriteLine("Workbook saved.");
Console.WriteLine("Done");
Console.Read();
}
static void ClearMemory()
{
try
{
System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();
if (tip == true)
{
proc.MaxWorkingSet = (IntPtr)((int)proc.MaxWorkingSet - 1);
proc.MinWorkingSet = (IntPtr)((int)proc.MinWorkingSet - 1);
}
else
{
proc.MaxWorkingSet = (IntPtr)((int)proc.MaxWorkingSet + 1);
proc.MinWorkingSet = (IntPtr)((int)proc.MinWorkingSet + 1);
}
tip = !tip;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
In a catch block, how can I get the line number which threw an exception?
If you need the line number for more than just the formatted stack trace you get from Exception.StackTrace, you can use the StackTrace class:
try
{
throw new Exception();
}
catch (Exception ex)
{
// Get stack trace for the exception with source file information
var st = new StackTrace(ex, true);
// Get the top stack frame
var frame = st.GetFrame(0);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
}
Note that this will only work if there is a pdb file available for the assembly.
Simple way, use the Exception.ToString() function, it will return the line after the exception description.
You can also check the program debug database as it contains debug info/logs about the whole application.
If you don't have the .PBO file:
C#
public int GetLineNumber(Exception ex)
{
var lineNumber = 0;
const string lineSearch = ":line ";
var index = ex.StackTrace.LastIndexOf(lineSearch);
if (index != -1)
{
var lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length);
if (int.TryParse(lineNumberText, out lineNumber))
{
}
}
return lineNumber;
}
Vb.net
Public Function GetLineNumber(ByVal ex As Exception)
Dim lineNumber As Int32 = 0
Const lineSearch As String = ":line "
Dim index = ex.StackTrace.LastIndexOf(lineSearch)
If index <> -1 Then
Dim lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length)
If Int32.TryParse(lineNumberText, lineNumber) Then
End If
End If
Return lineNumber
End Function
Or as an extentions on the Exception class
public static class MyExtensions
{
public static int LineNumber(this Exception ex)
{
var lineNumber = 0;
const string lineSearch = ":line ";
var index = ex.StackTrace.LastIndexOf(lineSearch);
if (index != -1)
{
var lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length);
if (int.TryParse(lineNumberText, out lineNumber))
{
}
}
return lineNumber;
}
}
You could include .PDB symbol files associated to the assembly which contain metadata information and when an exception is thrown it will contain full information in the stacktrace of where this exception originated. It will contain line numbers of each method in the stack.
Check this one
StackTrace st = new StackTrace(ex, true);
//Get the first stack frame
StackFrame frame = st.GetFrame(0);
//Get the file name
string fileName = frame.GetFileName();
//Get the method name
string methodName = frame.GetMethod().Name;
//Get the line number from the stack frame
int line = frame.GetFileLineNumber();
//Get the column number
int col = frame.GetFileColumnNumber();
I added an extension to Exception which returns the line, column, method, filename and message:
public static class Extensions
{
public static string ExceptionInfo(this Exception exception)
{
StackFrame stackFrame = (new StackTrace(exception, true)).GetFrame(0);
return string.Format("At line {0} column {1} in {2}: {3} {4}{3}{5} ",
stackFrame.GetFileLineNumber(), stackFrame.GetFileColumnNumber(),
stackFrame.GetMethod(), Environment.NewLine, stackFrame.GetFileName(),
exception.Message);
}
}
Convert.ToInt32(ex.StackTrace.Substring(ex.StackTrace.LastIndexOf(' ')));
This will give the Exception line no.
I tried using the solution By #davy-c but had an Exception "System.FormatException: 'Input string was not in a correct format.'", this was due to there still being text past the line number, I modified the code he posted and came up with:
int line = Convert.ToInt32(objErr.ToString().Substring(objErr.ToString().IndexOf("line")).Substring(0, objErr.ToString().Substring(objErr.ToString().IndexOf("line")).ToString().IndexOf("\r\n")).Replace("line ", ""));
This works for me in VS2017 C#.
Update to the answer
// Get stack trace for the exception with source file information
var st = new StackTrace(ex, true);
// Get the top stack frame
var frame = st.GetFrame(st.FrameCount-1);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
Extension Method
static class ExceptionHelpers
{
public static int LineNumber(this Exception ex)
{
int n;
int i = ex.StackTrace.LastIndexOf(" ");
if (i > -1)
{
string s = ex.StackTrace.Substring(i + 1);
if (int.TryParse(s, out n))
return n;
}
return -1;
}
}
Usage
try
{
throw new Exception("A new error happened");
}
catch (Exception ex)
{
//If error in exception LineNumber() will be -1
System.Diagnostics.Debug.WriteLine("[" + ex.LineNumber() + "] " + ex.Message);
}
Working for me:
var st = new StackTrace(e, true);
// Get the bottom stack frame
var frame = st.GetFrame(st.FrameCount - 1);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
var method = frame.GetMethod().ReflectedType.FullName;
var path = frame.GetFileName();
Line numbers will be included in the stack trace if the library which generated the exception is compiled with debug symbols. This can be a separate file (*.pdb) or embedded in the library.
For .NET Core, .NET 5 and later, to have full exception line numbers in release builds, configure the project as follows:
<PropertyGroup>
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<!-- Only enable the following if the line numbers mismatch -->
<!--<Optimize>false</Optimize>-->
<!--
Additional properties which may impact how printed line numbers match the source code line numbers are listed here:
https://learn.microsoft.com/en-us/dotnet/core/run-time-config/compilation
-->
</PropertyGroup>
The above configuration will include debug symbols directly with the built files, which can be published as nugets.
An alternative to the above is to restore debug packages together with the main nuget packages, which is currently not yet supported: https://github.com/NuGet/Home/issues/9667
Now get the exception line numbers:
try
{
throw new Exception();
}
catch (Exception ex)
{
// Get stack trace for the exception with source file information
var st = new StackTrace(ex, true);
// Get the top stack frame
var frame = st.GetFrame(0);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
}
If your stack trace is bigger than one it should be:
var st = new StackTrace(es, true);
// Get the top stack frame
var frame = st.GetFrame(st.FrameCount - 1);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
protected void Page_Load(object sender, EventArgs e)
{
try
{
int line1 = 1;
int line2 =int.Parse("Test");
}
catch (Exception ex)
{
var st =new System.Diagnostics.StackTrace(ex,true);
var frame = st.GetFrame(st.FrameCount - 1);
var linenumber = frame.GetFileLineNumber();
}
}
In Global.resx file there is an event called Application_Error
it fires whenever an error occurs,,you can easily get any information about the error,and send it to a bug tracking e-mail.
Also i think all u need to do is to compile the global.resx and add its dll's (2 dlls) to your bin folder and it will work!