c# Excel Interop alternatives to waiting to handle 0x800AC472 error - c#

I have an application that writes many times to a formula/macro-laden workbook. It loops through some data 3 times creating, filling, saving, then closing an excel file in each iteration. While it works fine when it's the only version of itself running, if there are multiple instances of it running it has trouble.
Specifically, I'm getting a 0x800AC472 error when writing data to certain cells. It isn't the same cell or value each time but it has seemed to be on the second pass through each time. This is the relevant code:
public void SetCellValue(int row, int col, string val)
{
if (_currWorkSheet != null)
{
string parms = string.Format("row={0}; col={1}; val={2}", row.ToString(), col.ToString(), val);
for (short i = 0; i < _maxRetries; i++)
{
try { (_currWorkSheet.Cells[row, col] as Range).Value2 = val; return; }
catch (Exception ex) { HandleError(ex, parms); }
}
Exception newExc = new Exception("Too many retries attempting to set cell value.");
newExc.Data.Add("parms", parms);
throw newExc;
}
}
private void HandleError(Exception exc, string parms)
{
if (exc != null && exc.Message != null)
{
// Excel error that just needs more time to complete. http://social.msdn.microsoft.com/forums/en-US/vsto/thread/9168f9f2-e5bc-4535-8d7d-4e374ab8ff09/
if (exc.Message.Contains("0x800AC472"))
Thread.Sleep(_threadSleepMs); // Give excel a chance to catch up, then keep processing.
else
{
Exception newExc = new Exception("Unexpected Error", exc);
newExc.Data.Add("parms", parms);
throw newExc;
}
}
}
I've set the _maxRetries to 10 and the _threadSleepMs to 500 and continue to get the error, so I don't think that increasing it anymore makes sense.
I was wondering if there are alternatives to sleeping the thread to give it a chance to get "unstuck" as it were.
And maybe this would qualify as a second question but I'm not as concerned about this but, when it crashes I still perform a Close() on it in the finally block, but I still have instances of it hanging around. This is how I close it:
public void Dispose()
{
if (!_disposed)
{
if (_currWorkBook != null)
for (short i = 0; i < _maxRetries; i++)
{
try { _currWorkBook.Close(false, _missing, _missing); break; }
catch (Exception ex) { HandleError(ex, ""); }
}
if (_app != null)
{
if (_app.Workbooks != null)
for (short i = 0; i < _maxRetries; i++)
{
try { _app.Workbooks.Close(); break; }
catch (Exception ex) { HandleError(ex, ""); }
}
for (short i = 0; i < _maxRetries; i++)
{
try { _app.Quit(); break; }
catch (Exception ex) { HandleError(ex, ""); }
}
if (_currWorkSheet != null)
{
Marshal.ReleaseComObject(_currWorkSheet);
_currWorkSheet = null;
}
if (_currWorkBook != null)
{
Marshal.ReleaseComObject(_currWorkBook);
_currWorkBook = null;
}
Marshal.ReleaseComObject(_app);
_app = null;
}
GC.Collect();
_disposed = true;
}
}
It doesn't throw and error, so I was just wondering if there were any holes in it?
Thank you,
Jeff

The only solution I was able to come up with was to create a lock on the thread once it needs to use the excel functionality. This just ensured I only have one process using excel at one time. It's not perfect, especially if unrelated processes also try to use excel, but it was the only fix I could come up with.

In my experience Excel cannot be made to run reliably with multiple instances being driven from COM Interop. We have a lot of test code which we run against Excel so we've tried to get this to work. The only solution is to limit your application to one instance at a time - and even then my experience is that there is an occasional unexplained exception.
SpreadsheetGear for .NET might solve your problem. SpreadsheetGear has a "workbook set" which is roughly analogous to an Excel application, and supports any number of workbook sets being used from different threads.
You can see live ASP.NET samples here and download the free trial here if you want to try it yourself.
Disclaimer: I own SpreadsheetGear LLC

You can often get this error because you are trying to work with an Office document on a synced folder (your home folder for example in a corporate desktop)
Just copy the files to a local disk folder first (e.g. temp), and all should be OK
If you are updating the Excel workbook, or waiting for a data table or PivotTable refresh use some artificial delays
e.g
field.ClearAllFilters();
Thread.Sleep(500);
Application.DoEvents();
field.CurrentPageName = value;
Thread.Sleep(500);
Application.DoEvents();

Related

Function ...Workbooks.Open() is failed while opening .xla and .xla addins of excel through c# - HRESULT: 0x80010105 (RPC_E_SERVERFAULT)

I wrote a code in c# in Visual studio and I open excels, run macro and close the excel
I do it in cycles (in a loop and delay between every cycle)
sometimes the function failes and sometimes it works
The error message I got:
"The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT))"
Please can someone help me ???
public void Open()
{
try
{
ExcelApp.Workbooks.Open(#"C:\Program Files (x86)\REFPROP\REFPROP.XLA");
ExcelApp.Workbooks.Open(#"C:\Program Files (x86)\REFPROP\REFPROP_Ribbon.xlam");
foreach (Excel.AddIn item in ExcelApp.AddIns)
{
if (item.Name.Equals("REFPROP.XLA") || item.Name.Equals("REFPROP_Ribbon.xlam"))
{
item.Installed = false;
item.Installed = true;
}
}
Thread.Sleep(3000);
//so then opening excel workbooks:
ExcelBook = ExcelApp.Workbooks.Open(ExcelPath);
Opened = true;
Thread.Sleep(3000);
ExcelApp.Workbooks.Open(#"C:\Program Files (x86)\REFPROP\REFPROP.XLA");
ExcelApp.Workbooks.Open(#"C:\Program Files (x86)\REFPROP\REFPROP_Ribbon.xlam");
foreach (Excel.AddIn item in ExcelApp.AddIns)
{
if (item.Name.Equals("REFPROP.XLA") || item.Name.Equals("REFPROP_Ribbon.xlam"))
{
item.Installed = false;
item.Installed = true;
}
}
Thread.Sleep(3000);
}
catch (Exception e)
{
throw;
}
}
There can be multiple reasons why you are getting that exception at runtime.
First, I'd suggest releasing underlying COM objects in the code and do not rely on the GC. Use the Marshal.ReleaseComObject method for that and then set the object to null.
Second, try to set the calculation mode to manual.
Third, make sure that the file doesn't require admin privileges.
You may find a similar thread helpful, see Excel interop The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT)).

WCF Service hosted in Windows Service hangs on stop

I have a WCF Service hosted in a Windows service as described here.
I have scheduled nightly restart of the service, but sometimes the restart fails and the service remains/hangs in Stopping state and the EXE process has to be killed manually. It looks likely that it hangs on line _ESSServiceHost.Close();, because nothing after that line is logged it the log file. It is possible but not very likely that the service gets the stop request when it is busy.
Moreover the underlying process cannot be killed because it is dependent on services.exe, so only server restart works.
What could be wrong with this approach?
protected override void OnStop()
{
try
{
if (_ESSServiceHost != null)
{
_ESSServiceHost.Close();
_ESSServiceHost = null;
//Never reaches the following line
Tools.LogInfo("Services stopped.");
}
}
catch (Exception ex)
{
Tools.LogError(ex.Message);
}
This is how I stop the service:
private bool StopService(ServiceController scESiftServer)
{
int i = 0;
if (scESiftServer.Status == ServiceControllerStatus.Running)
{
try
{
scESiftServer.Stop();
}
catch (Exception ex)
{
Tools.LogEvent("Exception ...");
return false;
}
while (scESiftServer.Status != ServiceControllerStatus.Stopped && i < 120)
{
Thread.Sleep(1000);
scESiftServer.Refresh();
i++;
}
}
if (scESiftServer.Status != ServiceControllerStatus.Stopped)
{
//This line gets executed
Tools.LogEvent("Failed within 120 sec...");
return false;
}
else
{
Tools.LogEvent("OK ...");
}
return true;
}
Could something like this help?
var task = Task.Run(() => _ESSServiceHost.Close(TimeSpan.FromSeconds(299)));
if (!task.Wait(TimeSpan.FromSeconds(300)))
{
_ESSServiceHost.Abort();
}
But _ESSServiceHost.Abort() should be called internally by the Close method if needed.
Target framework is 4.5, installed is .NET 4.7.2.
Found out that probably the service hangs after series of malformed requests. Expected record type 'Version', found '71'. etc.
I have found in the svclog file that my service hangs after series of malformed request that happen on Saturday and Sunday at approx. 5:15 AM. The error messages were Expected record type 'Version', found '71'., Error while reading message framing format at position 0 of stream (state: ReadingVersionRecord). But I could not find the cause of theese malformed request series, so I tried to fix the service to withstand the "attack".
I have modified the OnStop method as follows:
protected override void OnStop()
{
try
{
if (_ESSServiceHost != null)
{
Tools.LogInfo("Stopping ESService.");
var abortTask = Task.Run(() => _ESSServiceHost.Abort());
var closeTask = Task.Run(() => _ESSServiceHost.Close(TimeSpan.FromSeconds(300)));
try
{
if (_ESSServiceHost.State == CommunicationState.Faulted)
{
Tools.LogInfo("ESSServiceHost.State == CommunicationState.Faulted");
if (!abortTask.Wait(TimeSpan.FromSeconds(60)))
Tools.LogInfo("Failed to Abort.");
}
else
{
if (!closeTask.Wait(TimeSpan.FromSeconds(301)))
{
Tools.LogInfo("Failed to Close - trying Abort.");
if (!abortTask.Wait(TimeSpan.FromSeconds(60)))
Tools.LogInfo("Failed to Abort.");
}
}
}
catch (Exception ex)
{
Tools.LogException(ex, "ESSServiceHost.Close");
try
{
Tools.LogInfo("Abort.");
if (!abortTask.Wait(TimeSpan.FromSeconds(60)))
Tools.LogInfo("Failed to Abort.");
}
catch (Exception ex2)
{
Tools.LogException(ex2, "ESSServiceHost.Abort");
}
}
_ESSServiceHost = null;
Tools.LogInfo("ESService stopped.");
}
}
catch (Exception ex)
{
Tools.LogException(ex,"OnStop");
}
}
Today on Monday I have checked the svclog and the "attacks" with malformed request remained there but my service lived happily through it. So it seemed to be fixed. Moreover only:
Stopping ESService.
ESService stopped.
events were logged in my log file. No aborts etc. So I guess that putting the Close call on a separate thread fixed the problem but absolutely do not know why.

Exception being thrown multiple times

Basically I am ting to catch any exception off a block of code, and fire said code one.
try {
CODE
catch (Exception e)
{
DO THIS ONCE
}
finally
{
CODE
}
In Depth
So I have been creating a TCP/SOCKET Server. Which can work with multiple clients. And send/recite (I/O) Data. It works well, and has been for a long time now. But I have found in my console that it says this:
This is bad because if it thinks the user disconnected twice it can create many problems. The way I know if a user has disconnected is by sending data to them every 200ms. And if there is a error then print they disconnected remove them from the client list, and disconnect there stream/tcp.
static bool currentlyUsing;
private static void PingClient(Object o)
{
if (!currentlyUsing)
{
if (clientsConnected.Count != 0)
{
foreach (Client c in clientsConnected)
{
try
{
c.tcp.Client.Blocking = false;
c.tcp.Client.Send(new byte[1], 0, 0);
}
catch (Exception e)
{
currentlyUsing = true;
Console.WriteLine("[INFO] Client Dissconnected: IP:" + c.ip + " PORT:" + c.port.ToString() + " Reason:" + e.Message);
clientsConnected.Remove(c);
c.tcp.Close();
break;
}
finally
{
currentlyUsing = false;
}
GC.Collect();
}
}
}
Is there a way to make it so it catches it only once, or catches it multiple times but only fires the code I want once?
If I understand your question correctly: you want to try to run the code on each iteration of the foreach block, and always run the finally code for each iteration, but only run the catch code once?
If so:
Before the foreach block, define:
bool caught = false;
And then after:
catch (Exception e)
{
if (caught == false)
{
caught = true;
...
}
}
I was making multiple timers. So it overlapped.

Why does my download queue break when called faster after each other?

I am using the following script to download XML files from a external site, but when the function is called fast after each other (Fast switching of tables to show) the queue seems to slip up.
When the function is called in a normal manner it works just fine, but when the user starts to switch between tables at a faster pace, the data won't load. It does not give any exceptions besides on some rare occasions it will say that the queue is busy. All tough I can't seem to find what is causing this queue to slip.
public void PreObtainData(ref MonavisaRequestForm request, string dateAndTime, string fileDateAndTime)
{
if (!initialized)
initialize();
try
{
if (!request.webclient.IsBusy && requestQueue.Count == 0)
{
request.url = request.url.Replace("&", "%26");
request.url = request.url.Replace("+", "%2B");
Uri uri = new Uri(string.Format("http://localhost/login.php?username={0}&password={1}&request={2}", request.username, request.password, request.url));
request.webclient.DownloadFile(uri, #"Nioo Graph Data " + fileDateAndTime + ".xml");
}
else if (!request.webclient.IsBusy && requestQueue.Count > 0)
{
Uri uri = new Uri(string.Format("http://localhost/login.php?username={0}&password={1}&request={2}", requestQueue.Peek().username, requestQueue.Peek().password, requestQueue.Peek().url));
requestQueue.Peek().webclient.DownloadStringAsync(uri);
requestQueue.Dequeue();
}
else
{
requestQueue.Enqueue(request);
}
}
catch (System.Net.WebException ex)
{
//if (ex.Status != System.Net.WebExceptionStatus.ProtocolError)
{
throw ex;
}
}
}
Queues are not designed to be accessed from multiple threads, and any number of things can go wrong when you do so. You should use a ConcurrentQueue or a BlockingCollection (which uses a ConcurrentQueue), as it is specifically designed to be used from multiple threads.

IPC Cannot Find the File Specified

using IPC over local TCP to communicate from Client to a Server thread. The connection itself doesn't seem to be throwing any errors, but every time I try to make one of the associated calls, I get this message:
System.Runtime.Remoting.RemotingException: Could not connect to an IPC Port: The System cannot Find the file specified
What I am attempting to figure out is WHY. Because this WAS working correctly, until I transitioned the projects in question (yes, both) from .NET 3.5 to .NET 4.0.
Listen Code
private void ThreadListen()
{
_listenerThread = new Thread(Listen) {Name = "Listener Thread", Priority = ThreadPriority.AboveNormal};
_listenerThread.Start();
}
private void Listen()
{
_listener = new Listener(this);
LifetimeServices.LeaseTime = TimeSpan.FromDays(365);
IDictionary props = new Hashtable();
props["port"] = 63726;
props["name"] = "AGENT";
TcpChannel channel = new TcpChannel(props, null, null);
ChannelServices.RegisterChannel(channel, false);
RemotingServices.Marshal(_listener, "Agent");
Logger.WriteLog(new LogMessage(MethodBase.GetCurrentMethod().Name, "Now Listening for commands..."));
LogEvent("Now Listening for commands...");
}
Selected Client Code
private void InitializeAgent()
{
try
{
_agentController =
(IAgent)RemotingServices.Connect(typeof(IAgent), IPC_URL);
//Note: IPC_URL was originally "ipc://AGENT/AGENT"
// It has been changed to read "tcp://localhost:63726/Agent"
SetAgentPid();
}
catch (Exception ex)
{
HandleError("Unable to initialize the connected agent.", 3850244, ex);
}
}
//This is the method that throws the error
public override void LoadTimer()
{
// first check to see if we have already set the agent process id and set it if not
if (_agentPid < 0)
{
SetAgentPid();
}
try
{
TryStart();
var tries = 0;
while (tries < RUNCHECK_TRYCOUNT)
{
try
{
_agentController.ReloadSettings();//<---Error occurs here
return;
} catch (RemotingException)
{
Thread.Sleep(2000);
tries++;
if (tries == RUNCHECK_TRYCOUNT)
throw;
}
}
}
catch (Exception ex)
{
HandleError("Unable to reload the timer for the connected agent.", 3850243, ex);
}
}
If you need to see something I haven't shown, please ask, I'm pretty much flying blind here.
Edit: I think the issue is the IPC_URL String. It is currently set to "ipc://AGENT/AGENT". The thing is, I have no idea where that came from, why it worked before, or what might be stopping it from working now.
Update
I was able to get the IPC Calls working correctly by changing the IPC_URL String, but I still lack understanding of why what I did worked. Or rather, why the original code stopped working and I needed to change it in the first place.
The string I am using now is "tcp://localhost:63726/Agent"
Can anyone tell me, not why the new string works, I know that...but Why did the original string work before and why did updating the project target to .NET 4.0 break it?

Categories