Interop中不退出Excel应用程序

时间:2012-06-20 15:38:56

标签: c# asp.net excel impersonation excel-interop

我正在使用以下代码在ASP.net表单上动态写入excel文件,该表单可以将数据表保存为excel。

//Create Excel Object
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook workbook = excel.Workbooks.Open(target);
Microsoft.Office.Interop.Excel.Worksheet worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Worksheets[1];

worksheet.Name = "Worksheet1";
excel.Visible = false;
//Generate Fields Name
foreach (DataColumn col in dataTable.Columns)
{
    colIndex++;
    excel.Cells[1, colIndex] = col.ColumnName;
}
object[,] objData = new object[rowNo, columnNo];
for (int row = 0; row < rowNo; row++)
{
    for (int col = 0; col < columnNo; col++)
    {
        objData[row, col] = dataTable.Rows[row][col];
    }
}
Microsoft.Office.Interop.Excel.Range range;
range = worksheet.Range[excel.Cells[2, 1], excel.Cells[rowNo + 1, columnNo]];
range.Value2 = objData;
worksheet.Columns.AutoFit();

workbook.Save();
workbook.Close();

IntPtr hwnd = new IntPtr(excel.Hwnd);
IntPtr processId;
IntPtr foo = GetWindowThreadProcessId(hwnd, out processId);
Process proc = Process.GetProcessById(processId.ToInt32());
proc.Kill();

//excel.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();

将数据写入excel文件。但是,当我尝试使用上面的proc.Kill()来杀死进程时,我得到以下异常

描述:执行当前Web请求期间发生了未处理的异常。请查看堆栈跟踪,以获取有关错误及其在代码中的起源位置的更多信息。

异常详细信息:System.ComponentModel.Win32Exception:拒绝访问

来源错误:

在执行当前Web请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常的起源和位置的信息。

堆栈追踪:

[Win32Exception (0x80004005): Access is denied]
   System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited) +1985316
   System.Diagnostics.Process.Kill() +49
   Faculty_Report1.FetchData_EXCEL(String prog, String cls, String spcln, String fromdate, String todate) +2413
   Faculty_Report1.fetchselectedvalues_EXCEL() +1044
   System.Web.UI.WebControls.ImageButton.OnClick(ImageClickEventArgs e) +187
   System.Web.UI.WebControls.ImageButton.RaisePostBackEvent(String eventArgument) +165
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3707

我没有使用proc.Kill()方法,而是尝试使用excel.Quit()方法,但该过程不会终止。当我这样做时,每次用户尝试生成报告时,我都会使用新的Excel流程。

我在web.config文件中有用户模拟,但这不会改变任何内容。

  <system.web>
    <identity impersonate="true" userName="xxxxxx" password="xxxxxxxx" />
  </system.web>

关于如何避免异常并确保excel进程终止正常的任何指示?我已经尝试了我在这里找到的COM清理解决方案,但它不起作用..

3 个答案:

答案 0 :(得分:5)

MS不支持在服务器上使用Interop(如ASP.NET) - 请参阅http://support.microsoft.com/default.aspx?scid=kb;EN-US;q257757#kb2

由于Windows Vista MS引入了一些与安全相关的措施,这些措施阻止Windows服务执行“类似桌面”的操作......这意味着您必须绕过一些安全措施才能使其工作(不推荐!)。

要在服务器场景中处理Excel,有几个选项(免费和商业):

我可以推荐Aspose.CellsFlexcel ...没试过SpreadsheetGear但是听到+读了很多关于它的好东西......

免费选项(仅适用于较新的xlsx格式!)例如OpenXML 2 from MSEPPlus

答案 1 :(得分:1)

要创建简单的Excel数据表,除了互操作之外还有一些可能有趣的选项,但是有些方法可以在必须使用互操作时帮助excel。

首先是第一件事。如果您确定可以重用现有应用程序,可以使用'oldschool'getactiveobject来获取现有实例:

    using System.Runtime.InteropServices;

    Microsoft.Office.Interop.Excel.Application excel;
            try
            {
                excel = (Microsoft.Office.Interop.Excel)Marshal.GetActiveObject("Excel.Application");
            }
            catch
            {
                 excel = new Microsoft.Office.Interop.Excel.Application();
            }

要成功退出excel应用程序,必须销毁所有句柄,但我想这就是你清理COM的意思吗? 确保所有句柄都与marshal.releasecomobject一起发布(或许在现代互操作中不再需要它)可能是一种痛苦,这就是为什么我将每个办公室互操作'句柄'包括在工作表,工作簿,范围等中的原因。包装。 我所拥有的代码没有多大用处,但我会在这里发布它以提出一个大致的想法:

public class WrapperBase<T>: IWrapper
{

    protected T obj;
    protected internal WrapperBase(IWrapper Owner)
    {
        SetOwner(Owner);
    }

    protected WrapperBase() { }


    CultureSwitcher cultswitch;
    public CultureSwitcher CultureSwitcher
    {
        get
        {
            if (cultswitch == null)
            {
                if (owner != null) return owner.CultureSwitcher;
                cultswitch = new CultureSwitcher();
            }
            return cultswitch;
        }
    }

    public T Object { get { return obj; } internal set { obj = value; } }


    //public abstract void Save();

    public bool IsDisposed { get { return disposed; } }

    bool disposed;
    public void Dispose()
    {
        Dispose(false);
    }

    void Dispose(bool fromfinalize)
    {
        if (disposed) return;
        disposed = true;
        if (!fromfinalize)
            GC.SuppressFinalize(this);

        CultureSwitcher.Try(OnDisposing);

        if (obj != null)
        {
            while (Marshal.ReleaseComObject(obj) > 0) { }
            obj = default(T);
        }

        if (cultswitch != null)
        {
            cultswitch.Dispose();
            cultswitch = null;
        }

        SetOwner(null);
    }

    protected virtual void OnDisposing()
    {
        if (children != null)
        {
            foreach (var c in children)
                c.Dispose();
            children = null;
        }
    }

    ~WrapperBase()
    {
        Dispose(true);
    }

    List<IWrapper> children;
    IWrapper owner;

    public IWrapper Owner
    {
        get { return owner; }
    }

    protected void SetOwner(IWrapper Owner)
    {
        if (Owner == owner) return;
        if (owner != null)
        {
            owner.DeRegister(this);
        }
        owner = Owner;
        if (owner != null)
        {
            owner.Register(this);
        }
    }

    public void Register(IWrapper Child)
    {
        if (disposed)
            throw new ObjectDisposedException("Cannot register children after disposing");
        if (children == null)
            children = new List<IWrapper>();
        children.Add(Child);
    }

    public void DeRegister(IWrapper Child)
    {
        if (disposed) return;
        children.Remove(Child);
    }

    protected IEnumerable<IWrapper> GetChildren()
    {
        return children;
    }
}

答案 2 :(得分:0)

我在过去使用Interop使Excel应用程序真正关闭时也遇到了问题,但是发现解决方案是释放应用程序的实例。请尝试以下方法:

excel.Quit();
excel = null;

这听起来太简单了,但它成功地阻止了Excel的ghost实例在我的应用程序中停留在操作系统中。