Exception (HRESULT: 0x800AC472) when using Excel.Worksheet.Select after calling Excel.Workbook.Sa...
Categories:
Resolving HRESULT: 0x800AC472 Exception with Excel.Worksheet.Select after SaveAs

Understand and fix the 'HRESULT: 0x800AC472' exception that occurs when programmatically selecting an Excel worksheet immediately after saving the workbook using SaveAs
in C#.
When automating Microsoft Excel using C# (or other .NET languages) via COM Interop, developers often encounter a peculiar exception: HRESULT: 0x800AC472
. This error typically manifests when attempting to select an Excel.Worksheet
object immediately after performing a Excel.Workbook.SaveAs
operation. This article delves into the root cause of this issue and provides robust solutions to ensure smooth Excel automation.
Understanding the HRESULT: 0x800AC472 Exception
The HRESULT: 0x800AC472
error, often translated as VBA_E_IGNORE
or RPC_E_SERVERCALL_RETRYLATER
, indicates that the COM server (in this case, Excel) is busy or in a state where it cannot immediately process the requested operation. After a SaveAs
call, Excel might be performing background tasks like saving the file, updating internal references, or refreshing its UI. If your code tries to interact with Excel too quickly during this period, it can lead to this exception.
sequenceDiagram participant C#App as C# Application participant Excel as Excel Application C#App->>Excel: Open Workbook C#App->>Excel: Perform Operations (e.g., write data) C#App->>Excel: Call Workbook.SaveAs("new_path.xlsx") Note over Excel: Excel is busy saving the file and updating internal states. C#App->>Excel: Call Worksheet.Select() (TOO SOON!) Excel-->>C#App: HRESULT: 0x800AC472 Exception Note over C#App,Excel: The 'Select' operation failed because Excel was not ready.
Sequence diagram illustrating the HRESULT: 0x800AC472 exception flow.
Common Scenarios and Reproducing the Error
This error is most frequently observed in scenarios where a workbook is opened, modified, saved to a new location using SaveAs
, and then an immediate attempt is made to select a different sheet or perform another UI-related action. The key is the SaveAs
operation, which can be more resource-intensive than a simple Save
.
using Excel = Microsoft.Office.Interop.Excel;
public void ReproduceError()
{
Excel.Application excelApp = null;
Excel.Workbook workbook = null;
Excel.Worksheet worksheet1 = null;
Excel.Worksheet worksheet2 = null;
try
{
excelApp = new Excel.Application();
excelApp.Visible = true;
workbook = excelApp.Workbooks.Add();
worksheet1 = (Excel.Worksheet)workbook.Sheets[1];
worksheet1.Name = "Sheet1";
worksheet1.Cells[1, 1] = "Hello from Sheet1";
worksheet2 = (Excel.Worksheet)workbook.Sheets.Add(After: worksheet1);
worksheet2.Name = "Sheet2";
worksheet2.Cells[1, 1] = "Hello from Sheet2";
// Save the workbook to a new file
string newFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "TestWorkbook.xlsx");
workbook.SaveAs(newFilePath);
// IMMEDIATELY try to select another sheet - this often causes the error
worksheet2.Select(); // <-- HRESULT: 0x800AC472 often occurs here
System.Console.WriteLine("Sheet2 selected successfully (if no error occurred).");
}
catch (Exception ex)
{
System.Console.WriteLine($"An error occurred: {ex.Message}");
if (ex.HResult == unchecked((int)0x800AC472))
{
System.Console.WriteLine("This is the expected HRESULT: 0x800AC472 exception.");
}
}
finally
{
// Proper cleanup of COM objects
if (worksheet2 != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet2);
if (worksheet1 != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet1);
if (workbook != null) { workbook.Close(false); System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook); }
if (excelApp != null) { excelApp.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp); }
}
}
C# code demonstrating a scenario likely to trigger the HRESULT: 0x800AC472 exception.
Solutions to Prevent the Exception
The core problem is Excel's temporary unresponsiveness. The solutions revolve around giving Excel enough time to complete its internal operations or ensuring that the Select
call is made on a valid, responsive object.
Marshal.ReleaseComObject
to prevent memory leaks and ensure Excel processes are terminated correctly, especially in server-side applications.Solution 1: Introduce a Delay
The simplest, though not always the most elegant, solution is to introduce a small delay after the SaveAs
call. This gives Excel a moment to catch up. However, the optimal delay can vary depending on system performance and file size, making it a less reliable approach for production systems.
workbook.SaveAs(newFilePath);
// Introduce a short delay
System.Threading.Thread.Sleep(500); // Wait for 500 milliseconds
worksheet2.Select(); // Should now work without error
Adding a Thread.Sleep
after SaveAs
to mitigate the exception.
Solution 2: Re-obtain the Worksheet Object
A more robust solution is to re-obtain the Worksheet
object from the Workbook
's Sheets
collection after the SaveAs
operation. When SaveAs
is called, Excel might internally invalidate or update references to existing objects. By getting a fresh reference, you ensure you're interacting with a valid, up-to-date object.
workbook.SaveAs(newFilePath);
// Release the old reference to worksheet2
if (worksheet2 != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet2);
// Re-obtain the worksheet object from the workbook
worksheet2 = (Excel.Worksheet)workbook.Sheets["Sheet2"]; // Or by index if name is not guaranteed
worksheet2.Select(); // This is much more likely to succeed
Re-obtaining the worksheet object after SaveAs
to ensure a fresh reference.
Solution 3: Combine Re-obtaining with a Retry Mechanism
For maximum robustness, especially in environments where Excel might be under heavy load, combine re-obtaining the object with a simple retry loop. This handles transient busy states more gracefully.
workbook.SaveAs(newFilePath);
// Release the old reference to worksheet2
if (worksheet2 != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet2);
int retryCount = 0;
const int maxRetries = 5;
const int delayMs = 100;
while (retryCount < maxRetries)
{
try
{
// Re-obtain the worksheet object
worksheet2 = (Excel.Worksheet)workbook.Sheets["Sheet2"];
worksheet2.Select();
System.Console.WriteLine("Sheet2 selected successfully after retry.");
break; // Success, exit loop
}
catch (Exception ex)
{
if (ex.HResult == unchecked((int)0x800AC472) && retryCount < maxRetries - 1)
{
System.Console.WriteLine($"Retry {retryCount + 1}: Excel busy, retrying in {delayMs}ms...");
System.Threading.Thread.Sleep(delayMs);
retryCount++;
}
else
{
throw; // Re-throw if it's not the expected error or max retries reached
}
}
}
Implementing a retry mechanism with re-obtaining the worksheet object.
Excel.Application
directly in server-side applications (e.g., ASP.NET, Windows Services) is generally discouraged by Microsoft due to stability, security, and scalability issues. Consider using libraries like EPPlus or OpenXML SDK for such scenarios, which do not require Excel to be installed.