OLE实现EXCEL读取的动态链接库的具体步骤
1.环境搭建:
我们需要什么环境背景呢?我用的是VS2015 + Microsoft Excel 2015,这里强调一下OLE不能在WPS上进行,因为OLE需要调用微软的基础类库(MFC),所以我们需要用到Excel软件,如果您用的是WPS,请出门右转!
2.具体步骤:
2.1创建工程:
我们要实现一个可以实现excel读取的动态链接库,当需要读取EXCEL时,直接加载dll即可实现excel读取,看图!
选择MFC DLL,取名ExcelRead,点击确定。这样我们就在G盘生成了一个动态链接库的工程。
一路下一步,记得在自动化的前面打上勾,点击完成。
默认工程是debug和x86格式的,因为excel是64位的,所以我们将工程调整为x64,如果工程按照x86编译,会出现10处报错,例如:LPWSTR不能转换为char *类型,1.exe已退出等等,主要原因在于32位指令与64位指令的字符集是不一样的,32位指令用的是ASCII码指令字,64位指令的字符集是宽字符集,具体原因请百度
在项目名处右键,点击添加->类。
点击添加MFC中的typelib类中的MFC类,点击添加。
在可用的类型库下拉框选择Microsoft Excel 16.0 Object Library,之后将图上6个类添加进去,点击完成!
红色的框框里面是新生成的头文件,有6个头文件,分别定义了六个OLE的类。之后我们将绿色框里面的代码注释掉,红框里面的6个头文件都有这句代码,统统注释掉,否则,,,,呵呵,120个编译错误会等着你。。。
在stdafx.h里面添加绿色框框里面的代码。之后你会看见下图。
以CApplication.h为例,红色波浪线消失了有木有
,把#import "C:\\Program Files (x86)\\Microsoft Office\\Root\\Office16\\EXCEL.EXE" no_namespace那句注释掉之后界面如上图所示!
之后我们创建一个头文件以实现对OLE读取EXCEL类的定义。创建一个CExcelFileRead.h,代码如下:
#pragma once
// 用于OLE方式读取Excel文件
class CReadExcelFile //对一个excel文件的读取操作
{
private:
static CApplication excel_application; //EXCEL进程实例,只有一个
CWorkbooks excel_books; // workbook集合(多个文件时)
CWorkbook excel_work_book; // 当时使用的workbook
CWorksheets excel_sheets; // worksheet集合
CWorksheet excel_work_sheet; // 当前使用的worksheet
CRange excel_current_range; // 当前操作的区域
CString open_excel_file; // 打开的Excel文件名称
BOOL OpenFileFlag; // 打开Excel文件是否失败
protected:
// 使用某个sheet
BOOL LoadSheet(long iSheet);
BOOL LoadSheet(const char* sheet_name);
public:
// 构造函数:打开Excel文件
CReadExcelFile(const char* file_name);
// 析构函数:关闭Excel文件
~CReadExcelFile();
// 判断是否打开成功
BOOL IsOpenSuccess();
// 初始化Excel OLE
static BOOL InitExcel();
// 释放Excel OLE
static void ReleaseExcel();
// 取得打开的文件名称
CString GetCurrentFileName();
// 得到Sheet的总数
int GetSheetCount();
// 取得某个Sheet的名称
CString GetSheetName(long iSheet);
// 取得行的总数
int GetRowCount(long iSheet);
// 取得列的总数
int GetColCount(long iSheet);
// 根据Sheet名称获得Sheet标号
int GetSheetIndex(const char * sheet_name);
// 得到一个Cell的String
CString GetCellString(long iSheet, long iRow, long iCol);
// 得到一个Cell的Int
int GetCellInt(long iSheet, long iRow, long iCol);
// 得到一个Cell的double
double GetCellDouble(long iSheet, long iRow, long iCol);
// 检查一个Cell是否空
BOOL IsCellEmpty(long iSheet, long iRow, long iCol);
// 检查一个Cell是否是字符串
BOOL IsCellString(long iSheet, long iRow, long iCol);
// 检查一个Cell是否是数值
BOOL IsCellInt(long iSheet, long iRow, long iCol);
// 取得列的名称(如第27列为AA)
static CString GetColName(long iCol);
};
创建头文件的实现文件 CExcelFileRead.cpp中定义以下代码。
#include "stdafx.h"
#include "CExcelFileRead.h"
COleVariant covTrue((short)TRUE), covFalse((short)FALSE), covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
// Excel App初始化
CApplication CReadExcelFile::excel_application;
// 初始化Excel OLE
BOOL CReadExcelFile::InitExcel()
{
// 初始化COM库
CoInitialize(NULL);
// 初始化Excel
if (!excel_application.CreateDispatch("Excel.Application", NULL))return FALSE;
excel_application.put_DisplayAlerts(FALSE); // 屏蔽警告
return TRUE;
}
// 释放Excel OLE
void CReadExcelFile::ReleaseExcel()
{
excel_application.ReleaseDispatch();
excel_application.Quit();
excel_application = NULL;
}
// 构造函数
CReadExcelFile::CReadExcelFile(const char * file_name)
{
// 连接excel app
excel_books.AttachDispatch(excel_application.get_Workbooks());
// 打开文件并初始化excel_work_book
LPDISPATCH lpDisp = NULL;
TRY
{
lpDisp = excel_books.Open(file_name, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional);
}
CATCH(COleDispatchException, e)
{
AfxMessageBox(e->m_strDescription);
OpenFileFlag = FALSE;
return;
}
END_CATCH
OpenFileFlag = TRUE;
excel_work_book.AttachDispatch(lpDisp);
// 得到所有的sheets并初始化excel_sheets
excel_sheets.AttachDispatch(excel_work_book.get_Worksheets());
// 得到活动的工作表并初始化excel_work_sheet
excel_work_sheet.AttachDispatch(excel_work_book.get_ActiveSheet());
// 初始化当前的操作区域excel_current_range
excel_current_range.AttachDispatch(excel_work_sheet.get_Cells());
// 设置当前的文件名
open_excel_file = file_name;
}
// 析构函数:关闭文件
CReadExcelFile::~CReadExcelFile()
{
excel_books.Close();
}
// 判断是否打开成功
BOOL CReadExcelFile::IsOpenSuccess() { return OpenFileFlag; }
// 返回打开的Excel文件名称
CString CReadExcelFile::GetCurrentFileName()
{
return open_excel_file;
}
// 加载某个Sheet,通过index
BOOL CReadExcelFile::LoadSheet(long iSheet)
{
if (iSheet > excel_sheets.get_Count())return FALSE;
// 如果iSheet当前已经加载
if (iSheet == excel_work_sheet.get_Index())return TRUE;
// 释放先前的
excel_current_range.ReleaseDispatch();
excel_work_sheet.ReleaseDispatch();
// 加载iSheet
LPDISPATCH lpDisp = excel_sheets.get_Item(COleVariant((long)iSheet));
if (!lpDisp)return FALSE;
excel_work_sheet.AttachDispatch(lpDisp);
excel_current_range.AttachDispatch(excel_work_sheet.get_Cells());
return TRUE;
}
// 加载某个Sheet,通过名称
BOOL CReadExcelFile::LoadSheet(const char * sheet_name)
{
// 如果iSheet当前已经加载
if (excel_work_sheet.get_Name() == sheet_name)return TRUE;
// 释放先前的
excel_current_range.ReleaseDispatch();
excel_work_sheet.ReleaseDispatch();
// 加载iSheet
LPDISPATCH lpDisp = excel_sheets.get_Item(COleVariant(sheet_name));
if (!lpDisp)return FALSE;
excel_work_sheet.AttachDispatch(lpDisp);
excel_current_range.AttachDispatch(excel_work_sheet.get_Cells());
return TRUE;
}
// 获得sheet数目
int CReadExcelFile::GetSheetCount()
{
return excel_sheets.get_Count();
}
// 获得某个Sheet的名称
CString CReadExcelFile::GetSheetName(long iSheet)
{
// 加载iSheet
if (!LoadSheet(iSheet))return CString();
return excel_work_sheet.get_Name();
}
// 获得某个Sheet的row数目
int CReadExcelFile::GetRowCount(long iSheet)
{
// 加载iSheet
if (!LoadSheet(iSheet))return 0;
// 获取使用范围usedRange,获取行range
CRange usedRange; usedRange.AttachDispatch(excel_work_sheet.get_UsedRange());
CRange range; range.AttachDispatch(usedRange.get_Rows());
// 获得数目
int count = range.get_Count();
// 释放
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
// 返回
return count;
}
// 获得某个Sheet的column数目
int CReadExcelFile::GetColCount(long iSheet)
{
// 加载iSheet
if (!LoadSheet(iSheet))return 0;
// 获取使用范围usedRange,获取行range
CRange usedRange; usedRange.AttachDispatch(excel_work_sheet.get_UsedRange());
CRange range; range.AttachDispatch(usedRange.get_Columns());
// 获得数目
int count = range.get_Count();
// 释放
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
// 返回
return count;
}
// 根据Sheet的名称获得Sheet的index
int CReadExcelFile::GetSheetIndex(const char * sheet_name)
{
if (!LoadSheet(sheet_name))return -1;
return excel_work_sheet.get_Index();
}
// 获得某个Sheet的字符串Cell
CString CReadExcelFile::GetCellString(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return CString();
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
range.ReleaseDispatch();
// 分析vResult
CString str;
if (vResult.vt == VT_BSTR)str = vResult.bstrVal; // 字符串
else if (vResult.vt == VT_INT)str.Format(_T("%d"), vResult.pintVal); // 整数
else if (vResult.vt == VT_R8)str.Format(_T("%0.0f"), vResult.dblVal); // 8字节的整数
else if (vResult.vt == VT_DATE) // 时间格式
{
SYSTEMTIME st; VariantTimeToSystemTime(vResult.date, &st);
CTime tm(st); str = tm.Format("%Y-%m-%d");
}
else if (vResult.vt == VT_EMPTY)str = ""; // 空的单元格
else str = "";
return str;
}
// 获得某个Sheet的整数Cell
int CReadExcelFile::GetCellInt(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return 0;
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
range.ReleaseDispatch();
// 分析vResult
int num = static_cast<int>(vResult.dblVal);
return num;
}
// 获得某个Sheet的浮点数Cell
double CReadExcelFile::GetCellDouble(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return 0.0;
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
range.ReleaseDispatch();
// 分析vResult
double rtn_value = 0.0;
if (vResult.vt == VT_R8)rtn_value = vResult.dblVal;
return rtn_value;
}
// 检查Cell是否空
BOOL CReadExcelFile::IsCellEmpty(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return TRUE;
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
range.ReleaseDispatch();
// 分析vResult
if (vResult.vt == VT_EMPTY)return TRUE;
else return FALSE;
}
// 检查Cell是否是字符串
BOOL CReadExcelFile::IsCellString(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return FALSE;
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
//VT_BSTR标示字符串
if (vResult.vt == VT_BSTR)return TRUE;
return FALSE;
}
// 检查Cell是否是int
BOOL CReadExcelFile::IsCellInt(long iSheet, long iRow, long iCol)
{
// 加载iSheet
if (!LoadSheet(iSheet))return FALSE;
// 读取
CRange range; range.AttachDispatch(excel_current_range.get_Item(COleVariant((long)iRow), COleVariant((long)iCol)).pdispVal);
COleVariant vResult = range.get_Value2();
// 检查
if (vResult.vt == VT_INT || vResult.vt == VT_R8)return TRUE;
return FALSE;
}
// 取得列的名称(如第27列为AA)
CString CReadExcelFile::GetColName(long iCol)
{
// 初始化column_name
char column_name[64];
// 分析iCol
size_t str_len = 0; while (iCol > 0)
{
int num_data = iCol % 26;
iCol /= 26;
if (num_data == 0)
{
num_data = 26;
iCol--;
}
column_name[str_len] = (char)((num_data - 1) + 'A');
str_len++;
}
column_name[str_len] = '\0';
// 反转
_strrev(column_name);
// 返回
return CString(column_name);
}
创建一个对外接口头文件DLL_API.h文件,代码如下:
#pragma once
//--------------------------------------------------------------------//
// DLL对外接口函数:
// 1.完成Excel OLE的初始化
// 2.给出要读取的文件名、Sheet标号、列数、行数等相关信息进行读取;
// 3.完成Excel OLE的释放
//--------------------------------------------------------------------//
// 维护函数:
// 维护函数实现从读取函数到CReadExcel类对象的过程实现和维护。在得到
// 用户调用的读取函数之后,维护函数判断该文件是否已经打开:
// 1.如果已经打开,那么到OpenFileList中找到对应的CReadExcelFile类的
// 指针,并进行后续的操作;
// 2.如果没有打开,那么申请一个新的CReadExcelFile类并将其存入打开文
// 件列表OpenFileList,之后进行后续操作;
//--------------------------------------------------------------------//
#pragma once
#include "CExcelFileRead.h"
#include<vector>
#define DLL_API extern "C" __declspec(dllexport)
using namespace std;
//----DLL全局变量:读取文件记录---------------------------------------//
vector<CReadExcelFile*> OpenFileList;
//----DLL对外接口函数-------------------------------------------------//
// 完成Excel OLE的初始化
DLL_API bool Excel_Init();
// 完成Excel OLE的释放
DLL_API void Excel_Release();
// 得到某文件Sheet的总数
DLL_API int get_sheets_count(string &file_name);
// 得到某文件某个Sheet的名称
DLL_API string get_sheet_name(string &file_name, long iSheet);
// 得到某个文件某个Sheet的行数
DLL_API int get_rows_count(string &file_name, long iSheet);
// 得到某个文件某个Sheet的列数
DLL_API int get_cols_count(string &file_name, long iSheet);
// 根据Sheet名称获得Sheet标号
DLL_API int get_sheet_index(string &file_name, string &sheet_name);
// 得到某个文件某个Sheet某个Cell(字符串)
DLL_API string get_cell_string(string &file_name, long iSheet, long iRows, long iCols);
// 得到某个文件某个Sheet某个Cell(整数)
DLL_API int get_cell_int(string &file_name, long iSheet, long iRows, long iCols);
// 得到某个文件某个Sheet某个Cell(浮点数)
DLL_API double get_cell_double(string &file_name, long iSheet, long iRows, long iCols);
// 判断Cell是否空
DLL_API bool IsCellEmpty(string &file_name, long iSheet, long iRows, long iCols);
//----维护函数,对外不可见---------------------------------------------//
// 在OpenFileList中寻找某文件的index,不存在返回-1
int FindFileIndexInList(string &file_name);
// 总维护过程
CReadExcelFile *Maintain(string &file_name);
// 释放Excel OLE时要清空列表,析构其中所有CReadExcelFile类
void CloseAllFile();
#include "stdafx.h"
#include "DLL_API.h"
//--维护函数的实现----------------------------------------------------//
// 在OpenFileList中寻找某文件的index,不存在返回-1
int FindFileIndexInList(string &file_name)
{
for (int index = 0; index < OpenFileList.size(); index++)
if (OpenFileList.at(index)->GetCurrentFileName() == file_name.c_str())
return index;
return -1;
}
// 总维护过程
CReadExcelFile *Maintain(string &file_name)
{
// 在OpenFileList中寻找某文件
int index = FindFileIndexInList(file_name);
// 如果找到,返回指针
if (index != (-1))return OpenFileList.at(index);
// 如果未找到,新打开一个文件
CReadExcelFile *new_file = new CReadExcelFile(file_name.c_str());
// 若打开文件失败,关闭所有文件,释放Excel OLE并退出
if (!(new_file->IsOpenSuccess()))
{
delete new_file;
CloseAllFile();
Excel_Release();
exit(1);
}
// 否则,将新文件存入打开文件列表
OpenFileList.push_back(new_file);
return new_file;
}
// 释放Excel OLE时要清空列表,析构其中所有CReadExcelFile类
void CloseAllFile()
{
for (int index = 0; index < OpenFileList.size(); index++)
delete OpenFileList.at(index);
OpenFileList.clear();
}
//--DLL对外接口函数的实现---------------------------------------------//
// 完成Excel OLE的初始化
bool Excel_Init()
{
if (CReadExcelFile::InitExcel())return true;
else return false;
}
// 完成Excel OLE的释放
void Excel_Release()
{
CloseAllFile();
CReadExcelFile::ReleaseExcel();
}
// 得到某文件Sheet的总数
int get_sheets_count(string &file_name)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetSheetCount();
}
// 得到某文件某个Sheet的名称
string get_sheet_name(string &file_name, long iSheet)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetSheetName(iSheet);
}
// 得到某个文件某个Sheet的行数
int get_rows_count(string &file_name, long iSheet)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetRowCount(iSheet);
}
// 得到某个文件某个Sheet的列数
int get_cols_count(string &file_name, long iSheet)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetColCount(iSheet);
}
// 根据Sheet名称获得Sheet标号
int get_sheet_index(string &file_name, string &sheet_name)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetSheetIndex(sheet_name.c_str());
}
// 得到某个文件某个Sheet某个Cell(字符串)
string get_cell_string(string &file_name, long iSheet, long iRows, long iCols)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetCellString(iSheet, iRows, iCols);
}
// 得到某个文件某个Sheet某个Cell(整数)
int get_cell_int(string &file_name, long iSheet, long iRows, long iCols)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetCellInt(iSheet, iRows, iCols);
}
// 得到某个文件某个Sheet某个Cell(浮点数)
double get_cell_double(string &file_name, long iSheet, long iRows, long iCols)
{
CReadExcelFile *file = Maintain(file_name);
return file->GetCellDouble(iSheet, iRows, iCols);
}
// 判断Cell是否空
bool IsCellEmpty(string &file_name, long iSheet, long iRows, long iCols)
{
CReadExcelFile *file = Maintain(file_name);
return file->IsCellEmpty(iSheet, iRows, iCols);
}
下图为本动态链接库工程的文件结构: