只能在Excel中实际打开时读取Excel文件

时间:2016-01-19 17:59:05

标签: c# excel

我使用以下代码打开excel文件(XLS)并使用第一个工作表填充DataTable

var connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; data source={0}; Extended Properties=Excel 8.0;", filename);

OleDbConnection connExcel = new OleDbConnection(connectionString);
connExcel.Open();
DataTable dtExcelSchema;
dtExcelSchema = connExcel.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string SheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
connExcel.Close();

var adapter = new OleDbDataAdapter("SELECT * FROM [" + SheetName + "]", connectionString);
var ds = new DataSet();
int count = 0;

adapter.Fill(ds, SheetName);

DataTable dt = ds.Tables[0];

仅当文件已在Ms Excel中打开时才有效。为什么会这样?

如果文件未打开,我会收到一条错误消息(在connExcel.Open行上):External table is not in the expected format.

3 个答案:

答案 0 :(得分:1)

我面临同样的问题,因此对于这个网站来说,很多开发人员都在努力:

- When I try read Excel with OLE DB all values are empty

- Can't connect to excel file unless file is already open

其实我正在使用经典的连接字符串(注意我正在尝试读取97/2003文件):

Provider=Microsoft.Jet.OLEDB.4.0; Data Source = " + GetFilename(filename) + "; Extended Properties ='Excel 8.0;HDR=NO;IMEX=1'

但只有在以下情况下才能正确读取文件:

  • 即使在Word中也在Excel 中打开! (该文件当然显示已损坏且无法读取,但OleDb程序可以读取文件的每一行),我没有尝试使用其他Office应用程序

  • 该文件未处于只读模式

我还试图手动锁定文件或用其他非办公应用程序打开它,但结果却不一样。如果我遵循前两个规则(在Word或Excel中以非只读模式打开的文件),我可以看到所有单元格,否则看起来第一个被完全忽略(因此F2成为F1, F3变为F2,...和F6,最后一个,应该变为F5,否则它会抛出并且超出索引错误。)

为了在不使用第三方库的情况下保持与OleDb的兼容性,我发现了使用Microsoft.Office.Interop.Excel程序集的非常愚蠢的解决方法。

Excel.Application _app = new Excel.Application();
var workbooks = _app.Workbooks;
workbooks.Open(_filename);

// OleDb Connection
using (OleDbConnection conn = new OleDbConnection(connectionOleDb))
            {
                try
                {
                    conn.Open();
                    OleDbCommand cmd = new OleDbCommand();
                    cmd.Connection = conn;

                    cmd.CommandText = String.Format("SELECT * FROM [{0}$]", tableName);
OleDbDataReader myReader = cmd.ExecuteReader();

                    int i = 0;
                    while (myReader.Read())
                    {
                        //Here I read through all Excel rows
                    }


                }
                catch (Exception E)
                {
                    MessageBox.Show("Error!\n" + E.Message);
                }
                finally
                {
                    conn.Close();

                    workbooks.Close();
                    if (workbooks != null)
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
                    _app.Quit();
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(_app);
                }
            }

基本上前3行运行的Excel实例完全可以持续OleDb执行任务所需的时间。 最后一个块内的最后4行,让Excel实例在任务后立即正确关闭,避免重影Excel进程。

我重复这是一个非常愚蠢的解决方法,还需要将1.5 MB dll(Microsoft.Office.Interop.Excel.dll)添加到项目中。 无论如何,OleDb无法自行管理丢失的数据......

答案 1 :(得分:1)

我遇到了同样的问题。如果文件是打开的,那么读取是正常的,但如果文件被关闭......有些事情很奇怪......在我的情况下,我收到了来自列和值的奇怪数据。调试我找到了第一张表的名称并且很奇怪[“xls _xlnm#_FilterDatabase”]在互联网上查找我发现这是一个隐藏表单的名称和一个避免阅读此表(HERE)的技巧,所以我实现了一个方法:

private string getFirstVisibileSheet(DataTable dtSheet, int index = 0)
{
    string sheetName = String.Empty;
    if (dtSheet.Rows.Count >= (index + 1))
    {
        sheetName = dtSheet.Rows[index]["TABLE_NAME"].ToString();
        if (sheetName.Contains("FilterDatabase"))
        {
            return getFirstVisibileSheet(dtSheet, ++index);
        }
    }
    return sheetName;
}

对我来说工作得非常好。

我的完整示例代码是:

string excelFilePath = String.Empty;
string stringConnection = String.Empty;

using (OpenFileDialog openExcelDialog = new OpenFileDialog())
{
    openExcelDialog.Filter = "Excel 2007 (*.xlsx)|*.xlsx|Excel 2003 (*.xls)|*.xls";
    openExcelDialog.FilterIndex = 1;
    openExcelDialog.RestoreDirectory = true;
    DialogResult windowsResult = openExcelDialog.ShowDialog();
    if (windowsResult != System.Windows.Forms.DialogResult.OK)
    {
        return;
    }

    excelFilePath = openExcelDialog.FileName;
    using (DataTable dt = new DataTable())
    {
        try
        {
            if (!excelFilePath.Equals(String.Empty))
            {
                stringConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + excelFilePath + ";Extended Properties='Excel 8.0; HDR=YES;';";
                using (OleDbConnection conn = new OleDbConnection(stringConnection))
                {
                    conn.Open();
                    OleDbCommand cmd = new OleDbCommand();
                    cmd.Connection = conn;

                    DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

                    string sheetName = getFirstVisibileSheet(dtSheet);

                    cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
                    dt.TableName = sheetName;

                    OleDbDataAdapter da = new OleDbDataAdapter(cmd);
                    da.Fill(dt);

                    cmd = null;
                    conn.Close();
                }
            }

            //Read and Use my DT
            foreach (DataRow row in dt.Rows)
            {
                //On my case I need data on first and second Columns
                if ((row.ItemArray.Count() < 2) ||
                    (row[0] == null || String.IsNullOrWhiteSpace(row[0].ToString()))
                    ||
                    (row[1] == null ||String.IsNullOrWhiteSpace(row[1].ToString())))
                {
                    continue;
                }

                //Get the number from the first COL
                int colOneNumber = 0;
                Int32.TryParse(row[0].ToString(), out colOneNumber);

                //Get the string from the second COL
                string colTwoString = row[1].ToString();

                //Get the string from third COL if is a file path valid
                string colThree = (row.ItemArray.Count() >= 3 
                                && !row.IsNull(2) 
                                && !String.IsNullOrWhiteSpace(row[2].ToString()) 
                                && File.Exists(row[2].ToString())
                                ) ? row[2].ToString() : String.Empty;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Import error.\n" + ex.Message, "::ERROR::", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

private string getFirstVisibileSheet(DataTable dtSheet, int index = 0)
{
    string sheetName = String.Empty;
    if (dtSheet.Rows.Count >= (index + 1))
    {
        sheetName = dtSheet.Rows[index]["TABLE_NAME"].ToString();
        if (sheetName.Contains("FilterDatabase"))
        {
            return getFirstVisibileSheet(dtSheet, ++index);
        }
    }
    return sheetName;
}

答案 2 :(得分:0)

它是否在ToString()失败,就像在这里一样? Error is "Object reference not set to an instance of an object"

Convert.ToString()修复了什么吗?