人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當前位置:蘿卜系統(tǒng)下載站 > 技術(shù)開發(fā)教程 > 詳細頁面

VC++程序員應當如何瀏覽ADO文檔

VC++程序員應當如何瀏覽ADO文檔

更新時間:2022-09-07 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

《ADO API參考》用VB的語法描述了ADO API的內(nèi)容。但ADO程序員卻使用著不同的編程語言,比如VB,VC++,VJ++。對此《ADO for VC++的語法索引》提供了符合VC++語法規(guī)范的詳細描述,包括功能、參數(shù)、異常處理等等。

ADO基于若干的COM借口實現(xiàn),因此它的使用對于一個正進行COM編程的程序員而言更簡單。比如,幾乎所有使用COM的細節(jié)對于VB程序員而言都是隱藏了的,但對于VC++程序員而言卻要特別注意。以下是對于C和C++程序員使用ADO和#import指示符方面的概述,主要描述了COM使用的數(shù)據(jù)類型(Variant, BSTR, and SafeArray)和異常的處理(_com_error)。



使用#import編譯指示符

#import編譯指示符使使用ADO的方法與屬性簡單化。這個指示符需要一個類型庫文件名,比如ADO.dll(Msado15.dll),并生成對應的頭文件,其中包括定義的類型、接口的智能化指針、常量。并且所有的接口都被封裝成類。

對于類中的每個操作(或稱方法、屬性調(diào)用),都有一個聲明以保證能直接調(diào)用它(或稱作操作的源形式),以及另一個聲明來調(diào)用這個源操作并在操作失敗時拋出一個COM錯誤。如果操作是一個屬性,那么編譯指示符可以為該操作創(chuàng)建一個可交互的類似VB的語法形式。

返回/設(shè)置屬性的操作有對應的形式化的名字—GetProperty/PutPropert,而設(shè)置一個指向某個ADO對象的指針型屬性值時則是PutRefProperty。你將使用如下的形式讀寫屬性的值:

variable = objectPtr->GetProperty(); // 讀取屬性的值

objectPtr->PutProperty(value);       // 設(shè)置屬性的值

objectPtr->PutRefProperty(&value);   // 設(shè)置一個指針型的屬性的值



直接使用屬性

__declspec(property...)編譯指示符是微軟定義的一個針對C語言的擴展,使一個函數(shù)象一個屬性那樣被使用。這樣你就可以采用如下的語法形式象在使用VB一樣讀寫一個屬性的值: objectPtr->property = value;        // 設(shè)置屬性的值

variable = objectPtr->property;     // 讀取屬性的值

__declspec(property...)編譯指示符只能針對屬性的讀寫函數(shù)使用,并根據(jù)屬性是否可供讀寫自動生成對應的調(diào)用形式。每個屬性可能有GetProperty, PutProperty,PutRefProperty三個函數(shù),但這個編譯符只能生成其中的兩種交互形式。比如,Command對象的ActiveConnection屬性有GetActiveConnection和PutRefActiveConnection這兩個讀寫函數(shù)。而PutRef-的形式在實踐中是個好的選擇,你可以將一個活動的Connection對象的指針保存在這個屬性中。另一方面,Recordset對象則有Get-, Put-, and PutRefActiveConnection操作,但卻沒有可交互的語法形式。



Collections,GetItem方法和Item屬性

ADO定義了幾種集合Collection,包括Fields,Parameters,Properties,和Errors。在Visual C++中,GetItem(index)方法返回Collection中的某個成員。Index是一個Variant型的參數(shù),內(nèi)容可以是一個該成員對應的序數(shù),也可以是一個包括其名稱的字符串。

__declspec(property...)編譯指示符為Item屬性生成對應于GetItem()方法的直接使用形式(上文提到的可交互的語法形式)。這種形式類似于引用數(shù)組元素時使用[]的語法形式:

collectionPtr->GetItem(index);

collectionPtr->Item[index];

舉例說明,要給一個Recordset對象rs中的某個字段賦值,而這個Recordset對象派生于pubs數(shù)據(jù)庫中的authors表。使用Item()屬性訪問這個Recordset的Fields集合中的第三個字段(集合總是從0開始編號,假設(shè)第三個字段名為au_fname)。然后調(diào)用Value()方法為該字段賦一個字符串值。

Visual Basic的語法形式:

rs.Fields.Item(2).Value = "value"

rs.Fields.Item("au_fname").Value = "value"

或者:

rs(2) = "value"

rs!au_fname = "value"

Visual C++的語法形式:

rs->Fields->GetItem(2)->PutValue("value");

rs->Fields->GetItem("au_fname")->PutValue("value");

或者:

rs->Fields->Item[2]->Value = "value";

rs->Fields->Item["au_fname"]->Value = "value";



COM特定的數(shù)據(jù)類型

一般的,你在《ADO API Reference》中看到的VB的數(shù)據(jù)類型在VC++中也能找到對應的類型。其中包括標準的數(shù)據(jù)類型,比如unsigned char對應VB的Byte,short對應Integer,long對應Long。參見《Syntax Indexes》將可以獲得關(guān)于所需操作數(shù)的更詳細內(nèi)容。

而作為例外的專屬于COM使用的數(shù)據(jù)類型則有:Variant, BSTR, and SafeArray.



Variant

Variant是一個結(jié)構(gòu)化的數(shù)據(jù)類型,包含了一個成員值及其數(shù)據(jù)類型的表示。Variant可以表示相當多的數(shù)據(jù)類型,甚至另一個Variant, BSTR, Boolean, Idispatch或Iunknown指針,貨幣,日期等等。同時COM也提供了許多方法使數(shù)據(jù)類型間的轉(zhuǎn)換更簡單化。

_variant_t類封裝并管理Variant這一數(shù)據(jù)類型。

當《ADO API Reference》中說到一個方法或?qū)傩砸褂靡粋參數(shù)時,通常意味著需要一個_variant_t類型的參數(shù)。這條準則在《ADO API Reference》的Parameters一章中得到了明白無誤的表述。作為例外的是,有時則會要求操作數(shù)是一個標準的數(shù)據(jù)類型,比如Long或Byte, 或者一個枚舉值。另一個例外是要求操作數(shù)是一個字符串String。



BSTR

BSTR (Basic STRing)也是一個結(jié)構(gòu)化的數(shù)據(jù)類型,包括了串及串的長度。COM提供了方法進行串的空間分配、操作、釋放。

_bstr_t類封裝并管理BSTR這一數(shù)據(jù)類型。

當《ADO API Reference》中說到一個方法或?qū)傩砸褂靡粋字符串參數(shù)時,通常意味著需要一個類_bstr_t型的參數(shù)。



_variant_t和_bstr_t類的強制類型轉(zhuǎn)換

通常當傳遞一個_variant_t或_bstr_t參數(shù)給一個操作時并不需要顯式的類型轉(zhuǎn)換代碼。如果_variant_t或_bstr_t類提供了對應于該參數(shù)類型的構(gòu)造函數(shù),那么編譯器將會自動生成適當?shù)腳variant_t或_bstr_t值。

然而,當參數(shù)模棱兩可時,即對應了多個構(gòu)造函數(shù)時,你就必須顯式地調(diào)用正確的構(gòu)造函數(shù)以獲得正確的參數(shù)。比如,Recordset::Open方法的函數(shù)聲明如下:

    HRESULT Open (

        const _variant_t & Source,

        const _variant_t & ActiveConnection,

        enum CursorTypeEnum CursorType,

        enum LockTypeEnum LockType,

        long Options );

其中參數(shù)ActiveConnection就是針對一個variant_t型變量的引用,它可以是一個連接串或者一個指向已打開的Connection對象的指針。

正確的_variant_t型參數(shù)會被構(gòu)造,無論你傳遞的是一個類似"DSN=pubs;uid=sa;pwd=;"這樣的字符串,或者是一個類似"(IDispatch *) pConn"的指針。

或者你還可以顯式的編寫"_variant_t((IDispatch *) pConn, true)"這樣的代碼來傳遞一個包含指針的_variant_t變量。這里的強制類型轉(zhuǎn)換(IDispatch *)避免了可能調(diào)用IUnknown接口構(gòu)造函數(shù)的模棱兩可性。

雖然很少提及但特別重要的是,ADO總是一個IDispatch接口。任何被傳遞的被包含在Variant中的指針都必須被轉(zhuǎn)換為一個IDispatch接口指針。

最后需要說明的是構(gòu)造函數(shù)的第二個邏輯參數(shù)是可選擇的,它的缺省值是True。這個參數(shù)將決定Variant的構(gòu)造函數(shù)是否調(diào)用內(nèi)嵌的AddRef()方法,并在完成ADO的方法或?qū)傩哉{(diào)用后是否自動調(diào)用_variant_t::Release()方法



SafeArray

SafeArray也是一種結(jié)構(gòu)化的數(shù)據(jù)類型,包含了一個由其它數(shù)據(jù)類型的數(shù)據(jù)元素組成的數(shù)組。之所以稱之為安全的數(shù)組是因為它包含了每一維的邊界信息,并限制在邊界內(nèi)進行數(shù)組元素的訪問。

當《ADO API Reference》中說到一個方法或?qū)傩砸褂没蛘叻祷匾粋數(shù)組時,通常意味著是一個SafeArray數(shù)組,而非一個本地化的C/C++數(shù)組。

比如,Connection對象的OpenSchema方法的第二個參數(shù)需要一個由Variant值組成的數(shù)組。這些Variant值必須作為一個SafeArray數(shù)組的元素進行傳遞。而這個SafeArray數(shù)組本身又被作為一個Variant進行傳遞。

更進一步的,F(xiàn)ind方法的第一個參數(shù)是一個指向一維SafeArray數(shù)組的Variant;AddNew方法的可選的第一與第二個參數(shù)也是一個一維的SafeArray數(shù)組;GetRows方法的返回值則是一個包含二維SafeArray數(shù)組的Variant。



缺省參數(shù)

VB允許省略方法的某些參數(shù)。例如,Recordset對象的Open方法有五個參數(shù),但是你可以跳過中間的參數(shù)并省略之后的參數(shù)。被省略的參數(shù)會被自動創(chuàng)建的BSTR或Variant缺省值替代。

在C/C++中,所有的操作數(shù)必須被明確。如果你想定義一個字符串型的缺省參數(shù),那么就定義一個包含空字符串的_bstr_t。如果想定義一個Variant型的缺省參數(shù),那么就定義一個值為DISP_E_PARAMNOTFOUND、類型為VT_ERROR的_variant_t。你還可以使用#import編譯指示符提供的與之等價的常量vtMissing。

vtMissing的使用有三種意外情形:Connection與Command對象的Execute方法,Recordset對象的NextRecordset方法。

_RecordsetPtr Execute( _bstr_t CommandText, VARIANT * RecordsAffected,

        long Options );  // Connection

_RecordsetPtr Execute( VARIANT * RecordsAffected, VARIANT * Parameters,

        long Options );  // Command

_RecordsetPtr NextRecordset( VARIANT * RecordsAffected );  // Recordset

參數(shù)RecordsAffected與Parameters都是指向Variant的指針。Parameters是一個傳入?yún)?shù),指向一個包含一個或一組參數(shù)信息的Variant的地址,將決定命令執(zhí)行的內(nèi)容。RecordsAffected是一個傳出參數(shù),指向一個包含該方法返回時影響行的數(shù)目的Variant的地址。

在Command對象的Execute方法中,如果只是沒有參數(shù)的話,需要將Parameters設(shè)置為&vtMissing (推薦使用)或者一個空指針(NULL)。如果傳遞的是一個空指針,那么等價的vtMissing會被傳遞并完成操作。

在所有的方法中,通過設(shè)置RecordsAffected為空指針可以指示不需返回被影響的記錄的數(shù)目。此時,這個空指針實際成為了指示該方法拋棄被影響記錄數(shù)目的指示器。

因此,如下的編碼是有效的:

pConnection->Execute("commandText", NULL, adCmdText);

pCommand->Execute(NULL, NULL, adCmdText);

pRecordset->NextRecordset(NULL);



錯誤的處理

在COM中,大多數(shù)的操作總是返回一個HRESULT值說明該函數(shù)是否被成功完成。編譯指示符#import為所有源方法和屬性提供了封裝好的代碼并檢查返回的HRESULT值。如果HRESULT指示失敗,這些封裝代碼將會通過調(diào)用以HRESULT為參數(shù)的_com_issue_errorex()拋出一個COM錯誤。COM錯誤對象將在try-catch塊中被捕獲(出于效率的考慮,實際捕獲的是一個_com_error對象的引用指針)。

記住,由ADO操作失敗產(chǎn)生的錯誤才是ADO錯誤。由下層提供者返回的錯誤以Connection對象中Errors集合中的一個Error對象的形式出現(xiàn)。

編譯指示符#import只能為在ADO.dll中聲明的方法和屬性提供錯誤處理例程。因此,你可以基于同樣的錯誤處理機制編寫自己的錯誤檢查宏或內(nèi)置函數(shù)。參見《Visual C++擴展》以及本文后續(xù)的示例代碼。



在VC++與VB中編碼時的約定

下面是ADO文檔中關(guān)于如何使用VB和VC++編寫代碼的一個概覽。



聲明一個ADO對象

在VB中,一個ADO對象變量(此處以Recordset對象為例)如下聲明:

Dim rst As ADODB.Recordset

子句"ADODB.Recordset"是在注冊表中登記的Recordset對象的ProgID。而一個Record對象的實例如下聲明: Dim rst As New ADODB.Recordset

或者:

Dim rst As ADODB.Recordset

Set rst = New ADODB.Recordset

而在VC++中,#import為所有的ADO對象生成了智能的指針類型。比如一個指向_Recordset對象的指針變量的數(shù)據(jù)類型為_RecordsetPtr,并如下聲明:

_RecordsetPtr  rs;

而一個_Recordset對象的實例則如下聲明:

_RecordsetPtr  rs("ADODB.Recordset");

或者:

_RecordsetPtr  rs;

rs.CreateInstance("ADODB.Recordset");

或者:

_RecordsetPtr  rs;

rs.CreateInstance(__uuidof(_Recordset));

當CreateInstance方法被成功調(diào)用后,該變量可被如此使用:rs->Open(...);

注意,如果變量是一個類的實例則用"."操作符,若是一個指向?qū)嵗闹羔槃t應使用"->"操作符。

一個變量能通過兩種方式被使用。因為"->"操作符被重載,允許一個對象實例類似一個接口指針那樣被使用;"->"操作符返回該指針;而由這個返回的指針訪問_Recordset對象的成員。



編寫省略String參數(shù)的代碼

當你需要利用VB編寫省略String參數(shù)的代碼時,只需簡單的略掉該操作數(shù)即可。但在VC++中,你必須指定該操作數(shù)為一個包含空字符串的_bstr_t變量:_bstr_t strMissing(L"");



編寫省略Variant參數(shù)的代碼

當你需要利用VB編寫省略Variant參數(shù)的代碼時,只需簡單的略掉該操作數(shù)即可。但在VC++中,你必須指定所有的操作數(shù)。編寫省略Variant參數(shù)的代碼只需將該Variant設(shè)為專門的值,可以定義一個值為DISP_E_PARAMNOTFOUND、類型為VT_ERROR的_variant_t。還可以使用#import編譯指示符提供的與之等價的常量vtMissing。

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);

或者:

...vtMissing...;



聲明一個Variant

在VB中,一個Variant如下被聲明:

Dim VariableName As Variant

在VC++中,定義一個_variant_t型的變量即可。主要有以下幾種形式。注意:這些聲明只是你在變成時刻采用的一個粗略的思路。

_variant_t  VariableName(value);

_variant_t  VariableName((data type cast) value);

_variant_t  VariableName(value, VT_DATATYPE);

_variant_t  VariableName(interface * value, bool fAddRef = true);



使用Variants數(shù)組

在VB中,利用Dim語句可以進行Variant數(shù)組的編程,并可以使用Array的函數(shù)。見如下示例:

Public Sub ArrayOfVariants

Dim cn As ADODB.Connection

Dim rs As ADODB.Recordset

Dim fld As ADODB.Field



cn.Open "DSN=pubs", "sa", ""

rs = cn.OpenSchema(adSchemaColumns, _

                     Array(Empty, Empty, "authors", Empty))

For Each fld in rs.Fields

   Debug.Print "Name = "; fld.Name

Next fld

rs.Close

cn.Close

End Sub

以下的代碼演示了如何通過一個_variant_t使用一個SafeArray數(shù)組。注意注釋對應了編碼的步驟。

1.再一次的,TESTHR()內(nèi)置函數(shù)被定義以利用預存的錯誤處理機制。

2.如果你只需要一個一維數(shù)組,你可以使用SafeArrayCreateVector,而非SAFEARRAYBOUND聲明與SafeArrayCreate函數(shù)。下面的代碼使用了SafeArrayCreate:

   SAFEARRAYBOUND   sabound[1];

   sabound[0].lLbound = 0;

   sabound[0].cElements = 4;

   pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);

3.枚舉常量adSchemaColumns定義的模式,決定了與TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME和COLUMN_NAME四列相聯(lián)系。為此,一個有四個Variant元素的數(shù)組被創(chuàng)建。而對應于第三列TABLE_NAME的值被設(shè)置。

由若干列組成的返回的Recordset只是對應的所有列的一個子集,并且每一行的值保持了一一對應。

4.熟悉SafeArrays的人也許會對退出前沒有調(diào)用SafeArrayDestroy()感到驚奇。實際上,在這種情況下調(diào)用SafeArrayDestroy()會導致一個運行時的異常發(fā)生。這是因為vtCriteria的析構(gòu)函數(shù)會在_variant_t超出使用范圍時調(diào)用VariantClear(),從而釋放SafeArray。只調(diào)用SafeArrayDestroy,而沒有手動清除_variant_t,將會導致析構(gòu)函數(shù)試圖去清除一個無效的SafeArray指針。如果要調(diào)用SafeArrayDestroy(),那么代碼應該象這樣:

   TESTHR(SafeArrayDestroy(pSa));

   vtCriteria.vt = VT_EMPTY;

   vtCriteria.parray = NULL;

實際更像是讓_variant_t管理SafeArray。



完整的代碼如下:

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \

   no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>



// Note 1

inline void TESTHR( HRESULT _hr )

   { if FAILED(_hr) _com_issue_error(_hr); }



void main(void)

{

   CoInitialize(NULL);

   try

   {

   _RecordsetPtr   pRs("ADODB.Recordset");

   _ConnectionPtr  pCn("ADODB.Connection");

   _variant_t      vtTableName("authors"),

                   vtCriteria;

   long            ix[1];

   SAFEARRAY       *pSa = NULL;



   pCn->Open("DSN=pubs;User ID=sa;pwd=;Provider=MSDASQL;", "", "",

               adConnectUnspecified);

// Note 2, Note 3

   pSa = SafeArrayCreateVector(VT_VARIANT, 1, 4);

   if (!pSa) _com_issue_error(E_OUTOFMEMORY);



// 為第三個元素賦值TABLE_NAME(索引值2).

   ix[0] = 2;      

   TESTHR(SafeArrayPutElement(pSa, ix, &vtTableName));



// 由于Variant沒有SafeArray的構(gòu)造函數(shù),所以手工設(shè)置Variant的數(shù)據(jù)類型和值。

   vtCriteria.vt = VT_ARRAY | VT_VARIANT;

   vtCriteria.parray = pSa;



   pRs = pCn->OpenSchema(adSchemaColumns, vtCriteria, vtMissing);



   long limit = pRs->GetFields()->Count;

   for (long x = 0; x < limit; x++)

      printf("%d: %s\n", x+1,

         ((char*) pRs->GetFields()->Item[x]->Name));

// Note 4

   pRs->Close();

   pCn->Close();

   }

   catch (_com_error &e)

   {

   printf("Error:\n");

   printf("Code = %08lx\n", e.Error());

   printf("Code meaning = %s\n", (char*) e.ErrorMessage());

   printf("Source = %s\n", (char*) e.Source());

   printf("Description = %s\n", (char*) e.Description());

   }

   CoUninitialize();

}



使用屬性的Get/Put/PutRef

在VB中,屬性的名稱并未被檢驗,無論它是被讀取、被賦值,或者賦予一個引用。

Public Sub GetPutPutRef

Dim rs As New ADODB.Recordset

Dim cn As New ADODB.Connection

Dim sz as Integer

cn.Open "Provider=sqloledb;Data Source=yourserver;" & _

         "Initial Catalog=pubs;User Id=sa;Password=;"

rs.PageSize = 10

sz = rs.PageSize

rs.ActiveConnection = cn

rs.Open "authors",,adOpenStatic

' ...

rs.Close

cn.Close

End Sub



以下是VC++關(guān)于Get/Put/PutRefProperty的演示

1.這個例子演示了省略字符串參數(shù)的兩種形式:一種是采用常量strMissing,另一種則是由編譯器自動生成一個臨時的存在于Open方法使用期間的_bstr_t。

2.因為操作數(shù)已經(jīng)是(IDispatch *)的指針,所以沒有必要將rs->PutRefActiveConnection(cn)的操作數(shù)再進行類型轉(zhuǎn)換。

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \

   no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>



void main(void)

{

   CoInitialize(NULL);

   try

   {

      _ConnectionPtr  cn("ADODB.Connection");

      _RecordsetPtr   rs("ADODB.Recordset");

      _bstr_t         strMissing(L"");

      long            oldPgSz = 0,

                      newPgSz = 5;



// Note 1

      cn->Open("Provider=sqloledb;Data Source=a-tima10;"

         "Initial Catalog=pubs;User Id=sa;Password=;",

         strMissing, "",

         adConnectUnspecified);

   

      oldPgSz = rs->GetPageSize();

   // -or-

      oldPgSz = rs->PageSize;



      rs->PutPageSize(newPgSz);

   // -or-

      rs->PageSize = newPgSz;



// Note 2

      rs->PutRefActiveConnection( cn );

      rs->Open("authors", vtMissing, adOpenStatic, adLockReadOnly,

               adCmdTable);

      printf("Original pagesize = %d, new pagesize = %d\n", oldPgSz,

               rs->GetPageSize());

      rs->Close();

      cn->Close();

   }

   catch (_com_error &e)

   {

      printf("Description = %s\n", (char*) e.Description());

   }

   ::CoUninitialize();

}



使用GetItem(x)和Item[x]

下面是VB中關(guān)于Item()的標準與交互語法的演示。

Public Sub GetItemItem

Dim rs As New ADODB.Recordset

Dim name as String

rs = rs.Open "authors", "DSN=pubs;", adOpenDynamic, _

         adLockBatchOptimistic, adTable

name = rs(0)

' -or-

name = rs.Fields.Item(0)

rs(0) = "Test"

rs.UpdateBatch

' Restore name

rs(0) = name

rs.UpdateBatch

rs.Close

End Sub



以下則是VC++關(guān)于Item的演示

當訪問collection中的Item時,索引值2必須被轉(zhuǎn)換為long類型以確保正確的構(gòu)造函數(shù)被調(diào)用。

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \

   no_namespace rename("EOF", "EndOfFile")

#include <stdio.h>



void main(void)

{

   CoInitialize(NULL);

   try {

      _RecordsetPtr   rs("ADODB.Recordset");

      _variant_t      vtFirstName;



      rs->Open("authors",

               "Provider=sqloledb;Data Source=a-tima10;"

               "Initial Catalog=pubs;User Id=sa;Password=;",

               adOpenStatic, adLockOptimistic, adCmdTable);

      rs->MoveFirst();



// Note 1.取得一個字段的名稱

      vtFirstName = rs->Fields->GetItem((long)2)->GetValue();

   // -or-

      vtFirstName = rs->Fields->Item[(long)2]->Value;



      printf( "First name = '%s'\n", (char*) ((_bstr_t) vtFirstName));



      rs->Fields->GetItem((long)2)->Value = L"TEST";

      rs->Update(vtMissing, vtMissing);



   // 恢復原名稱

      rs->Fields->GetItem((long)2)->PutValue(vtFirstName);

      // -or-

      rs->Fields->GetItem((long)2)->Value = vtFirstName;

      rs->Update(vtMissing, vtMissing);

      rs->Close();

   }

   catch (_com_error &e)

   {

      printf("Description = '%s'\n", (char*) e.Description());

   }

   ::CoUninitialize();

}



利用(IDispatch *)轉(zhuǎn)換ADO對象的指針類型

1.在一個Variant中顯式地封裝一個活動的Connection對象,然后用(IDispatch *)進行類型轉(zhuǎn)換確保正確的構(gòu)造函數(shù)被調(diào)用。同時明確地設(shè)置第二個參數(shù)為缺省的true,使該對象的引用計數(shù)在Recordset::Open操作完成后仍得到正確的維護。

2.表達式(_bstr_t)不是一個類型轉(zhuǎn)換,而是一個_variant_t的操作符,用以從中提取一個_bstr_t字符串。

表達式(char*)也不是一個類型轉(zhuǎn)換,而是一個_bstr_t的操作符,用以從中提取封裝在_bstr_t中的字符串的指針。

下面這些代碼演示了_variant_t和_bstr_t的一些常見操作。

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \

no_namespace rename("EOF", "EndOfFile")



#include <stdio.h>



void main(void)

{

   CoInitialize(NULL);

   try

   {

      _ConnectionPtr pConn("ADODB.Connection");

      _RecordsetPtr  pRst("ADODB.Recordset");



      pConn->Open("Provider=sqloledb;Data Source=a-tima10;"

         "Initial Catalog=pubs;User Id=sa;Password=;",

         "", "", adConnectUnspecified);

// Note 1

      pRst->Open(

         "authors",

         _variant_t((IDispatch *) pConn, true),

         adOpenStatic,

         adLockReadOnly,

         adCmdTable);

      pRst->MoveLast();

// Note 2

      printf("Last name is '%s %s'\n",

            (char*) ((_bstr_t) pRst->GetFields()->GetItem("au_fname")->GetValue()),

            (char*) ((_bstr_t) pRst->Fields->Item["au_lname"]->Value));



      pRst->Close();

      pConn->Close();

   }

   catch (_com_error &e)

   {

      printf("Description = '%s'\n", (char*) e.Description());

   }   

::CoUninitialize();

}

///////////////////////////////////////////////

VC++對ADO的擴展

///////////////////////////////////////////////

對于VC++程序員而言,每次都要將ADO返回的數(shù)據(jù)轉(zhuǎn)換成一般的C++數(shù)據(jù)類型,接著將數(shù)據(jù)存入一個類或結(jié)構(gòu)總是一件枯燥的事。更討厭的是這也帶來了效率的低下。

因此,ADO提供了一個接口以支持將數(shù)據(jù)直接返回為一個本地化的C/C++數(shù)據(jù)類型而非VARIANT,并提供了一系列的預處理宏來方便使用這些接口。這樣做的結(jié)果是一個復雜的工具可以很輕松的被使用并能獲得很好的性能。

一個普通的C/C++客戶場景是將一個Recordset中的一條記錄綁定到一個包含本地C/C++數(shù)據(jù)類型的C/C++結(jié)構(gòu)或類之上。如果通過Variant傳遞數(shù)據(jù),這意味著要編寫大量的轉(zhuǎn)換代碼,以在VARIANT和C/C++本地類型間進行數(shù)據(jù)轉(zhuǎn)換。VC++對ADO的擴展出現(xiàn)的目的就是要簡化這一過程。



如何使用VC++對ADO的擴展



IADORecordBinding接口

VC++對ADO的擴展聯(lián)系或綁定了一個Recordset對象的各個字段到C/C++變量。當被綁定的Recordset的當前行改變時,其中所有被綁定的字段的值也同樣會被拷貝到相應的C/C++變量中。如果需要,被拷貝的數(shù)據(jù)還會自動進行相應的數(shù)據(jù)類型轉(zhuǎn)換。

IADORecordBinding接口的BindToRecordset方法將字段綁定到C/C++變量之上。AddNew方法則是增加一個新的行到被綁定的Recordset。Update方法利用C/C++變量的值填充Recordset中新的行或更新已存在的行。

IADORecordBinding接口由Recordset對象實現(xiàn),你不需要自己編碼進行實現(xiàn)。



綁定條目

VC++對ADO的擴展在一個Recordset對象與一個C/C++變量間進行映像(Map)。一個字段與對應的一個變量間的映像被稱作一個綁定條目。預定義的宏為數(shù)字、定長或不定長數(shù)據(jù)提供了綁定條目。所有的綁定條目與相應的C/C++變量都被封裝、聲明在一個從VC++擴展類CADORecordBinding派生的類中。這個CADORecordBinding類在內(nèi)部由綁定條目宏定義。

在ADO內(nèi)部,將所有宏的參數(shù)都映射在一個OLE DB DBBINDING結(jié)構(gòu)中,并創(chuàng)建一個OLE DB訪問子(Accessor)對象來管理所有的行為和字段與變量間的數(shù)據(jù)轉(zhuǎn)換。OLE DB定義的數(shù)據(jù)由以下三部分組成:存儲數(shù)據(jù)的緩沖區(qū);一個狀態(tài)值表示一個字段是否被成功地被存入緩沖區(qū),或變量值是否被成功地存入字段;數(shù)據(jù)長度。(參見OLE DB程序員參考第6章:讀寫數(shù)據(jù)的更多信息)



所需的頭文件

為了使用VC++對ADO的擴展,你得在你的應用中包含這個頭文件:#include <icrsint.h>



綁定Recordset的字段

要綁定Recordset的字段到C/C++變量,需要這樣做:

1.創(chuàng)建一個CADORecordsetBinding的派生類。

2.在派生類中定義綁定條目和相應的C/C++變量。注意不要使用逗號、分號切斷宏。每個宏都會自動地定義適當?shù)姆指舴?br>
為每個被映像的字段定義一個綁定條目。并注意根據(jù)不同情況選用ADO_FIXED_LENGTH_ENTRY、 ADO_NUMERIC_ENTRY、ADO_VARIABLE_LENGTH_ENTRY中的某個宏。

3.在你的應用中,創(chuàng)建一個該派生類的實例。從Recordset中獲得IADORecordBinding接口,然后調(diào)用BindToRecordset方法將Recordset的所有字段綁定到對應的C/C++變量之上。

請參見示例程序以獲得更多信息。



接口方法

IADORecordBinding接口只有三個方法:BindToRecordset, AddNew,和Update。每個方法所需的唯一的參數(shù)就是一個CADORecordBinding派生類的實例指針。因此,AddNew和Update方法不能使用任何與它們同名的ADO方法中的參數(shù)。



語法

BindToRecordset方法將字段綁定到C/C++變量之上。

BindToRecordset(CADORecordBinding *binding)

AddNew方法則引用了它的同名ADO函數(shù),來增加一個新的記錄行。

AddNew(CADORecordBinding *binding)

Update方法也引用了它的同名ADO函數(shù),來更新Recordset。

Update(CADORecordBinding *binding)



綁定條目宏

綁定條目宏定義了一個Recordset字段與一個變量間的對應關(guān)系。每個條目的綁定宏由開始宏與結(jié)束宏組成并配對使用。

定長數(shù)據(jù)的宏適用于adDate,adBoolean等,數(shù)字的宏適用于adTinyInt, adInteger和adDouble等,變長數(shù)據(jù)的宏適用于adChar, adVarChar和adVarBinary等。所有的數(shù)字類型,除了adVarNumeric以外也是定長數(shù)據(jù)類型。每個宏的族之間都有不同的參數(shù)組,因此你可以排除不感興趣的綁定信息。

參見OLE DB程序員參考附錄A:數(shù)據(jù)類型的更多信息



開始綁定條目

BEGIN_ADO_BINDING(Class)



定長數(shù)據(jù):

ADO_FIXED_LENGTH_ENTRY(Ordinal, DataType, Buffer, Status, Modify)

ADO_FIXED_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Modify)

數(shù)字型數(shù)據(jù):

ADO_NUMERIC_ENTRY(Ordinal, DataType, Buffer, Precision, Scale, Status,                                   Modify)

ADO_NUMERIC_ENTRY2(Ordinal, DataType, Buffer, Precision, Scale, Modify)

變長數(shù)據(jù):

ADO_VARIABLE_LENGTH_ENTRY(Ordinal, DataType, Buffer, Size, Status,                                                       Length, Modify)

ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status,                                                       Modify)

ADO_VARIABLE_LENGTH_ENTRY3(Ordinal, DataType, Buffer, Size, Length,                                                       Modify)

ADO_VARIABLE_LENGTH_ENTRY4(Ordinal, DataType, Buffer, Size, Modify)



結(jié)束綁定

END_ADO_BINDING()

參數(shù)
描述

Class
派生類的名字。

Ordinal
從1開始的序號,對應于Recordset中的字段。

DataType
與C/C++變量對應的ADO數(shù)據(jù)類型(參見DataTypeEnum以獲得有效數(shù)據(jù)類型的列表)。如果需要,字段的值會被轉(zhuǎn)換成該類型的值。

Buffer
對應的C/C++變量的名字。

Size
該C/C++變量的最大字節(jié)數(shù)。如果是個變長字符串,使用0表示即可。

Status
指示變量的名字。該變量用以表示緩沖是否有效,數(shù)據(jù)轉(zhuǎn)換是否成功。

值adFldOK意味著轉(zhuǎn)換成功;adFldNull意味著該字段的值為空。其他可能的值見后面的狀態(tài)值列表。

Modify
邏輯標志。TRUE意味著ADO允許利用變量值更新Recordset中的字段的值。

設(shè)置該值為TRUE將允許更新,如果你只想檢查字段的值而不想改變它那么就設(shè)置為FALSE。

Precision
數(shù)字型變量的位數(shù)。

Scale
數(shù)字型變量的小數(shù)位數(shù)。

Length
一個4字節(jié)變量的名字。該變量將包含緩沖區(qū)中數(shù)據(jù)的實際長度。




狀態(tài)值

變量Status的值指示了一個字段的值是否被成功的拷貝到了對應的變量中。寫數(shù)據(jù)時,可以給Status賦值為adFldNull來指示該字段將被設(shè)置為null。

常量

描述

adFldOK
0
一個非空的字段值被返回。

adFldBadAccessor
1
綁定無效。

adFldCantConvertValue
2
值因為符號不匹配或超界外的原因?qū)е聼o法被正確轉(zhuǎn)換。

adFldNull
3
讀字段值時,指示一個空值被返回。寫字段值時,指示當字段自身無法編碼NULL時該字段將被設(shè)置為NULL。

adFldTruncated
4
變長數(shù)據(jù)或數(shù)字被截斷。

adFldSignMismatch
5
值是有符號數(shù),而數(shù)據(jù)類型是無符號數(shù)。

adFldDataOverFlow
6
數(shù)據(jù)值超出界限。

adFldCantCreate
7
不知名的列類型和字段已經(jīng)被打開。

adFldUnavailable
8
字段值無法確定。比如一個新的未賦值的無缺省值的字段。

adFldPermissionDenied
9
未被允許更新數(shù)據(jù)。

adFldIntegrityViolation
10
更新字段時值違反了列的完整性要求。

adFldSchemaViolation
11
更新字段時值違反了列的規(guī)范要求。

adFldBadStatus
12
更新字段時,無效的狀態(tài)參數(shù)。

adFldDefault
13
更新字段時,使用缺省值。




使用VC++對ADO的擴展的示例

在這個例子中,還使用了COM專有的“智能指針”功能,它能自動處理IADORecordBinding接口的QueryInterface和引用計數(shù)。如果沒有智能指針,你得這樣編碼:

IADORecordBinding   *picRs = NULL;

...

TESTHR(pRs->QueryInterface(

          __uuidof(IADORecordBinding), (LPVOID*)&picRs));

...

if (picRs) picRs->Release();

使用智能指針,你可以用這樣的語句從IADORecordBinding接口派生IADORecordBindingPtr類型:

_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));

然后這樣實例化指針:

IADORecordBindingPtr picRs(pRs);

因為VC++的擴展由Recordset對象實現(xiàn),因此智能指針picRs的構(gòu)造函數(shù)使用了_RecordsetPtr類指針pRs。構(gòu)造函數(shù)利用pRs調(diào)用QueryInterface來獲得IADORecordBinding接口。



// 以下即是示例程序

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \

   no_namespace rename("EOF", "EndOfFile")



#include <stdio.h>

#include <icrsint.h>

_COM_SMARTPTR_TYPEDEF(IADORecordBinding, __uuidof(IADORecordBinding));



inline void TESTHR(HRESULT _hr) { if FAILED(_hr) _com_issue_error(_hr); }



class CCustomRs : public CADORecordBinding

{

BEGIN_ADO_BINDING(CCustomRs)

   ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_ch_fname,

                        sizeof(m_ch_fname), m_ul_fnameStatus, false)

   ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_ch_lname,

                        sizeof(m_ch_lname), m_ul_lnameStatus, false)

END_ADO_BINDING()

public:

   CHAR    m_ch_fname[22];

   CHAR    m_ch_lname[32];

   ULONG   m_ul_fnameStatus;

   ULONG   m_ul_lnameStatus;

};



void main(void)

{

   ::CoInitialize(NULL);

   try

      {

      _RecordsetPtr pRs("ADODB.Recordset");

      CCustomRs rs;

      IADORecordBindingPtr picRs(pRs);

      

      pRs->Open("SELECT * FROM Employee ORDER BY lname",

         "dsn=pubs;uid=sa;pwd=;",

         adOpenStatic, adLockOptimistic, adCmdText);

      

      TESTHR(picRs->BindToRecordset(&rs));



      while (!pRs->EndOfFile)

         {

      // 處理CCustomRs中的數(shù)據(jù)

         printf("Name = %s %s\n",

            (rs.m_ul_fnameStatus == adFldOK ? rs.m_ch_fname: "<Error>"),

            (rs.m_ul_lnameStatus == adFldOK ? rs.m_ch_lname: "<Error>"));



      // 移動到下一行,新行的值會被自動填充到對應的CCustomRs的變量中

         pRs->MoveNext();

         }

      }

   catch (_com_error &e )

      {

      printf("Error:\n");

      printf("Code = %08lx\n", e.Error());

      printf("Meaning = %s\n", e.ErrorMessage());

      printf("Source = %s\n", (LPCSTR) e.Source());

      printf("Description = %s\n", (LPCSTR) e.Description());

      }

   ::CoUninitialize();

}

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

網(wǎng)站地圖xml | 網(wǎng)站地圖html
主站蜘蛛池模板: 淄博市| 澄江县| 禹城市| 龙游县| 汉寿县| 沧源| 邛崃市| 安图县| 嘉善县| 边坝县| 华阴市| 个旧市| 莎车县| 临西县| 镇宁| 南江县| 民县| 绥化市| 沙雅县| 建始县| 农安县| 广汉市| 兴和县| 思茅市| 布尔津县| 手游| 永福县| 南汇区| 五指山市| 北票市| 林口县| 彭山县| 德格县| 和平县| 子长县| 泗洪县| 沂南县| 临朐县| 东丰县| 礼泉县| 周宁县|