Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/EPPlus/ExcelPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,9 @@ public void Dispose()
public void Save()
{
CheckNotDisposed();
#if !NET35
Workbook.ThrowIfCalculationCancelled();
#endif
try
{
if (_stream is MemoryStream && _stream.Length > 0)
Expand Down
31 changes: 31 additions & 0 deletions src/EPPlus/ExcelWorkbook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,37 @@ private void GetSharedStrings()
}


#region Calculation cancellation (poison flag)

#if !NET35
internal bool IsCalculationCancelled { get; private set; }

internal void MarkCalculationCancelled()
{
IsCalculationCancelled = true;
}

internal void ThrowIfCalculationCancelled()
{
if (IsCalculationCancelled)
{
throw new InvalidOperationException(
"This workbook has been left in an inconsistent state due to a cancelled " +
"calculation. The workbook must be disposed and cannot be used for further " +
"operations. Reload the workbook from the source to continue.");
}
}

/// <summary>
/// Returns true if a calculation was cancelled, leaving the workbook in an inconsistent state.
/// A workbook in this state must be disposed � saving or recalculating is not permitted.
/// </summary>
public bool IsCalculationInconsistent => IsCalculationCancelled;
#endif

#endregion


internal void GetDefinedNames()
{
XmlNodeList nl = WorkbookXml.SelectNodes("//d:definedNames/d:definedName", NameSpaceManager);
Expand Down
69 changes: 53 additions & 16 deletions src/EPPlus/FormulaParsing/CalculateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public static void Calculate(this ExcelWorkbook workbook, Action<ExcelCalculatio
/// <param name="options">Calculation options</param>
public static void Calculate(this ExcelWorkbook workbook, ExcelCalculationOption options)
{
#if !NET35
workbook.ThrowIfCalculationCancelled(); // Guard: prevent recalc on poisoned workbook
#endif

Init(workbook);

var filterInfo = new FilterInfo(workbook);
Expand All @@ -74,13 +78,25 @@ public static void Calculate(this ExcelWorkbook workbook, ExcelCalculationOption
}

//CalcChain(workbook, workbook.FormulaParser, dc, options);
var dc=RpnFormulaExecution.Execute(workbook, options);
dc._parsingContext.RangeCriteriaCache?.Clear();
if (workbook.FormulaParser.Logger != null)
#if !NET35
try
{
var msg = string.Format("Calculation done...number of cells parsed: {0}", dc.processedCells.Count);
workbook.FormulaParser.Logger.Log(msg);
#endif
var dc =RpnFormulaExecution.Execute(workbook, options);
dc._parsingContext.RangeCriteriaCache?.Clear();
if (workbook.FormulaParser.Logger != null)
{
var msg = string.Format("Calculation done...number of cells parsed: {0}", dc.processedCells.Count);
workbook.FormulaParser.Logger.Log(msg);
}
#if !NET35
}
catch (OperationCanceledException)
{
workbook.MarkCalculationCancelled();
throw;
}
#endif
}
internal static RpnOptimizedDependencyChain CalculateWithDC(this ExcelWorkbook workbook, Action<ExcelCalculationOption> configHandler)
{
Expand Down Expand Up @@ -158,8 +174,21 @@ public static void Calculate(this ExcelWorksheet worksheet, Action<ExcelCalculat
public static void Calculate(this ExcelWorksheet worksheet, ExcelCalculationOption options)
{
Init(worksheet.Workbook);
var dc = RpnFormulaExecution.Execute(worksheet, options);
dc._parsingContext.RangeCriteriaCache?.Clear();
#if !NET35
worksheet.Workbook.ThrowIfCalculationCancelled();
try
{
#endif
var dc = RpnFormulaExecution.Execute(worksheet, options);
dc._parsingContext.RangeCriteriaCache?.Clear();
#if !NET35
}
catch (OperationCanceledException)
{
worksheet.Workbook.MarkCalculationCancelled();
throw;
}
#endif
}

/// <summary>
Expand Down Expand Up @@ -195,15 +224,23 @@ public static void Calculate(this ExcelRangeBase range, Action<ExcelCalculationO
/// <param name="options">Calculation options</param>
public static void Calculate(this ExcelRangeBase range, ExcelCalculationOption options)
{
Init(range._workbook);
//var parser = range._workbook.FormulaParser;
//var filterInfo = new FilterInfo(range._workbook);
//parser.InitNewCalc(filterInfo);
//var dc = DependencyChainFactory.Create(range, options);
//CalcChain(range._workbook, parser, dc, options);
var dc = RpnFormulaExecution.Execute(range, options);
// Clear RangeCriteriaCache after calculation completes
dc._parsingContext.RangeCriteriaCache?.Clear();
#if !NET35
range._workbook.ThrowIfCalculationCancelled();
try
{
#endif
Init(range._workbook);
var dc = RpnFormulaExecution.Execute(range, options);
// Clear RangeCriteriaCache after calculation completes
dc._parsingContext.RangeCriteriaCache?.Clear();
#if !NET35
}
catch (OperationCanceledException)
{
range._workbook.MarkCalculationCancelled();
throw;
}
#endif
}

/// <summary>
Expand Down
42 changes: 42 additions & 0 deletions src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ internal static RpnOptimizedDependencyChain Execute(ExcelWorkbook wb, ExcelCalcu
var depChain = new RpnOptimizedDependencyChain(wb, options);
foreach (var ws in wb.Worksheets)
{
#if !NET35
options.CancellationToken.ThrowIfCancellationRequested();
#endif
if (ws.IsChartSheet == false)
{
ExecuteChain(depChain, ws.Cells, options, true);
Expand Down Expand Up @@ -118,9 +121,15 @@ private static void ExecuteChain(RpnOptimizedDependencyChain depChain, ExcelRang
{
var ws = range.Worksheet;
RpnFormula f = null;
#if !NET35
var ct = options.CancellationToken; // Cache locally — avoids property lookup in hot loop
#endif
var fs = new CellStoreEnumerator<object>(ws._formulas, range._fromRow, range._fromCol, range._toRow, range._toCol);
while (fs.Next())
{
#if !NET35
ct.ThrowIfCancellationRequested(); // P0 – per cell
#endif
if (fs.Value == null || fs.Value.ToString().Trim() == "") continue;
var id = ExcelCellBase.GetCellId(ws.IndexInList, fs.Row, fs.Column);
if (depChain.processedCells.Contains(id) == false)
Expand All @@ -132,6 +141,12 @@ private static void ExecuteChain(RpnOptimizedDependencyChain depChain, ExcelRang
CalculateFormulaChain(depChain, f, options, writeToCell);
}
}
#if !NET35
catch (OperationCanceledException)
{
throw; // Must propagate — do not swallow
}
#endif
catch (CircularReferenceException)
{
throw;
Expand Down Expand Up @@ -240,6 +255,12 @@ private static void ExecuteChain(RpnOptimizedDependencyChain depChain, ExcelName
ExecuteName(depChain, name, options, writeToCell);
}
}
#if !NET35
catch (OperationCanceledException)
{
throw; // Must propagate
}
#endif
catch (CircularReferenceException)
{
throw;
Expand Down Expand Up @@ -289,6 +310,12 @@ private static object ExecuteChain(RpnOptimizedDependencyChain depChain, ExcelWo
f.SetFormula(formula, depChain);
return CalculateFormulaChain(depChain, f, options, writeToCell).Result;
}
#if !NET35
catch (OperationCanceledException)
{
throw; // Must propagate
}
#endif
catch (CircularReferenceException)
{
throw;
Expand All @@ -309,6 +336,12 @@ private static object ExecuteChain(RpnOptimizedDependencyChain depChain, ExcelWo
f._row = -1;
return CalculateFormulaChain(depChain, f, options, writeToCell).Result;
}
#if !NET35
catch (OperationCanceledException)
{
throw; // Must propagate
}
#endif
catch (CircularReferenceException)
{
throw;
Expand Down Expand Up @@ -419,6 +452,9 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
ExecuteFormula:
try
{
#if !NET35
options.CancellationToken.ThrowIfCancellationRequested(); // P0 – per dependency step
#endif
SetCurrentCell(depChain, f);
var ws = f._ws;
if (f._tokenIndex < f._tokens.Count)
Expand Down Expand Up @@ -568,6 +604,12 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d

goto ExecuteFormula;
}
#if !NET35
catch (OperationCanceledException)
{
throw; // Must propagate
}
#endif
catch (CircularReferenceException)
{
throw;
Expand Down
23 changes: 23 additions & 0 deletions src/EPPlus/FormulaParsing/ExcelCalculationOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Date Author Change
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
#elif (!NET35)
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
#else
using System.Configuration;
using System.Collections.Generic;
Expand Down Expand Up @@ -115,5 +121,22 @@ public bool EnableUnicodeAwareStringOperations
{
get; set;
} = false;

#if !NET35
/// <summary>
/// A cancellation token that can be used to cancel a running calculation.
/// When cancelled, an <see cref="OperationCanceledException"/> will be thrown
/// and the workbook will be left in an inconsistent, partially calculated state.
/// The workbook must be discarded after cancellation � saving or recalculating
/// a cancelled workbook is not permitted.
/// </summary>
/// <example>
/// <code>
/// using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
/// workbook.Calculate(opt => opt.CancellationToken = cts.Token);
/// </code>
/// </example>
public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
#endif
}
}
Loading