上次项目中用到F1Book,已经感觉到很纠结,但总归大大小小的问题差不多都被解决了。上次在网上查到bcb6自带的F1Book貌似版本太低,只是3点几的版本,但查到的资料几乎都是6.0版本的,索性这次就把F1Book升级了一下,感觉用起来比原来的舒服多了。我装的版本是6.1.4,网上找的。
(1)安装完成后导入到bcb中,具体导入方法如下:【组件】--【导入ActiveX控件】----【添加】,选择安装目录下的ocx文件,确定,然后编译安装。
(2)对于C++ Bulider,安装完成后使用时发现TextRC属性无法设置了,网上有人说在F1Book头文件中的TextRC定义的地方之后{read=get_...}后面加上write=set_...就行了。
从这个地方看来,bcb对应变量的变量的取值和赋值实质是通过函数来完成的,据我猜测,应该是编译器在编译的时候的工作,比方说Form.Left=100;应该在编译的时候编译成类似Form.set_left(100)类似的形式。以前总想不明白为什么只改变变量就能触发相应的函数,还以为是程序内部不断循环检查实现的,现在看来茅塞顿开了。
(3)控件升级之后,有出问题拉,晕死
发现原来用OLE操作Excel的代码出问题了,总是说非法内存访问,百度了一下,找到了几条类似的情况,虽然没找到为什么会这样,但我发现出现这种原因的都是F1Book升级之后,而原来低版本的时候都是好的。
(4)此路不通,另辟蹊径吧。。。可这条路走的我蛋疼啊。。。
首先想到在网上找个类库来试试,但下到的不是太庞大了(看不懂。。。)就要不然是VC、MFC下的(当然应该也可以用,但里边混杂这一些MFC的类,还要进行引入,恐怕在bcb中会出问题)。纠结了一个晚上,试了很多方法,最终我放弃了使用别人的类库,而采用ADO连接的方式,本以为有点烦的,没想到其实和访问数据库大同小异。
(5)ADO连接Excel
使用到的控件-----ADOConnection、ADOQuery
设置ConnectionString--------
ADOConnExcel->ConnectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+
OpenDialog1->FileName+";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=0\"";
其中HDR=Yes表示Excel中第一行为字段名
下面是别人的博客上转的
VC 利用ADO操作Excel(原创)
把Excel当做数据库来操作,步骤如下:
1、在stdafx.h中加入#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename ("EOF", "adoEOF")
2、在工程的App类的构造函数中打开要操作的Excel表格
_ConnectionPtr pCon_ex;
CString ConnectionString;
CToolsApp::CToolsApp()
{
CString m_strAppPath=_T("");
CString excel_path=_T("");
CString con_str=_T("");
//程序所在目录路径
TCHAR exeFullPath[MAX_PATH];
GetModuleFileName(NULL,exeFullPath,MAX_PATH);
CString str;
str.Format("%s",exeFullPath);
m_strAppPath = str.Left( str.ReverseFind( '\\' ) );
excel_path = m_strAppPath+"\\Database"+"\\新全国图数据统计模版--.xls";
CoInitialize(NULL);
//打开excel
/*"HDR=Yes;" 表示工作表的第一行是表头,没有数据。 "HDR=No;"与之相反。
"IMEX=1;"告诉驱动程序始终将"intermixed"数据类型(numbers, dates, strings等等)作为文本型读取。
注意:该选项可能引起Excel工作表写权限的修改。如果想写入数据,创建新表等必须使其为0*/
ConnectionString = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=");
ConnectionString += excel_path; //excel file name
ConnectionString += _T(";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=0\"");
BSTR resultsString = ConnectionString.AllocSysString();
pCon_ex.CreateInstance(__uuidof(Connection));
resultsString = ConnectionString.AllocSysString();
pCon_ex->Open(resultsString,"","",adModeUnknown);
}
3、从Excel中读数据
读数据比较简单,可以使用SQL查询语句来找到自己感兴趣的记录。
CString strSQL=_T("");
_RecordsetPtr pRst(__uuidof(Recordset)); //数据集
_RecordsetPtr Rs1(__uuidof(Recordset)); //数目集
strSQL="select * from [道路$] where 城市 like '%"+str_city+"%'"; //[道路$]为sheet的名称
pRst=(((CDataManaApp*)AfxGetApp())->pCon_ex)->Execute((_bstr_t)strSQL,NULL,adCmdText); //指定的城市
CString sql=_T("");
sql="select count(*) as geshu from [道路$] where 城市 like '%"+str_city+"%'";
Rs1=(((CDataManaApp*)AfxGetApp())->pCon_ex)->Execute((_bstr_t)sql,NULL,adCmdText);
_variant_t vCount=Rs1->GetCollect("geshu");
int num1=vCount.lVal; //符合条件的记录个数
pRst->MoveFirst(); //只读取第一行
_variant_t t = _variant_t(long(6));
result = (LPCSTR)_bstr_t(pRecordset->GetCollect(t));//以列序号的方式来读取字段内容 0based
result = (LPCSTR)_bstr_t(pRecordset->GetCollect("人口"));//以字段名的方式来读字段内容
4 增加新行以及填写某个cell
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));
try
{
m_pRecordset->Open("SELECT * FROM [道路$]",// 查询道路表中所有字段
((CToolsApp*)AfxGetApp())->pCon_ex.GetInterfacePtr(), // 获取库接库的IDispatch指针
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
try
{
CString strDate= "123.698";
//添加新记录
m_pRecordset->MoveFirst();
m_pRecordset->Move(15); //此行没用 无论当前的指针在什么位置,都将插入到最后一行 数据库是不能在中间插入新行的
m_pRecordset->AddNew(); //如是要更新cell 不要这句
_variant_t t = _variant_t(long(6));
m_pRecordset->PutCollect(&t,_variant_t(strDate)); //更新第七个字段 也可以用字段名的指定
m_pRecordset->Update();
m_pRecordset->Close();
m_pRecordset = NULL;
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
'连接EXCEL表需注意事项:
'例HDR=NO 首行是否为字段名,IMEX 表示是否强制转换为文本,IMEX=1时解决数字与字符混合时,识别不正常的情况
'HDR,是否令第一行为字段名,默认为YES;IMEX,以文本方式读数据
'IMEX很关键,默认的IMEX为0,这个东西很恶心,自认为很聪明的把数字和字符串给分开了
'比如有一列数据
'sss,
'212,
'333,
'abc
'当读到sss时会认为该列为字符串,然后读到212时判断为数字,类型不匹配就过滤掉了,结果从Excel表中读数据的时候就造成了很多丢失。