最近一个同学说是要做一个报账的软件,总体上要实现的功能就是读写Excel文件,于是自己就开始在网上找读写Excel的方法,首先看到了C/C++读写Excel的几种方法,说是读写的方法有很多,但是反正就是各有利弊,我就选择了以OLE的方式进行Excel的操作(这种方式必须要在电脑上安装Excel程序,否则连最开始的配置就无法完成)。
在网上也看到了很多教程,其中VS2010通过OLE操作Excel2010这篇博客的正文加上下面有个博主的评论就差不多可以实现了,但是都是以文字、方法的方式进行讲解,很难操作,并且自己在弄得时候也遇到了各种各样的问题,所以,我以我自己的实例进行讲解:
1.新建一个MFC工程文件,如下
2.为工程文件导入Excel.exe程序并添加一些必须的类,首先点“文件”,然后找到自己的Excel的安装位置添加过来,最后添加下面 的那几个类(如果前面不小心已经添加了这几个类上面却没有添加Excel.exe程序,这个时候直接把添加的文件直接“删除”即可,如果不删直接添加的话仍然会添加成功,但是添加的就是“CApplication0.h”、“CRange0.h”,这样的话就会跟下面添加的头文件和源文件的类不一致,就会报错)
3.添加完成以后,在“解决方案资源管理器”里面就多了下面6个文件,依次打开每个文件,把右边这个路径给注释掉或者直接删掉
4.这个时候编译时没有问题的
5.为这个工程添加对Excel表格进行操作的头文件“IllusionExcelFile.h”和源文件“IllusionExcelFile.cpp”
头文件(IllusionExcelFile.h):
#pragma once
//OLE的头文件
#include "CRange.h"
#include "CWorkbook.h"
#include "CWorkbooks.h"
#include "CWorksheet.h"
#include "CWorksheets.h"
#include "CApplication.h"
///
///用于OLE的方式的EXCEL读写,
class IllusionExcelFile
{
public:
//构造函数和析构函数
IllusionExcelFile();
virtual ~IllusionExcelFile();
protected:
///打开的EXCEL文件名称
CString open_excel_file_;
///EXCEL BOOK集合,(多个文件时)
CWorkbooks excel_books_;
///当前使用的BOOK,当前处理的文件
CWorkbook excel_work_book_;
///EXCEL的sheets集合
CWorksheets excel_sheets_;
///当前使用sheet
CWorksheet excel_work_sheet_;
///当前的操作区域
CRange excel_current_range_;
///是否已经预加载了某个sheet的数据
BOOL already_preload_;
///Create the SAFEARRAY from the VARIANT ret.
COleSafeArray ole_safe_array_;
protected:
///EXCEL的进程实例
static CApplication excel_application_;
public:
///
void ShowInExcel(BOOL bShow);
///检查一个CELL是否是字符串
BOOL IsCellString(long iRow, long iColumn);
///检查一个CELL是否是数值
BOOL IsCellInt(long iRow, long iColumn);
///得到一个CELL的String
CString GetCellString(long iRow, long iColumn);
///得到整数
int GetCellInt(long iRow, long iColumn);
///得到double的数据
double GetCellDouble(long iRow, long iColumn);
///取得行的总数
int GetRowCount();
///取得列的总数
int GetColumnCount();
///使用某个shet,shit,shit
BOOL LoadSheet(long table_index,BOOL pre_load = FALSE);
///通过名称使用某个sheet,
BOOL LoadSheet(LPCTSTR sheet,BOOL pre_load = FALSE);
///通过序号取得某个Sheet的名称
CString GetSheetName(long table_index);
///得到Sheet的总数
int GetSheetCount();
///打开文件
BOOL OpenExcelFile(LPCTSTR file_name);
///关闭打开的Excel 文件,有时候打开EXCEL文件就要
void CloseExcelFile(BOOL if_save = FALSE);
//另存为一个EXCEL文件
void SaveasXSLFile(const CString &xls_file);
///取得打开文件的名称
CString GetOpenFileName();
///取得打开sheet的名称
CString GetLoadSheetName();
///写入一个CELL一个int
void SetCellInt(long irow, long icolumn,int new_int);
///写入一个CELL一个string
void SetCellString(long irow, long icolumn,CString new_string);
public:
///初始化EXCEL OLE
static BOOL InitExcel();
///释放EXCEL的 OLE
static void ReleaseExcel();
///取得列的名称,比如27->AA
static char *GetColumnName(long iColumn);
protected:
//预先加载
void PreLoadSheet();
};
源文件(IllusionExcelFile.cpp):
#include "StdAfx.h"
#include "IllusionExcelFile.h"
COleVariant
covTrue((short)TRUE),
covFalse((short)FALSE),
covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
//
CApplication IllusionExcelFile::excel_application_;
IllusionExcelFile::IllusionExcelFile():
already_preload_(FALSE)
{
}
IllusionExcelFile::~IllusionExcelFile()
{
//
CloseExcelFile();
}
//初始化EXCEL文件,
BOOL IllusionExcelFile::InitExcel()
{
CoUninitialize();
if(CoInitialize(NULL)==S_FALSE)
{
AfxMessageBox(_T("初始化COM支持库失败!"));
return FALSE;
}
//创建Excel 2000服务器(启动Excel)
if (!excel_application_.CreateDispatch(_T("Excel.Application"),NULL))
{
AfxMessageBox(_T("创建Excel服务失败,你可能没有安装EXCEL,请检查!"));
return FALSE;
}
excel_application_.put_DisplayAlerts(FALSE);
return TRUE;
}
//
void IllusionExcelFile::ReleaseExcel()
{
excel_application_.Quit();
excel_application_.ReleaseDispatch();
excel_application_=NULL;
}
//打开excel文件
BOOL IllusionExcelFile::OpenExcelFile(LPCTSTR file_name)
{
//先关闭
CloseExcelFile();
//利用模板文件建立新文档
excel_books_.AttachDispatch(excel_application_.get_Workbooks(),true);
LPDISPATCH lpDis = NULL;
lpDis = excel_books_.Add(COleVariant(file_name));
if (lpDis)
{
excel_work_book_.AttachDispatch(lpDis);
//得到Worksheets
excel_sheets_.AttachDispatch(excel_work_book_.get_Worksheets(),true);
//记录打开的文件名称
open_excel_file_ = file_name;
return TRUE;
}
return FALSE;
}
//关闭打开的Excel 文件,默认情况不保存文件
void IllusionExcelFile::CloseExcelFile(BOOL if_save)
{
//如果已经打开,关闭文件
if (open_excel_file_.IsEmpty() == FALSE)
{
//如果保存,交给用户控制,让用户自己存,如果自己SAVE,会出现莫名的等待
if (if_save)
{
ShowInExcel(TRUE);
}
else
{
//
excel_work_book_.Close(COleVariant(short(FALSE)),COleVariant(open_excel_file_),covOptional);
excel_books_.Close();
}
//打开文件的名称清空
open_excel_file_.Empty();
}
excel_sheets_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
excel_current_range_.ReleaseDispatch();
excel_work_book_.ReleaseDispatch();
excel_books_.ReleaseDispatch();
}
void IllusionExcelFile::SaveasXSLFile(const CString &xls_file)
{
excel_work_book_.SaveAs(COleVariant(xls_file),
covOptional,
covOptional,
covOptional,
covOptional,
covOptional,
0,
covOptional,
covOptional,
covOptional,
covOptional,
covOptional);
return;
}
int IllusionExcelFile::GetSheetCount()
{
return excel_sheets_.get_Count();
}
CString IllusionExcelFile::GetSheetName(long table_index)
{
CWorksheet sheet;
sheet.AttachDispatch(excel_sheets_.get_Item(COleVariant((long)table_index)),true);
CString name = sheet.get_Name();
sheet.ReleaseDispatch();
return name;
}
//按照序号加载Sheet表格,可以提前加载所有的表格内部数据
BOOL IllusionExcelFile::LoadSheet(long table_index,BOOL pre_load)
{
LPDISPATCH lpDis = NULL;
excel_current_range_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
lpDis = excel_sheets_.get_Item(COleVariant((long)table_index));
if (lpDis)
{
excel_work_sheet_.AttachDispatch(lpDis,true);
excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true);
}
else
{
return FALSE;
}
already_preload_ = FALSE;
//如果进行预先加载
if (pre_load)
{
PreLoadSheet();
already_preload_ = TRUE;
}
return TRUE;
}
//按照名称加载Sheet表格,可以提前加载所有的表格内部数据
BOOL IllusionExcelFile::LoadSheet(LPCTSTR sheet,BOOL pre_load)
{
LPDISPATCH lpDis = NULL;
excel_current_range_.ReleaseDispatch();
excel_work_sheet_.ReleaseDispatch();
lpDis = excel_sheets_.get_Item(COleVariant(sheet));
if (lpDis)
{
excel_work_sheet_.AttachDispatch(lpDis,true);
excel_current_range_.AttachDispatch(excel_work_sheet_.get_Cells(), true);
}
else
{
return FALSE;
}
//
already_preload_ = FALSE;
//如果进行预先加载
if (pre_load)
{
already_preload_ = TRUE;
PreLoadSheet();
}
return TRUE;
}
//得到列的总数
int IllusionExcelFile::GetColumnCount()
{
CRange range;
CRange usedRange;
usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true);
range.AttachDispatch(usedRange.get_Columns(), true);
int count = range.get_Count();
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
return count;
}
//得到行的总数
int IllusionExcelFile::GetRowCount()
{
CRange range;
CRange usedRange;
usedRange.AttachDispatch(excel_work_sheet_.get_UsedRange(), true);
range.AttachDispatch(usedRange.get_Rows(), true);
int count = range.get_Count();
usedRange.ReleaseDispatch();
range.ReleaseDispatch();
return count;
}
//检查一个CELL是否是字符串
BOOL IllusionExcelFile::IsCellString(long irow, long icolumn)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true);
COleVariant vResult =range.get_Value2();
//VT_BSTR标示字符串
if(vResult.vt == VT_BSTR)
{
return TRUE;
}
return FALSE;
}
//检查一个CELL是否是数值
BOOL IllusionExcelFile::IsCellInt(long irow, long icolumn)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true);
COleVariant vResult =range.get_Value2();
//好像一般都是VT_R8
if(vResult.vt == VT_INT || vResult.vt == VT_R8)
{
return TRUE;
}
return FALSE;
}
//
CString IllusionExcelFile::GetCellString(long irow, long icolumn)
{
COleVariant vResult ;
CString str;
//字符串
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true);
vResult =range.get_Value2();
range.ReleaseDispatch();
}
//如果数据依据预先加载了
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vResult = val;
}
if(vResult.vt == VT_BSTR)
{
str=vResult.bstrVal;
}
//整数
else if (vResult.vt==VT_INT)
{
str.Format(_T("%d"),vResult.pintVal);
}
//8字节的数字
else if (vResult.vt==VT_R8)
{
str.Format(_T("%0.0f"),vResult.dblVal);
}
//时间格式
else if(vResult.vt==VT_DATE)
{
SYSTEMTIME st;
VariantTimeToSystemTime(www.sxzhongrui.com, &st);
CTime tm(st);
str=tm.Format("%Y-%m-%d");
}
//单元格空的
else if(vResult.vt==VT_EMPTY)
{
str="";
}
return str;
}
double IllusionExcelFile::GetCellDouble(long irow, long icolumn)
{
double rtn_value = 0;
COleVariant vresult;
//字符串
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true);
vresult =range.get_Value2();
range.ReleaseDispatch();
}
//如果数据依据预先加载了
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vresult = val;
}
if (vresult.vt==VT_R8)
{
rtn_value = vresult.dblVal;
}
return rtn_value;
}
//VT_R8
int IllusionExcelFile::GetCellInt(long irow, long icolumn)
{
int num;
COleVariant vresult;
if (already_preload_ == FALSE)
{
CRange range;
range.AttachDispatch(excel_current_range_.get_Item (COleVariant((long)irow),COleVariant((long)icolumn)).pdispVal, true);
vresult = range.get_Value2();
range.ReleaseDispatch();
}
else
{
long read_address[2];
VARIANT val;
read_address[0] = irow;
read_address[1] = icolumn;
ole_safe_array_.GetElement(read_address, &val);
vresult = val;
}
//
num = static_cast
return num;
}
void IllusionExcelFile::SetCellString(long irow, long icolumn,CString new_string)
{
COleVariant new_value(new_string);
CRange start_range = excel_work_sheet_.get_Range(COleVariant(_T("A1")),covOptional);
CRange write_range = start_range.get_Offset(COleVariant((long)irow -1),COleVariant((long)icolumn -1) );
write_range.put_Value2(new_value);
start_range.ReleaseDispatch();
write_range.ReleaseDispatch();
}
void IllusionExcelFile::SetCellInt(long irow, long icolumn,int new_int)
{
COleVariant new_value((long)new_int);
CRange start_range = excel_work_sheet_.get_Range(COleVariant(_T("A1")),covOptional);
CRange write_range = start_range.get_Offset(COleVariant((long)irow -1),COleVariant((long)icolumn -1) );
write_range.put_Value2(new_value);
start_range.ReleaseDispatch();
write_range.ReleaseDispatch();
}
//
void IllusionExcelFile::ShowInExcel(BOOL bShow)
{
excel_application_.put_Visible(bShow);
excel_application_.put_UserControl(bShow);
}
//返回打开的EXCEL文件名称
CString IllusionExcelFile::GetOpenFileName()
{
return open_excel_file_;
}
//取得打开sheet的名称
CString IllusionExcelFile::GetLoadSheetName()
{
return excel_work_sheet_.get_Name();
}
//取得列的名称,比如27->AA
char *IllusionExcelFile::GetColumnName(long icolumn)
{
static char column_name[64];
size_t str_len = 0;
while(icolumn > 0)
{
int num_data = icolumn % 26;
icolumn /= 26;
if (num_data == 0)
{
num_data = 26;
icolumn--;
}
column_name[str_len] = (char)((num_data-1) + 'A' );
str_len ++;
}
column_name[str_len] = '\0';
//反转
_strrev(column_name);
return column_name;
}
//预先加载
void IllusionExcelFile::PreLoadSheet()
{
CRange used_range;
used_range = excel_work_sheet_.get_UsedRange();
VARIANT ret_ary = used_range.get_Value2();
if (!(ret_ary.vt & VT_ARRAY))
{
return;
}
//
ole_safe_array_.Clear();
ole_safe_array_.Attach(ret_ary);
}
6.这个时候再次编译会出现如下问题,双击出现的错误定位到错误的行,只需在“DialogBox()”前面加一个横杠改为“_DialogBox()”即可
7.这个时候已经对工程文件配置好了,下面就开始进行测试,对Excel进行读写:
8.在工程文件的头文件“COperateExcelByOLEDlg.h”中包含我们刚才添加的头文件,并定义一个“IllusionExcelFile”类的对象“excl”如下:
? ?
9.在源文件“COperateExcelByOLEDlg.cpp”中的“OnInitDialog()”初始化函数中添加如下代码:
AfxOleInit();
excl.InitExcel();
10.在界面上添加一个按钮控件并为这个按钮添加如下代码(代码里面为了测试,我在电脑的F盘新建了一个“Exce”的文件夹并新建了一个“工作簿1.xlsx”文件,自己也可以根据自己的需求改变路径):
bool bRet = excl.OpenExcelFile("F:\\Excel\\工作簿1.xlsx");
CString strSheetName = excl.GetSheetName(1);
bool bLoad = excl.LoadSheet(strSheetName);
int nRow = excl.GetRowCount();
int nCol = excl.GetColumnCount();
if (bRet == 1)
{
AfxMessageBox("文件打开成功!");
for (int i=1; i<10; ++i)
{
for (int j=1; j<20; ++j)
{
CString strValue = excl.GetCellString(i,j);
excl.SetCellInt(i,j,9);
}
}
}
else
{
AfxMessageBox("文件打开失败!");
return ;
}
excl.SaveasXSLFile("F:\\Excel\\工作簿2.xlsx");
AfxMessageBox("文件写入成功!");
11.此时按道理讲,应该就可以运行了
??
12.如果运行的时候报错:“服务器出现意外情况”,看到一篇C#操作Excel报错:服务器出现意外情况,就是自己的电脑安装了福熙阅读器这个软件
解决方案:
打开你电脑中的Office-Excel。在“文件”->"选项"->"加载项"->"管理",选择“COM加载项”,点击“转到”按钮。将“复习阅读器”这个选项不勾选即可
最后,我上传了这个实例教程的工程文件,需要的可以自行下载:https://www.sxzhongrui.com/my
因为现在上传好像是自己改不了下载所需积分,因此如果没有积分的朋友可以直接下面评论给我说,我再私发也可以的
?
出处:https://www.sxzhongrui.com/ouyangyanlan/article/details/48597951
说到操作excel,可能用java来写更方便一些。mfc确实不太适合这种操作,个人感觉要比java复杂一点,尤其是在数据类型的转换和操作上,java简单有效。下面进入正题,环境为vs2010。
1. 这个小的mfc程序实现的是读取excel中的数据,然后经过数据处理,生成一个新的excel来存放处理结果。
2.我们用到的技术是通过OLE/COM对excel进行操作。
步骤:
1.新建MFC对话框。注意勾选自动化,否则后面加入不了需要的库,导致启动服务失败等错误。有的博客说需要加入
1 if (!AfxOleInit())
2 {
3 AfxMessageBox(IDP_OLE_INIT_FAILED);
4 return FALSE;
5 }
笔者实验发现自己的环境自动添加了这部分代码。
? ? ? ? 2.?向项目工程中添加基本的7个类(?Excel?作为?OLE/COM?库插件,定义好了各类交互的接口,这些接口是跨语言的接口。?VC?可以通过导入这些接口,并通过 ? 接口来对?Excel?的操作),?由于本文只关心对?Excel?表格中的数据的读取,主要关注7个接_?Application、?Workbooks?、?_Workbook?、Worksheets?、?_Worksheet?、Range、Font?。
VS2012?导入?OLE/COM?组件的接口的步骤为:?项目->类向导->添加类->类型库中的MFC类?,先选择要导入的组件所在的路径,即?Excel.exe?所在的路 ? ? ?径,然后再选择?要导入的?Excel?类型库中的接口。组件路径一般为C:\Program Files\Microsoft Office\Office15\EXCEL.exe;格式类似。
? ? ? ? 3. 导入之后需要“#import?"C:\\Program?Files\\Microsoft?Office\\Office12\\EXCEL.EXE"?no_namespace”注释掉,然后添加头文 ? ? ? ? ? ? ? ? 件:#include?
4.如果有错误error?C2059双击error?C2059,将VARIANT?DialogBox()改成VARIANT?_DialogBox()再次编译,通过!!
5.读写excel。
在对话框头文件中*Dlg.h定义变量
1 //定义接口类变量
2 CApplication app;
3 CWorkbook book;
4 CWorkbooks books;
5 CWorksheet sheet;
6 CWorksheets sheets;
7 CRange range;
8 CMyFont font;
9 CRange cols;
10 LPDISPATCH lpDisp;
以上部分来自网络,经试验,正确无误。
6.功能可以分为三个模块,第一个模块是一个文件选择器,第二个模块是读取excel,第三个模块为创建excel.
(1)文件选择部分
void CTXDlg::OnBnClickedButton1(){// TODO: 在此添加控件通知处理程序代码CFileDialog OpenDlg(true);if(OpenDlg.DoModal() == IDOK){CEdit* ECtr = (CEdit *)GetDlgItem(IDC_EDIT1);fileNameTrans = OpenDlg.GetPathName();ECtr->SetWindowText(fileNameTrans);}}
(2)导入excel并读取数据,放到list里面暂存待处理
void CTXDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码int mCount=0;CString str,str1;double num1,num2;double d_skewing =0.004;COleVariant vResult;COleVariant covOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);if(!app.CreateDispatch(_T("Excel.Application"))){this->MessageBox(_T("无法创建Excel应用!"));return;}books.AttachDispatch(app.get_Workbooks());lpDisp = www.sxzhongrui.com(fileNameTrans,covOptional, covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional);book.AttachDispatch(lpDisp);sheets.AttachDispatch(book.get_Worksheets());//得到当前活跃sheetlpDisp = book.get_ActiveSheet();sheet.AttachDispatch(lpDisp);//获得行数CRange usedRange;CRange mRange;usedRange.AttachDispatch(sheet.get_UsedRange());mRange.AttachDispatch(usedRange.get_Rows(),true);int count = mRange.get_Count();usedRange.ReleaseDispatch();mRange.ReleaseDispatch();//读取一个值range.AttachDispatch(sheet.get_Cells());range.AttachDispatch(range.get_Item(COleVariant((long)1),COleVariant((long)2)).pdispVal);vResult = range.get_Value2();if(vResult.vt == VT_BSTR){str1 = vResult.bstrVal;}else if(vResult.vt == VT_R8){str1.Format(L"%f",vResult.dblVal);}else{str1 = "数据类型错误!";this->MessageBox(str1);return;}num1 = _wtof(str1.GetBuffer(0));mList1.AddTail(1);mList2.AddTail(str1);//添加第一个值++mCount;//读取单元格值for(int i=2;i
(3)对读取的数据进行处理,结果保存,生成新的excel
void CTXDlg::OnBnClickedButton3(){// TODO: 在此添加控件通知处理程序代码if(!app.CreateDispatch(_T("Excel.Application"),NULL)){AfxMessageBox(_T("启动Excel服务器失败!"));return ;}//判断当前的Excel的版本CString strExcelVersion = app.get_Version();int iStart =0;strExcelVersion = strExcelVersion.Tokenize(_T("."),iStart);if(_T("11")==strExcelVersion){AfxMessageBox(_T("当前的Excel的版本是2003"));}else if(_T("12")==strExcelVersion){AfxMessageBox(_T("当前的Excel的版本是2007"));}else{AfxMessageBox(_T("当前的Excel的版本是其他版本"));}app.put_Visible(true);app.put_UserControl(false);//得到工作薄容器books.AttachDispatch(app.get_Workbooks());//打开一个工作薄,如不存在,则创建CString strBookPath = _T("D:\\tmp.xls");try{lpDisp = www.sxzhongrui.com(strBookPath,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing,vtMissing);book.AttachDispatch(lpDisp);}catch(...){lpDisp = books.Add(vtMissing);book.AttachDispatch(lpDisp);}//得到工作薄中的sheet的容器sheets.AttachDispatch(book.get_Sheets());//打开一个sheet,如不存在,就新增一个sheetCString strSheetName = _T("NewSheet");try{//尝试打开一个已有的sheetlpDisp = sheets.get_Item(_variant_t(strSheetName));sheet.AttachDispatch(lpDisp);}catch(...){//创建一个新的sheetlpDisp = sheets.Add(vtMissing,vtMissing,_variant_t((long)1),vtMissing);sheet.AttachDispatch(lpDisp);sheet.put_Name(strSheetName);}range.AttachDispatch(sheet.get_Cells());//插入数据POSITION ps1 = mList1.GetHeadPosition();POSITION ps2 = mList2.GetHeadPosition();int aa;CString mStr1,mStr2,strr;for(int i=1;ps1!=NULL&&ps2!=NULL;mList1.GetNext(ps1),mList2.GetNext(ps2),i++){iCells.AttachDispatch(range.get_Item(COleVariant((long)i),COleVariant((long)1)).pdispVal,true);aa = mList1.GetAt(ps1);strr.Format(L"%d",aa);iCells.put_Value2(COleVariant(strr));iCells.AttachDispatch(range.get_Item(COleVariant((long)i),COleVariant((long)2)).pdispVal,true);strr = mList2.GetAt(ps2);iCells.put_Value2(COleVariant(strr));}//www.sxzhongrui.com();range.ReleaseDispatch();sheet.ReleaseDispatch();sheets.ReleaseDispatch();book.ReleaseDispatch();books.ReleaseDispatch();app.Quit();app.ReleaseDispatch();}
---------根据自己程序需要补充区域----------------------------------------------------------------
这几天老师让我做一个实现从VS2010中向Access数据库读写数据的程序。我想起当初学习VS的时候做过一个类似的密码管理器,就是利用数据库的一些知识。当时也是突发奇想,找了各种资料,总算有了个结果,勉强能用,但是脑袋却是一片迷茫。现在恰好有个机会重新做一遍,把思路好好地捋一捋,填填当初没埋上的坑。
基本思路
数据库系统的原理性知识先放一放,这里主要讲一下实现一个数据库程序时的实际操作。 数据库应用系统的设计步骤: 1. 数据库设计 2. 配置数据源 3. 编写数据库应用程序
具体步骤
1.数据库设计
既然是对数据库进行操作,那么首先要有一个数据库。 至于如何创建一个Access数据库,可以网上搜一下。大概就是使用Microsoft Access2013打开软件,建立空白桌面数据库, 开始,视图,定义表,填写表。
2.配置ODBC数据源
(1)开始->设置->在搜索框中输入“ODBC”->选择对应的系统版本, 弹出: (2)选择系统DSN,并点击右边的添加按钮,选择对应的驱动程序: (3)点击完成,进行数据源和数据库的配置: 数据源名随便取,数据库选择所要用到的数据库的路径。 (4)点击确定,可以看到数据源已经添加到系统DSN的列表中了。
3.编写数据库应用程序
终于可以打开VS敲代码了。 首先,当然是新建一个对话框工程,关于对话框界面的设计之类的操作就不再细表。这里主要介绍数据库相关的操作。
导入ado.dll动态链接库
因为使用到了ado,所以先要进行ado的导入。 在stdafx.h头文件中的最后一条include语句的下方(位置好像有讲究)加入:
#import "C:\Program Files\Common Files\System\ado\msado15.dll" no_namespace\
rename("EOF","adoEOF")rename("BOF","adoBOF") //导入ADO动态链接库
同时还要进行COM的初始化,虽然我不知道这是啥。
封装一个ADO类
由于COM的初始化和连接与关闭数据库的一些基本操作会多次用到而且看上去有点吓人,所以我索性把它们封装成了一个ADO类。 这个类的声明:
class ADO
{
public:
_ConnectionPtr m_pConnection; //连接对象指针
_RecordsetPtr m_pRecordset; //记录集对象指针
public:
ADO();
virtual ~ADO();
void OnInitADOConn(); //连接数据库
_RecordsetPtr& OpenRecordset(CString sql); //打开记录集
void CloseRecordset(); //关闭记录集
void CloseConn(); //关闭数据库连接
UINT GetRecordCount(_RecordsetPtr pRecordset); //获得记录数
};
比如其中的连接数据库的函数的定义是这样的:
void ADO::OnInitADOConn()
{
::CoInitialize(NULL);//COM的初始化
try
{
m_pConnection.CreateInstance("ADODB.Connection"); //创建连接对象实例
_bstr_t strConnect="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Test01.accdb;";//连接字符串
m_pConnection->Open(strConnect,"","",adModeUnknown); //打开数据库
}
catch(_com_error e)
{
AfxMessageBox(e.Description()); //弹出错误处理
}
}
使用ADO类操作数据库
封装之后,就可以方便地使用ADO来操作数据库了。 读取:
//从数据库中读取一个表,并显示在一个列表控件中
void CTestDlg::AddToGrid(void)
{
ADO m_Ado;
m_Ado.OnInitADOConn();//连接数据库
CString SQL = "select * from table1 order by 学号 desc"; //设置查询字符串
m_Ado.m_pRecordset = m_Ado.OpenRecordset(SQL);//打开记录集
while(!m_Ado.m_pRecordset->adoEOF)
{
m_list.InsertItem(0,"");
m_list.SetItemText(0,0,(char*)(_bstr_t)m_Ado.m_pRecordset->GetCollect("学号"));
m_list.SetItemText(0,1,(char*)(_bstr_t)m_Ado.m_pRecordset->GetCollect("姓名"));
m_list.SetItemText(0,2,(char*)(_bstr_t)m_Ado.m_pRecordset->GetCollect("成绩"));
m_Ado.m_pRecordset->MoveNext();//将记录集指针移动到下一条记录
}
m_Ado.CloseRecordset();
m_Ado.CloseConn();//断开数据库连接
}
添加:
//“添加”按钮的消息响应函数,添加一条记录到数据库
void CTestDlg::OnBnClickedAdd()
{
UpdateData(TRUE); //把编辑框中的数据传给相应的变量
if(m_numb.IsEmpty() || m_name.IsEmpty() || m_grad.IsEmpty())
{
MessageBox("基础信息不能为空!");
return;
}
ADO m_Ado; //定义一个ADO对象
m_Ado.OnInitADOConn(); //连接数据库
CString sql = "select * from table1"; //查询语句
m_Ado.m_pRecordset = m_Ado.OpenRecordset(sql); //打开记录集
try
{
m_Ado.m_pRecordset->AddNew(); //添加新行
m_Ado.m_pRecordset->PutCollect("学号",(_bstr_t)m_numb);
m_Ado.m_pRecordset->PutCollect("姓名",(_bstr_t)m_name);
m_Ado.m_pRecordset->PutCollect("成绩",(_bstr_t)m_grad);
m_Ado.m_pRecordset->Update(); //更新数据表记录
m_Ado.CloseRecordset(); //关闭数据集
m_Ado.CloseConn(); //关闭数据库连接
}
catch(...)
{
MessageBox("操作失败");
return;
}
MessageBox("添加成功");
m_list.DeleteAllItems(); //删除列表控件
AddToGrid(); //从数据库中将数据载入表格
}
基本操作就是以上,接下来解决一下期间遇到的问题。
开始填坑
1.什么是数据源?为什么要弄这么个东西?不弄行不行?
先看看百度百科怎么说: 数据源是指数据库应用程序所使用的数据库或者数据库服务器。 数据源(Data Source)顾名思义,数据的来源,是提供某种所需要数据的器件或原始媒体。在数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接。
应用程序要访问一个数据库,首先必须用ODBC管理器注册一个数据源,管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与具体数据库的联系。这样,只要应用程序将数据源名提供给ODBC,ODBC就能建立起与相应数据库的连接。
2. 连接字符串的问题
老版本的:
_bstr_t strConnect="DRIVER={Microsoft Access Driver (*.mdb)};\
uid=;pwd=;DBQ=SecretData1.mdb;";
新版本的:
_bstr_t strConnect="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Test01.accdb;";
m_pConnection->Open(strConnect,"","",adModeUnknown); //打开数据库
可能会出现错误信息:未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序。 解决办法: 去http://www.sxzhongrui.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463960fdcdb/AccessDatabaseEngine.exe下载。然后安装就行了。
3. 数据库文件的路径问题,在其他电脑上还能运行吗?
发现必须在项目路径和Debug文件夹路径中各自放一份数据库文件才能运行。不知道为啥。 http://www.sxzhongrui.com/topics/390096371