| 本文描述了如何在MFC的文檔/視圖/框架架構(gòu)中使用ADO和ADOX來創(chuàng)建和打開數(shù)據(jù)庫。 預(yù)備閱讀 在閱讀本文之前,建議先對COM,數(shù)據(jù)庫和MFC的文檔/視圖/框架有一個基本的了解。推薦閱讀下列文章 MFC技術(shù)文章 微軟知識庫文章 Office VBA參考 步驟 - 在計算機(jī)上安裝MDAC2.5以上版本
- 打開VC。首先,我們使用MFC應(yīng)用程序向?qū)?chuàng)建一個標(biāo)準(zhǔn)的MDI程序,這里我為這個工程起名為Passport,然后在stdafx.h中導(dǎo)入ADOX
#include <shlwapi.h> #import "c:\Program Files\Common Files\system\ado\Msado15.dll" rename("EOF","adoEOF") rename("DataTypeEnum","adoDataTypeEnum") #import "c:\Program Files\Common Files\System\ADO\Msadox.dll" rename("EOF", "adoXEOF") rename("DataTypeEnum","adoXDataTypeEnum") #import "c:\PROGRAM FILES\COMMON FILES\System\ado\MSJRO.DLL"
根據(jù)你的計算機(jī)上ADO的安裝路徑,這里的路徑可能有所不同。 - 在文檔類中聲明數(shù)據(jù)庫連接 ADODB::_ConnectionPtr m_pConn;和記錄集 ADODB::_RecordsetPtr m_pSet;,并且重載文檔類的DeleteContents() 、OnNewDocument()和OnOpenDocument()函數(shù),用于斷開數(shù)據(jù)庫連接,創(chuàng)建數(shù)據(jù)庫和表,以及打開現(xiàn)有的數(shù)據(jù)庫。
(作者的抱怨:CSDN文章中心該改改了,代碼排版這么麻煩) void CPassportDoc::DeleteContents() { try { if(m_pSet){ ESRecordsetClose(m_pSet); } if(m_pConn) if(m_pConn->State&ADODB::adStateOpen) m_pConn->Close(); m_pConn=NULL; } catch(_com_error &e){ ESErrPrintProviderError(m_pConn); ESErrPrintComError(e); } CDocument::DeleteContents(); }BOOL CPassportDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; CFileDialog dlgFile(FALSE, _T(".mdb"), NULL, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, _T("Access 數(shù)據(jù)庫 (*.mdb)|*.mdb|全部文件(*.*)|*.*||")); if (dlgFile.DoModal() != IDOK) return FALSE; CString strDBPath=dlgFile.GetPathName(); if(!CreateDB(strDBPath))return FALSE; //create CString strConnect; strConnect.Format(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s"),strDBPath); COleVariant Connect(strConnect); // TODO: add reinitialization code here // (SDI documents will reuse this document) try{ m_pConn.CreateInstance(_T("ADODB.Connection")); m_pSet.CreateInstance(_T("ADODB.Recordset")); m_pConn->PutCommandTimeout(30); m_pConn->PutConnectionTimeout(30); m_pConn->put_CursorLocation(ADODB::adUseClient); m_pConn->Open(_bstr_t(strConnect),_bstr_t(),_bstr_t(),ADODB::adConnectUnspecified); ::ESRecordsetOpen(_T("Passport"),m_pConn,m_pSet); SetPathName(strDBPath); return TRUE; } catch(_com_error &e){ ESErrPrintProviderError(m_pConn); ESErrPrintComError(e); } catch(...){ } m_pConn=NULL; return FALSE; } BOOL CPassportDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; ADODB::_ConnectionPtr tempConnn; CString strConnect; CString strDBPath=lpszPathName; strConnect.Format(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s"),strDBPath); COleVariant Connect(strConnect); // TODO: add reinitialization code here // (SDI documents will reuse this document) try{ tempConnn.CreateInstance(_T("ADODB.Connection")); tempConnn->PutCommandTimeout(30); tempConnn->PutConnectionTimeout(30); tempConnn->put_CursorLocation(ADODB::adUseClient); tempConnn->Open(_bstr_t(strConnect),_bstr_t(),_bstr_t(),ADODB::adConnectUnspecified); SetPathName(strDBPath); m_pConn=tempConnn; m_pSet=NULL; m_pSet.CreateInstance(_T("ADODB.Recordset")); ::ESRecordsetOpen(_T("Passport"),m_pConn,m_pSet); UpdateAllViews(NULL,UpdateHintRefresh); return TRUE; } catch(_com_error &e){ ESErrPrintProviderError(tempConnn); ESErrPrintComError(e); } catch(...){ } return FALSE; } - 編寫一個輔助函數(shù),用于創(chuàng)建數(shù)據(jù)庫、表和索引
BOOL CPassportDoc::CreateDB(LPCTSTR lpszFile) { if(::PathFileExists(lpszFile)){ CString strTemp; strTemp.Format(IDS_TARGET_EXISTS,lpszFile); AfxMessageBox(lpszFile); return FALSE; } ADODB::_ConnectionPtr tempConnn; ADOX::_CatalogPtr pCatalog = NULL; ADOX::_TablePtr pTable = NULL; ADOX::_IndexPtr pIndexNew = NULL; ADOX::_IndexPtr pIndex = NULL; CString strConnect; CString strDBPath=lpszFile; strConnect.Format(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s"),strDBPath); COleVariant Connect(strConnect); try{ pCatalog.CreateInstance(_T("ADOX.Catalog")); pCatalog->Create((LPCTSTR)strConnect);//創(chuàng)建數(shù)據(jù)庫 tempConnn.CreateInstance(_T("ADODB.Connection")); tempConnn->PutCommandTimeout(30); tempConnn->PutConnectionTimeout(30); tempConnn->put_CursorLocation(ADODB::adUseClient); tempConnn->Open(_bstr_t(strConnect),_bstr_t(),_bstr_t(),ADODB::adConnectUnspecified); pCatalog->PutActiveConnection(_variant_t((IDispatch *) tempConnn)); pTable.CreateInstance(_T("ADOX.Table")); pTable->ParentCatalog =pCatalog; pTable->Name="Passport"; ADOX::ColumnsPtr pCols =pTable->Columns; pCols->Append(_T("RecordID") ,ADOX::adInteger,0);//自動編號字段 pCols->Append(_T("Name") ,ADOX::adWChar,255);//文本字段 pCols->Append(_T("DateOfBirth") ,ADOX::adDate,0);//日期字段 pCols->Append(_T("OtherInfo"),ADOX::adLongVarWChar,0);//備注字段 pCatalog->Tables->Refresh(); long lCount=pCols->Count; for(long i=0;i<lCount;i++){ pCols->GetItem(i)->ParentCatalog =pCatalog;//重要!設(shè)置Catalog,參見Q201826 PRB: Error 3265 When You Access Properties Collection ADOX::PropertiesPtr pProperties=pCols->GetItem(i)->Properties; if(pProperties){//這里是用于調(diào)試的屬性顯示代碼 long lp=pProperties->Count; TRACE("Properties for Col %s\r\n",(LPCTSTR)pCols->GetItem(i)->Name); for(long j=0;j<lp;j++){ TRACE("\rProperty %s:%s\r\n",g_GetValueString(pProperties->GetItem(j)->Name) ,g_GetValueString(pProperties->GetItem(j)->Value)); } } } pCols->GetItem(_T("RecordID"))->Properties->GetItem(_T("Description"))->Value=_T("記錄編號");//注釋 pCols->GetItem(_T("RecordID"))->Properties->GetItem(_T("AutoIncrement"))->Value=true;//自動編號 pCols->GetItem(_T("Name"))->Properties->GetItem(_T("Jet OLEDB:Compressed UniCode Strings"))->Value=true; pCols->GetItem(_T("Name"))->Properties->GetItem(_T("Description"))->Value=_T("姓名"); pCols->GetItem(_T("DateOfBirth"))->Properties->GetItem(_T("Description"))->Value=_T("出生日期"); pCols->GetItem(_T("OtherInfo"))->Properties->GetItem(_T("Jet OLEDB:Compressed UniCode Strings"))->Value=true; pCols->GetItem(_T("OtherInfo"))->Properties->GetItem(_T("Description"))->Value=_T("其他信息"); pCatalog->Tables->Append(_variant_t ((IDispatch*)pTable));//添加表 pCatalog->Tables->Refresh();//刷新 pIndexNew.CreateInstance(_T("ADOX.Index")); pIndexNew->Name = "RecordID";//索引名稱 pIndexNew->Columns->Append("RecordID",ADOX::adInteger,0);//索引字段 pIndexNew->PutPrimaryKey(-1);//主索引 pIndexNew->PutUnique(-1);//唯一索引 pTable->Indexes->Append(_variant_t ((IDispatch*)pIndexNew));//創(chuàng)建索引 pIndexNew=NULL; pCatalog->Tables->Refresh();//刷新 return TRUE; } catch(_com_error &e){ ESErrPrintProviderError(tempConnn); ESErrPrintComError(e); return FALSE; } catch(...){ } return FALSE; } - 輔助的數(shù)據(jù)庫函數(shù)。由于這些函數(shù)是Jiangsheng以前為一個項目寫的。所以命名有些奇怪。借鑒了MFC類CDaoRecordset的部分代碼
#define _countof(array) (sizeof(array)/sizeof(array[0])) BOOL ESRecordsetOpen( LPCTSTR lpszSQL ,ADODB::_ConnectionPtr pConnection ,ADODB::_RecordsetPtr& rst ,ADODB::CursorTypeEnum CursorType//=adOpenDynamic ,ADODB::LockTypeEnum LockType//=ado20::adLockOptimistic ,long lOptions//=adCmdUnspecified ) { _bstr_t bstrQuery; const TCHAR _afxParameters2[] = _T("PARAMETERS "); const TCHAR _afxSelect2[] = _T("SELECT "); const TCHAR _afxTransform2[] = _T("TRANSFORM "); const TCHAR _afxTable2[] = _T("TABLE "); // construct the default query string if ((_tcsnicmp(lpszSQL, _afxSelect2, _countof(_afxSelect2)-1) != 0) && (_tcsnicmp(lpszSQL, _afxParameters2, _countof(_afxParameters2)-1) != 0) && (_tcsnicmp(lpszSQL, _afxTransform2, _countof(_afxTransform2)-1) != 0) && (_tcsnicmp(lpszSQL, _afxTable2, _countof(_afxTable2)-1) != 0)){ CString strTemp; strTemp.Format("SELECT * FROM (%s)",lpszSQL); bstrQuery=(LPCTSTR)strTemp; } else bstrQuery=lpszSQL; if(rst!=NULL){ rst->CursorLocation=ADODB::adUseClient; rst->Open(bstrQuery,_variant_t(pConnection.GetInterfacePtr(),true),CursorType,LockType,lOptions); } TRACE("Open Recordset:%s\n",lpszSQL); return ESRecordsetIsOpen(rst); } BOOL ESRecordsetIsOpen(const ADODB::_RecordsetPtr& rst) { if(rst!=NULL){ return rst->State&ADODB::adStateOpen; } return FALSE; } void ESRecordsetClose(ADODB::_RecordsetPtr& rst) { if(rst!=NULL){ if(rst->State&ADODB::adStateOpen) rst->Close(); } } CString g_GetValueString(const _variant_t& val) { CString strVal; _variant_t varDest(val); if(!g_varIsValid(val)){ return strVal; } if(val.vt==VT_BOOL){ if(val.boolVal==VARIANT_FALSE){ return _T("否"); } else return _T("是"); } else{ } if(varDest.vt!=VT_BSTR){ HRESULT hr=::VariantChangeType(&varDest,&varDest,VARIANT_NOUSEROVERRIDE|VARIANT_LOCALBOOL,VT_BSTR); if(FAILED(hr)){ return strVal; } } strVal=(LPCTSTR)_bstr_t(varDest); return strVal; } - 錯誤處理代碼
void ESErrPrintComError(_com_error &e) { _bstr_t bstrSource(e.Source()); _bstr_t bstrDescription(e.Description()); CString strTemp; strTemp.Format(_T("´錯誤\n\t錯誤代碼: %08lx\n\t含義: %s\n\t來自 : %s\n\t描述 : %s\n"), e.Error(),e.ErrorMessage(),(LPCSTR) bstrSource,(LPCSTR) bstrDescription); // Print COM errors. ::AfxMessageBox(strTemp); #ifdef _DEBUG AfxDebugBreak(); #endif } void ESErrPrintProviderError(ADODB::_ConnectionPtr pConnection) { if(pConnection==NULL) return; try{ // Print Provider Errors from Connection object. // pErr is a record object in the Connection's Error collection. ADODB::ErrorPtr pErr = NULL; ADODB::ErrorsPtr pErrors=pConnection->Errors; if(pErrors){ if( (pErrors->Count) > 0){ long nCount = pErrors->Count; // Collection ranges from 0 to nCount -1. for(long i = 0;i < nCount;i++){ pErr = pErrors->GetItem(i); CString strTemp; strTemp.Format(_T("\t 錯誤代碼: %x\t%s"), pErr->Number, pErr->Description); } } } } catch(_com_error &e){ ESErrPrintComError(e); } } 總結(jié) 在文檔/視圖/框架架構(gòu)中集成數(shù)據(jù)庫訪問總體來說還是難度不大的。微軟提供了很多示例的代碼,大部分工作只是把示例代碼從其他語言改寫到VC。主要的工作是對MFC的文檔/視圖/框架架構(gòu)的理解,在適當(dāng)?shù)臅r候調(diào)用這些代碼。 盡管我在打開數(shù)據(jù)庫的同時也打開了一個記錄集,但是我并未給出顯示記錄集內(nèi)容的代碼,這超出了本文的范圍。我可以給出的提示是使用現(xiàn)成的數(shù)據(jù)列表控件來顯示,微軟知識庫文章Q229029 SAMPLE: AdoDataGrid.exe Demonstrates How to Use ADO with DataGrid Control Using Visual C++可以作為參考。 |