关于C#中使用NPOI组件读写Excel文件的说明

使用NPOI可以在没有安装Office或者相应环境的机器上对Excel文档进行读写。其下载地址为:http://npoi.codeplex.com/releases

一、读取Excel文件:

// 定义要读取的Excel文件地址
string excel = @"C:\test.xls";
// 定义要写入的数据表
DataTable dataTable = new DataTable();
// 创建Excel文件流
FileStream fileStream = new FileStream(excel, FileMode.Open, FileAccess.Read);
// 通过Excel文件流创建NPOI的Excel操作对象
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(fileStream);
// 关闭Excel文件流
fileStream.Close();
// 获取Excel工作表
ISheet sheet = hssfWorkbook.GetSheetAt(0);
// 获取Excel工作表的行枚举对象
IEnumerator rows = sheet.GetRowEnumerator();
// 是否存在第一行
if (rows.MoveNext())
{
    // 获取第一行
    HSSFRow hssfRow = (HSSFRow)rows.Current;
    // 遍历行数据设置数据表列名
    for (int j = 0, length = hssfRow.LastCellNum; j < length; j++)
    {
        // 获取单元格
        ICell cell = hssfRow.GetCell(j);
        if (cell != null)
        {
            // 设置数据表列名
            dataTable.Columns.Add(cell.ToString());
        }
    }
    // 循环获取行数据
    while (rows.MoveNext())
    { 
        // 获取当前行
        hssfRow = (HSSFRow)rows.Current;
        // 创建数据表行
        DataRow dataRow = dataTable.NewRow();
        // 遍历行数据设置数据表行数据
        for (int i = 0, length = hssfRow.LastCellNum; i < length; i++)
        {
            // 获取单元格
            ICell cell = hssfRow.GetCell(i);
            if (cell != null)
            {
                // 设置数据表行数据
                dataRow[i] = cell.ToString();
            }
        }
        // 添加数据行到数据表中
        dataTable.Rows.Add(dataRow);
    }
}
// 关闭NPOI的Excel操作对象
hssfWorkbook.Close();

二、写入Excel文件:

// 定义要读取的数据表
DataTable dataTable = new DataTable();
// 定义要写入的Excel文件地址
string excel = @"C:\test.xls";
// 创建NPOI的Excel操作对象
HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
// 根据数据表名创建Excel工作表
ISheet sheet = hssfWorkbook.CreateSheet(dataTable.TableName);
// 创建工作表第一行
IRow row = sheet.CreateRow(0);
// 遍历数据表列名写入工作表第一行
for (int i = 0, length = dataTable.Columns.Count; i < length; i++)
{
    // 创建工作表单元格并写入列名
    row.CreateCell(i).SetCellValue(dataTable.Columns[i].ColumnName);
}
// 遍历数据表行数据写入工作表
for (int i = 0, iLength = dataTable.Rows.Count; i < iLength; i++)
{
    //创建工作表数据行
    IRow row2 = sheet.CreateRow(i + 1);
    // 遍历行数据写入工作表
    for (int j = 0, jLength = dataTable.Columns.Count; j < jLength; j++)
    {
        // 获取数据
        object value = dataTable.Rows[i][j];
        // 创建单元格
        ICell cell = row2.CreateCell(j);
        // 设置单元格数据
        if (value == null)
        {
            cell.SetCellValue("");
        }
        else
        {
            cell.SetCellValue(value.ToString());
        }
    }
}
// 创建Excel文件流
FileStream fileStream = new FileStream(excel, FileMode.Create, FileAccess.Write);
// 将Excel数据写入文件流
hssfWorkbook.Write(fileStream);
// 将文件流数据写入到文件中
fileStream.Flush();
// 关闭Excel文件流
fileStream.Close();
// 关闭NPOI的Excel操作对象
hssfWorkbook.Close();

更多信息请参阅:NPOI – Home

关于C#中调用Excel程序后强制关闭其进程的说明

在C#中使用Microsoft.Office.Interop.Excel.dll操作Excel文件时,有时创建的Application无法正常退出,因此我们就需要通过结束其进程的方式强制将Excel的Application退出:

一、引入Windows API获取进程Id函数:

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int id);

二、强制结束Excel的Application进程:

// 创建Excel进程
Microsoft.Office.Interop.Excel.Application app = new Application();
try
{
    // 正常退出Excel进程
    app.Quit();
}
catch
{
    // 如Excel进程退出失败,则强制结束其进程
    // 获取当前Excel进程的句柄
    IntPtr intPtr = new IntPtr(app.Hwnd);
    // 获取当前Excel进程的进程Id
    int processId = 0;
    GetWindowThreadProcessId(intPtr, out processId);
    // 获取当前Excel进程
    Process process = Process.GetProcessById(processId);
    if (process != null)
    {
        // 强制结束Excel进程
        process.Kill();
    }
}

关于C#中使用SQLite自适应Any CPU程序的说明

在C#中如果要使程序自适应32位和64位系统,只需要将项目的“目标平台”设置为“Any CPU”就行了,但是如果程序中使用了SQLite组件,则需要对该组件额外进行一些简单的设置:

一、下载System.Data.SQLite组件:
从官网(http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki)中下载对应.NET版本的System.Data.SQLite二进制包,由于要自适应32位和64位系统,我们需要分别下载32位和64位的非静态连接二进制包:
csharp-sqlite-any-cpu-1

二、引用System.Data.SQLite.dll:
在C#程序中引用组件“System.Data.SQLite.dll”,比对下载的这两个二进制包,我们会发现除了SQLite.Interop组件外,其他文件是一样的,所以我们可以自由选择32位或64位的System.Data.SQLite.dll文件:
csharp-sqlite-any-cpu-2

三、引用SQLite.Interop.dll:
在程序生成目录里新建x86和x64两个子文件夹,分别将下载的对应平台的SQLite.Interop.dll复制到x86和x64文件夹中,由System.Data.SQLite.dll根据系统类型自动调用。为了方便管理,我们可以在项目中创建这两个文件夹,并设置SQLite.Interop.dll文件的属性为“如果较新则复制”,生成操作为“无”,从而使程序在生成的时候自动复制SQLite.Interop.dll文件:
csharp-sqlite-any-cpu-3

四、编译生成程序:
完成以上步骤后对程序进行编译生成,程序中使用的SQLite组件就能自适应32位和64位系统了。

关于C#中反射调用泛型方法的说明

在C#中通过反射的方式调用定义为泛型的方法时,我们需要先根据方法名称获取方法描写对象“MethodInfo”,然后通过
该对象的“MakeGenericMethod”方法构造出对应实际泛型类型的泛型方法,从而才能被我们使用:

// 定义的泛型方法结构
public void Test<T>();

// 获取当前类型
Type type = this.GetType();
// 获取Test的方法对象
MethodInfo methodInfo = type.GetMethod("Test");
// 定义泛型类型
Type genericType = typeof(int);
// 获取对应泛型类型的泛型方法
methodInfo = methodInfo.MakeGenericMethod(genericType);
// 反射调用泛型方法
methodInfo.Invoke(this, null);

关于C#中获取可空泛型类型Nullable中的基础类型的说明

在C#中可以使用在值类型后加问号(如“int?”)的方式定义可空的类型,该定义出来的类型实际是一个“Nullable<T>”的泛型类型。但是在对该类型数据进行强制转换等操作时会因为数据类型不匹配等原因无法转换,这个时候我们就需要取出该可空类型的实际基础类型:

// 定义可空泛型值类型
Type type = typeof(int?);
// 判断目标type是否是可空的泛型类型
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    // 取出可空泛型类型中的基础类型
    type = Nullable.GetUnderlyingType(type);
}
// 返回基础值类型:int
return type;