通过 C# 创建 Excel 时出错:转换到 COM 上下文 0x56b098

Posted

技术标签:

【中文标题】通过 C# 创建 Excel 时出错:转换到 COM 上下文 0x56b098【英文标题】:Getting error creating Excel through C# : Transition into COM context 0x56b098 【发布时间】:2016-08-23 21:44:25 【问题描述】:

我正在尝试通过 C# 代码创建一个 Excel 文件,场景是我有一个返回 15000 条记录的存储过程,我正在通过 SqlDataAdapter 读取数据,然后将数据填充到 DataTable 然后我正在将数据填充到 excel 文件中,但一段时间后应用程序会抛出以下错误。

错误:

为此 RuntimeCallableWrapper 转换到 COM 上下文 0x56b098 失败,出现以下错误:系统调用失败。 (来自 HRESULT 的异常:0x80010100 (RPC_E_SYS_CALL_FAILED))。这通常是因为创建此 RuntimeCallableWrapper 的 COM 上下文 0x56b098 已断开连接,或者它正忙于做其他事情。从当前 COM 上下文(COM 上下文 0x56af28)释放接口。这可能会导致损坏或数据丢失。为避免此问题,请确保所有 COM 上下文/单元/线程保持活动状态并且可用于上下文转换,直到应用程序完全使用 RuntimeCallableWrapper 完成,该 RuntimeCallableWrapper 表示存在于其中的 COM 组件。

下面是我正在使用的代码

public DataTable getData(string query,string year,string month)

        SqlDataAdapter da = new SqlDataAdapter();
        DataTable dt = new DataTable();
        SqlCommand cmd = new SqlCommand(query,con);
        cmd.Parameters.AddWithValue("@YR", year);
        cmd.Parameters.AddWithValue("@MN", month);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandTimeout = 0;
        da.SelectCommand = cmd;
        da.Fill(dt);

        return dt;
    

主要方法

private void btn_MRRRetention_Click(object sender, EventArgs e)
    
        // Working for creating MRR Retention Excel File
        DataTable dt_Mrr;
        string Yr, mn;
        int tot_rows;



        Yr = Cmb_Yr.SelectedItem.ToString();
        mn = Cmb_Mnth.SelectedItem.ToString();

        if (xlApp == null)
        
            MessageBox.Show("Excel is not properly installed!");
            return;
        

        excel.Workbook xlWorkBook;
        excel.Worksheet xlWorkSheet;
        object misValue = System.Reflection.Missing.Value;

        xlWorkBook = xlApp.Workbooks.Add(misValue);
        xlWorkSheet = (excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

        // Data Reader Code start for collecting data from DB and pasting into Excel 
        pictureBox2.Visible = true;
        dt_Mrr = func.getData("sp_MRR_Retention_APAC", Yr, mn);
        //string text = "exec sp_Mrr_Retention" + "'" + Yr + "','" + mn + "'";
        tot_rows = dt_Mrr.Rows.Count;
        int row = 2;
        int i=0;

        xlWorkSheet.Cells[1, 1] = "MONTH";
        xlWorkSheet.Cells[1, 2] = "Parent Name";
        xlWorkSheet.Cells[1, 3] = "Customer_Name";
        xlWorkSheet.Cells[1, 4] = "Customer_Account_No";
        xlWorkSheet.Cells[1, 5] = "Item_Category";
        xlWorkSheet.Cells[1, 6] = "Item_Description_Summary";
        xlWorkSheet.Cells[1, 7] = "Item_Number";
        xlWorkSheet.Cells[1, 8] = "Date_Range";
        xlWorkSheet.Cells[1, 9] = "Activity_Type";
        xlWorkSheet.Cells[1, 10] = "Line_Type";
        xlWorkSheet.Cells[1, 11] = "IBX_Code";
        xlWorkSheet.Cells[1, 12] = "IBX_Country";
        xlWorkSheet.Cells[1, 13] = "IBX_Region";
        xlWorkSheet.Cells[1, 14] = "Primary_Sales_Rep";
        xlWorkSheet.Cells[1, 15] = "MRC_Amount_USD_Budget_Rate";
        xlWorkSheet.Cells[1, 16] = "Entered_Currency_Code";
        xlWorkSheet.Cells[1, 17] = "MRC_Amount_LC";
        xlWorkSheet.Cells[1, 18] = "UCM ID";
        xlWorkSheet.Cells[1, 19] = "GAM_TAG";
        xlWorkSheet.Cells[1, 20] = "Client Services Manager";
        xlWorkSheet.Cells[1, 21] = "Sales Program Type";
        xlWorkSheet.Cells[1, 22] = "SFDC Account Id";
        xlWorkSheet.Cells[1, 23] = "Account Owner";

        //rs = func.getReader("sp_MRR_Retention '" + Yr + "','" + mn + "'");

        while (tot_rows>i)
        
            xlWorkSheet.Cells[row, 1]   = dt_Mrr.Rows[i]["MONTH"];
            xlWorkSheet.Cells[row, 2]   = dt_Mrr.Rows[i]["Parent Name"];
            xlWorkSheet.Cells[row, 3]   = dt_Mrr.Rows[i]["Customer_Name"];
            xlWorkSheet.Cells[row, 4]   = dt_Mrr.Rows[i]["Customer_Account_No"];
            xlWorkSheet.Cells[row, 5]   = dt_Mrr.Rows[i]["Item_Category"];
            xlWorkSheet.Cells[row, 6]   = dt_Mrr.Rows[i]["Item_Description_Summary"];
            xlWorkSheet.Cells[row, 7]   = dt_Mrr.Rows[i]["Item_Number"];
            xlWorkSheet.Cells[row, 8]   = dt_Mrr.Rows[i]["Date_Range"];
            xlWorkSheet.Cells[row, 9]   = dt_Mrr.Rows[i]["Activity_Type"];
            xlWorkSheet.Cells[row, 10]  = dt_Mrr.Rows[i]["Line_Type"];
            xlWorkSheet.Cells[row, 11]  = dt_Mrr.Rows[i]["IBX_Code"];
            xlWorkSheet.Cells[row, 12]  = dt_Mrr.Rows[i]["IBX_Country"];
            xlWorkSheet.Cells[row, 13]  = dt_Mrr.Rows[i]["IBX_Region"];
            xlWorkSheet.Cells[row, 14]  = dt_Mrr.Rows[i]["Primary_Sales_Rep"];
            xlWorkSheet.Cells[row, 15]  = dt_Mrr.Rows[i]["MRC_Amount_USD_Budget_Rate"];
            xlWorkSheet.Cells[row, 16]  = dt_Mrr.Rows[i]["Entered_Currency_Code"];
            xlWorkSheet.Cells[row, 17]  = dt_Mrr.Rows[i]["MRC_Amount_LC"];
            xlWorkSheet.Cells[row, 18]  = dt_Mrr.Rows[i]["UCM ID"];
            xlWorkSheet.Cells[row, 19]  = dt_Mrr.Rows[i]["GAM_TAG"];
            xlWorkSheet.Cells[row, 20]  = dt_Mrr.Rows[i]["Client Services Manager"];
            xlWorkSheet.Cells[row, 21]  = dt_Mrr.Rows[i]["Sales Program Type"];
            xlWorkSheet.Cells[row, 22]  = dt_Mrr.Rows[i]["SFDC Account Id"];
            xlWorkSheet.Cells[row, 23]  = dt_Mrr.Rows[i]["Account Owner"];

            row++;
            i++;

            //For Checking purpose!
            //if (i == 1000) 
            // 
            //    break; 
            //
        

        // Data Reader Code Ends Here
        xlWorkBook.SaveAs("D:\\MRR_Retention_Auto.xls", excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        xlWorkBook.Close(true, misValue, misValue);
        xlApp.Quit();

        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        releaseObject(xlApp);

        pictureBox2.Visible = false;

        MessageBox.Show("Excel file created , you can find the file D:\\MRR_Retention_Auto.xls");

    

我在这个问题上需要帮助并期待它。

【问题讨论】:

应该是Cells[row, 1].Value = 异常是否为您提供了发生此异常的行号。 COM 异常可能有很多原因,但我建议您尝试的一件事是将您的 while 循环更改为“while (i @faljbour 不,它没有给我行号,如果我在第一次迭代中更改 while 循环,“i”已经小于“tot_rows”,它不会进入循环. 我在 i = 1000 的值时打破循环,然后它工作正常并且成功创建了 excel 文件但是当我尝试运行整个代码时出现错误。你能提出一些建议吗? @斯莱 ***.com/questions/2692979/… 【参考方案1】:

在我看来,完全删除数据表可能会提高效率。数据表很棒,但它们确实有开销,我想知道在数据表中填充 15,000 行的开销是否会影响您的 COM 通信。

这是一个避免使用数据表的解决方案。它假定存储过程将按照您希望在 Excel 中看到的相同顺序转储列。

首先,让您的 getData 方法返回 SqlCommand 对象而不是数据表:

public SqlCommand getData(string query, string year, string month)

    SqlCommand cmd = new SqlCommand(query, con);
    cmd.Parameters.AddWithValue("@YR", year);
    cmd.Parameters.AddWithValue("@MN", month);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandTimeout = 0;

    return cmd;

从这里开始,您的函数调用和输出到 Excel 可以简单地如下所示:

SqlCommand cmd = func.getData("sp_MRR_Retention_APAC", Yr, mn);

SqlDataReader reader = cmd.ExecuteReader();
for (int col = 0; col < reader.FieldCount; col++)
    xlWorkSheet.Cells[col + 1, 1].Value2 = reader.GetName(col);

while (reader.Read())

    for (int col = 0; col < reader.FieldCount; col++)
        if (!reader.IsDBNull(col))
            xlWorkSheet.Cells[row, col + 1] = reader.GetValue(col);
    row++;


reader.Close();

您甚至可以将其封装在一个方法中以执行 Excel 输入:

public void getData(string query, string year, string month, excel.Worksheet Ws)

    SqlCommand cmd = new SqlCommand(query, con);
    cmd.Parameters.AddWithValue("@YR", year);
    cmd.Parameters.AddWithValue("@MN", month);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandTimeout = 0;

    int row = 1;

    SqlDataReader reader = cmd.ExecuteReader();
    for (int col = 0; col < reader.FieldCount; col++)
        Ws.Cells[col + 1, 1].Value2 = reader.GetName(col);

    while (reader.Read())
    
        for (int col = 0; col < reader.FieldCount; col++)
            if (!reader.IsDBNull(col))
                Ws.Cells[row, col + 1] = reader.GetValue(col);
        row++;
    

    reader.Close();

这会将您的 main 方法中的所有代码简化为:

func.getData("sp_MRR_Retention_APAC", Yr, mn, xlWorkSheet);

这才是真正的关键...... Excel 中内置的 MS Query 实际上为您完成了所有这些工作。通常您使用 ODBC,但使用 MS SQL Server,您可以在不使用 ODBC 的情况下直接访问服务器——这是 Microsoft 与 Microsoft 共存的一个好处。老实说,我从来没有尝试过使用存储过程,但是使用查询它会很好地工作。我没有理由怀疑 MS Query 将与过程一起运行。

C# 中的 MS Query 调用如下所示:

excel.ListObject lo = sheet.ListObjects.AddEx(excel.XlListObjectSourceType.xlSrcQuery,
    connectionString, true, Excel.XlYesNoGuess.xlGuess, range);
lo.QueryTable.CommandText = queryText;
lo.Refresh();

你会发现它的速度非常快——我敢说它可以与你可以手写的任何东西相媲美。

【讨论】:

以上是关于通过 C# 创建 Excel 时出错:转换到 COM 上下文 0x56b098的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用 OleDbDataAdapter 读取 Excel 文件时出错

将新工作表添加到XLSM宏Excel文件时出错

使用 C# 代码打开 Excel 工作簿时出错

使用 C# 从 Excel 2007 批量上传 SQL 服务器时出错

将 char 数组发送到 DLL 调用“不知道如何转换参数”时出错

使用 Json.Net 进行 C# 枚举反序列化:将值转换为类型时出错