Killing Excel.EXE on server [duplicate] - asp.net

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to properly clean up Excel interop objects in C#
Suppose a ASP.NET web application generates automated Excel Reports on the server. How do we kill a server-side Excel.EXE once the processing is over. I am raising this purposely, because I believe that the Garbage Collecter does not clean the Excel executable even after the Excel file is closed.
Any pointers would be helpful?

Sorry to say this, and I'm not trying to be smart, but... don't put office on the server!!!
That's if I've understood correctly! :)
EDIT: Even though I've been marked down for this, I will never ever advocate running Office on the server - it has proven way too much of a pain in the ass for me in the past.
Having said that, the same now goes for me and Crystal Reports ;-)

I agree with not running Office on a server. Not that I have any choice in the matter :)
One thing to keep in mind with the taskkill option, is that unless you specifically plan for it (aka - singleton), you may have multiple copies of Excel (or any other Office app) running, and unintentionally close the wrong instance.
Also note that per http://support.microsoft.com/kb/257757
Microsoft does not currently
recommend, and does not support,
Automation of Microsoft Office
applications from any unattended,
non-interactive client application or
component (including ASP, ASP.NET,
DCOM, and NT Services), because Office
may exhibit unstable behavior and/or
deadlock when Office is run in this
environment.
As an alternative, there is a product called Aspose Cells that offers a product that is designed to allow you to programmatically work with an Excel sheet in a server environment. As a disclaimer, I have never personally used this product, but I have heard about it from several people I worked with in the past.

I've had more time to think about this answer, and would now recommend using an XML approach with the Open XML Office spreadsheet format.
Heres some good links to get started on building a office document with code.
http://msdn.microsoft.com/en-us/magazine/cc163478.aspx
http://msdn.microsoft.com/en-us/library/bb735940(office.12).aspx
Just use SSIS on SQL Server. It provides the ability to export to Excel.
Don't run office on the server. Alteranatively waste money on aspose or spreadsheetgear.
GC does work your just not using it properly follow this pattern...
private void killExcel()
{
xlApp.Quit();
Marshal.ReleaseCOMObject(xlApp);
if(xlApp != null)
{
xlApp = null;
}
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
get your Excel operational class to implement IDisposable, and then stick killExcel() in the Dispose method.
UPDATE: Also note that sometimes dev will still see Excel.exe running in task manager. Before assuming the above code isn't working, check that the process that is running the code is also closed. In the case of a VSTO or COM addin, check that Word/powerpoint/other excel instance is also closed as there is still a GC root back to the launching process. Once that is closed the Excel.exe process will close.

Are you using VSTO? You can close the Excel app after you finished with excelobject.Quit(); It worked for me, but I don't use Excel on server-side anymore.
You can have a look on Excel's XML schema to build the Excel file without Excel itself. Check out CarlosAg Excel Writer, which does exactly the same.

I've had a similar problem. While 'taskkill excel.exe' or enumerating all "excel" processes and killing them does work, this kills ALL running Excel processes. You're better off killing only the instance you're currently working with.
Here's the code I used to accomplish that. It uses a PInvoke (see here) to get the ProcessID from the Excel.Application instance (Me.ExcelInstance in the example below).
Dim ExcelPID As Integer
GetWindowThreadProcessId(New IntPtr(Me.ExcelInstance.Hwnd), ExcelPID)
If ExcelPID > 0 Then
Dim ExcelProc As Process = Process.GetProcessById(ExcelPID)
If ExcelProc IsNot Nothing Then ExcelProc.Kill()
End If
Please not this might not work on all platforms because of the PInvoke... To date, this is the only method I have found to be reliable. I have also tried to find the correct PID by enumarating all Excel processes and comparing the Process.MainModule.BaseAddress to the Excel.Application.Hinstance.
'DO NOT USE THIS METHOD, for demonstration only
For Each p as Process in ExcelProcesses
Dim BaseAddr As Integer = p.MainModule.BaseAddress.ToInt32()
If BaseAddr = Me.ExcelInstance.Hinstance Then
p.Kill()
Exit For
End If
Next
This is not a reliable way to find the correct process, as the BaseAddress sometimes seems to be the same for several processes (resulting in killing the wrong PID).

The command you need is "taskkill".
http://technet.microsoft.com/en-us/library/bb491009.aspx
> taskkill excel.exe

:). I jotted down my skirmish with Excel here. It also has some links that I found after some heavy searching. Hope it helps.
Basically Excel is a pain even though it can be automated.

I also would not recommend using office apps on a server except for data access to mdb files.
I can definitely understand that there are times where it is necessary. In thoses cases
I would recommend the following:
Create a separate server where that is the only function. (Let's you reboot with minimum impact).
Have the server implement a mechanism of queuing requests
Keep a single thread processing the queue. This gives you the ability to keep track of the office app, kill it if necessary, and continue on without impacting any queued up jobs or other applications.
If you absolutely need to do it on the same server, then at least implement the above in it's own app pool.
Limiting yourself keeping a queue of work and only one instance of Excel (or any other office app) let's you kill it with abandon with TaskKill or .Kill() and not lose work.
I believe if you keep it to a single thread then you would rarely have a need to kill it.

I have used spreadsheetgear to generate XL reports on the server and it works really well. We don't have to worry about the EXCEL process..

I had a similar problem and used the following code:
System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcesses();
for (int i = 0; i < procs.Length; i++)
{
if(procs[i].ProcessName == "EXCEL")
{
procs[i].Kill();
}
}
This worked pretty well, but I would really think about working with Office on a server.

I actually had a question that was similar to this awhile back - Check for hung Office process when using Office Automation - some of the responses to that question might be useful for you.
Also, I have to agree with what everyone else is saying in regards to keeping any Office products off of a server; however, since you are doing Excel, it might be feasible for you to generate Excel XML documents. You can do this without having to do any Office automation and the process is fairly straightforward. For simple grid based spreadsheets I have found it to be a bit easier than trying to automate it using Excel. The Office Open XML is quite powerful and allows for more complex reports are possible as well some more effort.

You need safely dispose all COM interop objects after you end your work. By "all" I mean absolutely all: collections property values and so on. I've created stack object and pushed objects during their setup:
Stack<object> comObjectsToRelease = new Stack<object>();
...
Log("Creating VBProject object.");
VBProject vbProject = workbook.VBProject;
comObjectsToRelease.Push(vbProject);
...
finally
{
if(excel != null)
{
Log("Quiting Excel.");
excel.Quit();
excel = null;
}
while (comObjectsToRelease.Count > 0)
{
Log("Releasing {0} COM object.", comObjectsToRelease.GetType().Name);
Marshal.FinalReleaseComObject(comObjectsToRelease.Pop());
}
Log("Invoking garbage collection.");
GC.Collect();
}
If Excel is still there you have to kill it manually.

The best approach is to use a purpose built library such as the one from Aspose to generate the spreadsheets or populate templates. The next best approach is to use the xml formats for office if practical for your needs. A lightweight approach that is sometimes suitable is to create an HTML file with one table in it and name it with an .xls extension. Excel will happily read that, but it is very limited in what it can do.
Those are the options I've used (but not much). There's also a thing called Microsoft Office Sharepoint Server, but I've no idea how much it really lets you do.
That said, your problem is happening because when you invoke the regular Excel libraries, you're actually spinning up Excel completely independently of .Net and actually just working with a proxy library to talk to it. This is pretty much the same kind of thing you'd have with WCF and a service. You wouldn't expect the service to die just because the client application was done using it. Worse, Excel is an unmanaged resource and will not be disposed/ finalized/ garbage collected at all. The .Net Runtime doesn't know about Excel, it just knows about those proxies. Application.quit is what you need and also you may need to explicitly release the com objects that are created.

Related

Using ASP.NET with Excel Files

I want to create an excel file from a web page in ASP.NET, I'm using the Microsoft.Office.Interop.Excel library, but I can't delete the file create on the side of the server, because I get in trouble with the times. I want to know if there is a way to create an Excel file with data of a database for example, and then that the client can download that file without saturate the server.
Actually I'm using this code in VB after the file is create in the server, but it doesn't work like I want:
Dim p As Process = New Process()
p.StartInfo = New ProcessStartInfo()
p.StartInfo.UseShellExecute = True
p.StartInfo.FileName = "taskkill.exe"
p.StartInfo.Arguments = "/F /IM EXCEL.exe"
p.Start()
p.WaitForExit()
Thread.Sleep(24)
Response.ContentType = "application/vnd.ms-excel"
Response.AppendHeader("Content-Disposition", "attachment; filename=Reporte.xlsx")
Response.TransmitFile(Server.MapPath("~/Resources/" & HiddenField1.Value.ToString() & ".xlsx"))
'Thread.Sleep(10000)
'Dim fso, f2
'fso = CreateObject("Scripting.FileSystemObject")
'f2 = fso.GetFile(Server.MapPath("~/Resources/" & HiddenField1.Value.ToString() & ".xlsx"))
'f2.Delete()
'Thread.Sleep(24)
Response.End()
Thanks
You might consider using something other than Excel to create the file you want to send.
For example, there is Carlos Aguilar Mares's free Excel XML library: http://www.carlosag.net/tools/excelxmlwriter/.
Also, some reporting tools like SQL Server Reporting Services have the option of exporting native Excel files. That's one way to generate an Excel file, complete with formatting.
Finally, Excel can read other types of files, such as CSV and HTML files. Those are a lot easier to produce and don't require Excel itself to generate.
A good run-through of the options: Generating an Excel file in ASP.NET
Automating excel on a server is not officially supported by Microsoft and I suggest you find an alternative. Microsoft has a lot of good information on this topic here: http://support.microsoft.com/kb/257757.
All current versions of Microsoft Office were designed, tested, and
configured to run as end-user products on a client workstation. They
assume an interactive desktop and user profile. They do not provide
the level of reentrancy or security that is necessary to meet the
needs of server-side components that are designed to run unattended.
Microsoft does not currently recommend, and does not support,
Automation of Microsoft Office applications from any unattended,
non-interactive client application or component (including ASP,
ASP.NET, DCOM, and NT Services), because Office may exhibit unstable
behavior and/or deadlock when Office is run in this environment.
If you are building a solution that runs in a server-side context, you
should try to use components that have been made safe for unattended
execution. Or, you should try to find alternatives that allow at least
part of the code to run client-side. If you use an Office application
from a server-side solution, the application will lack many of the
necessary capabilities to run successfully. Additionally, you will be
taking risks with the stability of your overall solution.
They even offer suggestions on how to avoid using them:
Most server-side Automation tasks involve document creation or
editing. Office 2007 supports new Open XML file formats that let
developers create, edit, read, and transform file content on the
server side. These file formats use the System.IO.Package.IO namespace
in the Microsoft .NET 3.x Framework to edit Office files without using
the Office client applications themselves. This is the recommended and
supported method for handling changes to Office files from a service.
They also have tutorials on using the APIs:
http://msdn.microsoft.com/en-us/library/bb739834.aspx
http://msdn.microsoft.com/en-us/library/bb727373.aspx
We use EPPlus to generate our excel documents.
It gives you quite a bit of control over the output and it's very fast. As John said, automating Excel on the server through a web site is completely unsupported by MS and they suggest you use alternatives.

Editing an Excel document with Macros in ASP.net

Is it possible in any way to edit an excel sheet through an ASP.net page that contains macro. I have tried to open the Excel sheet and it seems to just hang rather than load the excel. Testing on a page without macros works perfectly fine?
Disclaimer: I don't know the Excel license agreement and I don't know if utilizing Excel in a server process violates it or not. This is purely a technical description of how to get it working. The reader is advised to check the license agreement to see if it's allowed to do so or not. Different Office versions may have different license agreements. I used this method at several Fortune 100/500 companies and they didn't seem to care. Go figure.
This solution works but it has some limitations and require a fair amount of control over the server where it runs. The server also needs to have lots of memory.
To start, make sure that you perform a complete installation of every single Office feature on the server so that Excel won't try to install something if you attempt to use a feature that's not present.
You also need to create a dedicated user account on the server that has the right privileges. I can't tell you what exactly because in my case we controlled the server and we gave admin rights to this user.
When you have the user account, you need to log in as that user and run Excel (preferably all Office applications) at least once so that it can create its settings.
You also need to configure Excel to run under this user account when it's created as a COM object. For this, you need to go into DCOM Config on the server and configure Launch and Activation Permissions for the Excel.Application object to use your new user account. I'm not sure if I remember correctly, but I think after this step, running Excel as an interactive user was slightly problematic.
By default, Office applications try to display various messages on the screen: warnings, questions, etc. These must be turned off because when you utilize an Office application from a web application, it runs on the server so a human user won't be around to dismiss these messages - the Office program will just sit around indefinitely, waiting for the message to be dismissed.
You need to set (at the minimum) these properties:
DisplayAlerts = false
AskToUpdateLinks = false
AlertBeforeOverwriting = false
Interactive = false
Visible = false
FeatureInstall = 0 'msoFeatureInstallNone
to disable UI messages from Excel. If you use Excel 2010, there may be more, but I'm not familiar with that.
If you have Excel files with macros in them, you may have to disable macro security in Excel - that can't be done programmatically, for obvious reasons.
To access Excel services, implement a manager object that will actually hold the Excel reference - don't try to hold the Excel.Application object in the page because your page code will become very complicated and you may not be able to properly clean things up.
The object that holds the Excel reference may be a separate DLL or an out-of-process server. You must make sure, however, that when you acquire an instance of Excel on a given thread you always create a new Excel instance. The default is that an already running Excel instance will also serve other requests but that won't work for you because the same Excel instance cannot be shared among multiple threads. Each request-processing thread in IIS must have its own Excel instance - if you share instances, you'll have all kinds of problems. This means that your server will need to have quite a bit of memory to have many instances of Excel running. This was not an issue for me becasue we controlled the server.
If you can, try to create an out-of-proc (.exe) COM server because this way you can hold the Excel reference in a separate process. It's possible to get it working using an in-proc (.dll) COM object but it'll be more risky to your application pool - if Excel crashes, it'll crash your app pool as well.
When you have an .exe server, you can pass parameters in several possible ways:
Make your manager objet a COM object and pass parameters as properties.
Pass parameters as command-line parameteres to the .exe as it starts up.
Pass parameters in a text/binary file; pass the name of the file on the command-line.
I used all these and found the COM object option the cleanest.
In your manager object, follow these guidelines:
Wrap every single function that uses Excel in a try..catch block to capture any possible exception.
Always explicitly release all Excel objects by calling Marshal.ReleaseComObject() and then setting your variables to null as soon as you don't need them. Always release these objects in a finally block to make sure that a failed Excel method call won't result in a dangling COM object.
If you try to use any formatting features in Excel (page header, margins, etc.) you must have a printer installed and accessible to the user account that you use to run Excel. If you don't have an active printer (preferably attached to the server), formatting-related features may not work.
When an error happens, close the Excel instance that you're using. It's not likely that you can recover from Excel-related errors and the longer you keep the instance, the longer it uses resources.
When you quit Excel, make sure that you guard that code against recursive calls - if your exception handlers try to shut down Excel while your code is already in the process of shutting down Excel, you'll end up with a dead Excel instance.
Call GC.Collect() and GC.WaitForPendingFinalizers() right after calling the Application.Quit() method to make sure that the .NET Framework releases all Excel COM objects immediately.
Edit: John Saunders may have a point regarding the license agreement - I can't advise about that. The projects that I did using Word/Excel were all intranet applications at large clients and the requirement to use Word/Excel was a given.
The link he provided also has some tools that may be useful, although those libraries won't have full Excel functionality and if that's what you need, you don't have a choice. If you don't need full Excel functionality, check out those libraries - they may be much simpler to use.
A few links that may be useful to people trying to implement this approach:
StackOverflow question
Possible alternate products
COM server activation and window stations
The story changed a little while ago, with HPC Services for Excel.
With that, you can do Office Automation on a web server. I'm still trying to determine how it fits my situation, but you may want to check it out.

Keeping data in memory and persisting on Application_Disposed

I'm building a website (for personal use, low load) and instead of using an Access or MySQL database for data storage I'm thinking of having one XML file that I load and parse on Application_Start and then keep in memory (in static objects). The website then do reads and writes against these in-memory objects and I will finally persist all data to the XML file on Application_Disposed.
I'm aware that I'll need to make reading/writing thread-safe, but besides that, does anyone see any problem using this approach?
Yes, I see a big problem: There are a number of reasons to why the whole application might die without you knowing about it, and without your data being saved to that xml file.
You'll find Application_Dispose can get fired multiple times (so might don't be the best place to dispose your DI containers etc) whereas Application_End will only fires once (you can prove this by adding logging)
https://bytes.com/topic/asp-net/answers/561768-event-sequence
https://learn.microsoft.com/en-us/previous-versions/ms178473(v=vs.140)?redirectedfrom=MSDN
It seems VS2019 IIS Express doesn't seem to call Application_End as it should when you stop debugging, But IIS will.

Development shell in ASP.NET

I write a lot of code, most of it I throw away eventually when I am done with it; recently I was thinking that if I just kept every small piece of utility script I wrote, named it, tagged it and filed it in a dev shell, I will never loose the code, and on top of that I won't need to redo something I have done already, which is the main motivation, as I keep finding myself writing something I've done earlier.
Is there a ASP.NET shell style environment anywhere?
If not, what would be the best way to go about this?
I am looking to be able to do the following:
Write big or small bits of code.
Derive from or chain together alread written code/libraries/services.
Ability to have everything on my desktop (would that mean IIS on the desktop? or is there an lighter weight mechanism?), sync'ed with the server at home, so if I am on the move I can still access this and make this part of my day-to-day workflow.
You could build a unique solution, with many class library projects inside. Each project would address a specific scenario, something like this:
MyStuff (Solution)
MyStuff.Common
MyStuff.Validation
MyStuff.Web
MyStuff.Encryption
etc.
Then you can put this solution on an online versioning service like bitbucket or assembla, so you can access your source code from anywhere, edit it and commit it back to the server. This way you get the advantages of versioning and you store your code on a remote server so even if your harddisk breaks it's not a problem, cause what's on the server is what matters.
You should either look into a source control system (Git perhaps?) or into a file storage / syncing / sharing service like DropBox.
DropBox would allow you to access code snippets from wherever you are and works really easily (just drop a file into a folder).
If you need versioning and branching you're going to have to look into a source control system. Since you have a server at home, that should be no problem.

What would be a good approach to use Thread or Thead Pool in ASP.NET

I am developing a component to create bespoke BulkImport functionality in ASP.NET. Underline this component will be using SqlBulkCopy class. There will be different file formats. The file is imported into a intermidiate table and is then transformed to the required tables. The upload file can be big and might take couple of minutes for processing. I would like to use Thread or Thead Pool to do asynchronous processing. Can you please suggest a good approach to handle this problem.
note: This is an internal application which would be used by max 2-5 person at any given time.
The main problem with firing up additional threads in ASP.NET is that the framework can rip the AppDomain out from under you (for example, if someone edits the web.config or IIS decides to recycle the worker process). If that happens, your worker thread is also terminated and you can't really control it.
If you don't think that'll be a problem, then it doesn't really matter, but I would suggest that a better solution would probably be to fire up the work in a separate process that you can then monitor from your web application.
That way, if someone edits the web config, or IIS recycles the worker process, the import process is running independently and you don't have to worry.
Here is my approach:
Ask the user to paste in the unc path to the file. Save this path into a table in sql.
Write a windows service to check for new entries in the path table. When finding a new entry, start processing the file. Update the tabel periodicly with the progress and check flags (below)
Have an ajax callback in the browser that checks the table for progress, returning as a percentage to the client. Allow the client to stop the process by adding some flags to the table.

Resources