Share via


Visual C++-ADO-Programmierung

Die ADO-API-Referenz beschreibt die Funktionalität der ADO-Anwendungsprogrammierschnittstelle (API) anhand einer Syntax, die der von Microsoft Visual Basic ähnelt. Auch wenn die Zielgruppe alle Benutzer sind, verwenden ADO-Programmierer verschiedene Sprachen wie Visual Basic, Visual C++ (mit und ohne die#import-Direktive) und Visual J++ (mit dem ADO/WFC-Klassenpaket).

Hinweis

Microsoft hat die Unterstützung für Visual J++ im Jahr 2004 eingestellt.

Um diese Vielfalt zu ermöglichen, bieten die ADO für Visual C++ Syntax-Indizes eine Visual C++ sprachspezifische Syntax mit Links zu allgemeinen Beschreibungen von Funktionen, Parametern, außergewöhnlichen Verhaltensweisen usw. in der API-Referenz.

ADO ist mit COM-Schnittstellen (Component Object Model) implementiert. Für Programmierer ist es jedoch in bestimmten Programmiersprachen einfacher, mit COM zu arbeiten als in anderen. So werden zum Beispiel fast alle Details der Verwendung von COM für Visual Basic-Programmierer implizit gehandhabt, während Visual C++-Programmierer sich selbst um diese Details kümmern müssen.

Die folgenden Abschnitte fassen die Details für C- und C++-Programmierer zusammen, die ADO und die #import-Anweisung verwenden. Sie konzentriert sich auf Datentypen, die für COM (Variant, BSTR und SafeArray) und die Fehlerbehandlung (_com_error) spezifisch sind.

Verwendung der #import Compiler-Anweisung

Die Compiler-Anweisung #import Visual C++ vereinfacht die Arbeit mit den ADO-Methoden und -Eigenschaften. Die Anweisung nimmt den Namen einer Datei, die eine Typbibliothek enthält, wie z. B. die ADO .dll (Msado15.dll), und generiert Header-Dateien, die Typdeklarationen, Smart Pointer für Schnittstellen und enumerierte Konstanten enthalten. Jede Schnittstelle ist in einer Klasse gekapselt oder verpackt.

Für jede Operation innerhalb einer Klasse (d. h. ein Methoden- oder Eigenschaftsaufruf) gibt es eine Deklaration, um die Operation direkt aufzurufen (d.h. die „rohe“ Form der Operation), und eine Deklaration, um die rohe Operation aufzurufen und einen COM-Fehler zu werfen, wenn die Operation nicht erfolgreich ausgeführt werden kann. Wenn die Operation eine Eigenschaft ist, gibt es in der Regel eine Compiler-Anweisung, die eine alternative Syntax für die Operation erstellt, die eine Syntax wie Visual Basic hat.

Operationen, die den Wert einer Eigenschaft abrufen, haben Namen der Form GetProperty. Operationen, die den Wert einer Eigenschaft festlegen, haben Namen der Form, PutProperty. Operationen, die den Wert einer Eigenschaft mit einem Zeiger auf ein ADO-Objekt setzen, haben Namen der Form, PutRefProperty.

Mit den Aufrufen dieser Formulare können Sie eine Eigenschaft abrufen oder festlegen:

variable = objectPtr->GetProperty(); // get property value   
objectPtr->PutProperty(value);       // set property value  
objectPtr->PutRefProperty(&value);   // set property with object pointer  

Verwendung von Eigenschaftsanweisungen

Die Compiler-Anweisung __declspec(property...) ist eine Microsoft-spezifische C-Spracherweiterung, die eine als Eigenschaft verwendete Funktion mit einer alternativen Syntax deklariert. So können Sie Werte für eine Eigenschaft ähnlich wie in Visual Basic setzen oder abrufen. Auf diese Weise können Sie zum Beispiel eine Eigenschaft setzen und abrufen:

objectPtr->property = value;        // set property value  
variable = objectPtr->property;     // get property value  

Beachten Sie, dass Sie nicht codieren müssen:

objectPtr->PutProperty(value);      // set property value  
variable = objectPtr->GetProperty;  // get property value  

Der Compiler generiert den entsprechenden Get-, Put-, oder PutRef-Eigenschaftsaufruf, je nachdem, welche alternative Syntax deklariert ist und ob die Eigenschaft gelesen oder geschrieben wird.

Die Compiler-Anweisung __declspec(property...) kann nur get, put, oder get und put als alternative Syntax für eine Funktion deklarieren. Schreibgeschützte Operationen haben nur eine get-Deklaration; schreibgeschützte Operationen haben nur eine put-Deklaration; Operationen, die sowohl lesen als auch schreiben können, haben sowohl get- als auch put-Deklarationen.

Mit dieser Anweisung sind nur zwei Deklarationen möglich; jede Eigenschaft kann jedoch drei Eigenschaftsfunktionen haben: Get-Eigenschaft, Put-Eigenschaft, und PutRef-Eigenschaft. In diesem Fall haben nur zwei Formen der Eigenschaft die alternative Syntax.

Zum Beispiel wird die Eigenschaft Command-Objekt ActiveConnection mit einer alternativen Syntax für GetActiveConnection und PutRefActiveConnection deklariert. Die Syntax PutRef - ist eine gute Wahl, denn in der Praxis werden Sie in der Regel ein offenes Connection-Objekt (d. h. einen Zeiger auf ein Connection-Ojekt) in diese Eigenschaft eingeben wollen. Andererseits hat das Recordset-Objekt die Operationen Get-, Put- und PutRefActiveConnection, aber keine alternative Syntax.

Sammlungen, die GetItem-Methode und die Item-Eigenschaft

ADO definiert mehrere Sammlungen, darunter Felder, Parameter, Eigenschaften, und Fehler. In Visual C++ gibt die Methode GetItem(index) ein Element der Sammlung zurück. Index ist eine Variante, deren Wert ein numerischer Index des Elements in der Sammlung oder eine Zeichenfolge mit dem Namen des Elements ist.

Die Compiler-Anweisung __declspec(property...) deklariert die Element-Eigenschaft als alternative Syntax zur grundlegenden GetItem()-Methode der jeweiligen Sammlung. Die alternative Syntax verwendet eckige Klammern und sieht ähnlich aus wie eine Array-Referenz. Im Allgemeinen sehen die beiden Formulare wie folgt aus:

  
      collectionPtr->GetItem(index);  
collectionPtr->Item[index];  

Ordnen Sie zum Beispiel einem Feld eines Recordset-Objekts namensrs, das aus der Tabelle authors der Datenbank pubs stammt, einen Wert zu. Verwenden Sie die Eigenschaft Item(), um das dritte Feld der Fields-Sammlung des Recordset-Objekts aufzurufen (Sammlungen werden von Null an indiziert; nehmen Sie an, das dritte Feld heißt au_fname). Rufen Sie dann die Methode Value() für dasField-Objekt auf, um einen Zeichenfolgenwert zuzuweisen.

Dies kann in Visual Basic auf die folgenden vier Arten ausgedrückt werden (die letzten beiden Formulare sind für Visual Basic eindeutig; andere Sprachen haben keine Entsprechungen):

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

Das Äquivalent in Visual C++ zu den ersten beiden Formularen oben ist:

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

-oder- (die alternative Syntax für die Eigenschaft Value wird ebenfalls angezeigt)

rs->Fields->Item[long(2)]->Value = "value";  
rs->Fields->Item["au_fname"]->Value = "value";  

Für Beispiele zur Iteration durch eine Sammlung siehe den Abschnitt „ADO-Sammlungen“ in der „ADO-Referenz“.

COM-spezifische Datentypen

Im Allgemeinen hat jeder Visual Basic-Datentyp, den Sie in der ADO-API-Referenz finden, eine Visual C++-Entsprechung. Dazu gehören Standard-Datentypen wie unsigned char für ein Visual Basic Byte, short für Integer und long für Long. Sehen Sie im Syntax-Index nach, um genau zu sehen, was für die Operanden einer bestimmten Methode oder Eigenschaft erforderlich ist.

Die Ausnahmen von dieser Regel sind die COM-spezifischen Datentypen: Variant, BSTR, und SafeArray.

Variant

Eine Variant ist ein strukturierter Datentyp, der ein Wert- und ein Datentypelement enthält. Eine Variant kann zahlreiche andere Datentypen enthalten, darunter eine andere Variante, BSTR, Boolean, IDispatch- oder IUnknown-Zeiger, Währung, Datum und so weiter. COM stellt auch Methoden zur Verfügung, mit denen Sie einen Datentyp leicht in einen anderen umwandeln können.

Die Klasse _variant_t kapselt und verwaltet den Datentyp Variant.

Wenn in der ADO-API-Referenz steht, dass ein Methoden- oder Eigenschaftsoperand einen Wert annimmt, bedeutet dies in der Regel, dass der Wert in einem _variant_t übergeben wird.

Die Regel gilt ausdrücklich, wenn im Abschnitt Parameters in den Themen der ADO-API-Referenz steht, dass ein Operand eine Variant ist. Eine Ausnahme ist, wenn die Dokumentation ausdrücklich besagt, dass der Operand einen Standarddatentyp wie Long oder Byte oder eine Enumeration annimmt. Eine weitere Ausnahme ist, wenn der Operand eine Zeichenfolge enthält.

BSTR

Ein BSTR (Basic STRing) ist ein strukturierter Datentyp, der eine Zeichenfolge und die Länge der Zeichenfolge enthält. COM bietet Methoden zum Zuordnen, Manipulieren und Freigeben von BSTR.

Die Klasse _bstr_t kapselt und verwaltet den Datentyp BSTR.

Wenn die ADO-API-Referenz sagt, dass eine Methode oder Eigenschaft einen Zeichenfolgenwert annimmt, bedeutet dies, dass der Wert in Form eines _bstr_t vorliegt.

Casting der Klassen _variant_t und _bstr_t

Oft ist es nicht notwendig, ein _variant_t oder _bstr_t in einem Argument für eine Operation explizit zu codieren. Wenn die Klasse_variant_t oder _bstr_t einen Konstruktor hat, der zum Datentyp des Arguments passt, erzeugt der Compiler den entsprechenden _variant_t oder_bstr_t.

Falls das Argument jedoch mehrdeutig ist, d. h. der Datentyp des Arguments zu mehr als einem Konstruktor passt, müssen Sie das Argument mit dem entsprechenden Datentyp casten, um den richtigen Konstruktor aufzurufen.

Zum Beispiel lautet die Deklaration für die Methode Recordset::Open:

    HRESULT Open (  
        const _variant_t & Source,  
        const _variant_t & ActiveConnection,  
        enum CursorTypeEnum CursorType,  
        enum LockTypeEnum LockType,  
        long Options );  

Das Argument ActiveConnection nimmt einen Verweis auf eine_variant_t, die Sie als Verbindungszeichenfolge oder als Zeiger auf ein offenes Connection-Objekt kodieren können.

Die korrekte _variant_t wird implizit konstruiert, wenn Sie einen String wie "DSN=pubs;uid=MyUserName;pwd=MyPassword;" oder einen Zeiger wie "(IDispatch *) pConn" übergeben.

Hinweis

Wenn Sie eine Verbindung mit einem Datenquellenanbieter herstellen, der Windows-Authentifizierung unterstützt, sollten Sie Trusted_Connection=yes oder Integrated Security = SSPI anstelle von Benutzer-ID- und Kennwortinformationen in der Verbindungszeichenfolge angeben.

Oder Sie kodieren explizit eine _variant_t, die einen Zeiger wie "_variant_t((IDispatch *) pConn, true)" enthält. Der Cast, (IDispatch *), löst die Mehrdeutigkeit mit einem weiteren Konstruktor auf, der einen Zeiger auf eine IUnknown-Schnittstelle annimmt.

Es ist eine wichtige, wenn auch selten erwähnte Tatsache, dass ADO eine IDispatch-Schnittstelle ist. Wann immer ein Zeiger auf ein ADO-Objekt als Variant übergeben werden muss, muss dieser Zeiger als Zeiger auf eine IDispatch-Schnittstelle gecastet werden.

Der letzte Fall kodiert explizit das zweite boolesche Argument des Konstruktors mit seinem optionalen Standardwert true. Mit diesem Argument ruft der Variant-Konstruktor seine AddRef()-Methode auf. Dadurch wird kompensiert, dass ADO automatisch die _variant_t::Release()-Methode aufruft, wenn der Aufruf der ADO-Methode oder -Eigenschaft abgeschlossen ist.

SafeArray

Ein SafeArray ist ein strukturierter Datentyp, der ein Array mit anderen Datentypen enthält. Ein SafeArray wird als sicher bezeichnet, da es Informationen über die Grenzen der einzelnen Array-Dimensionen enthält und den Zugriff auf Array-Elemente innerhalb dieser Grenzen begrenzt.

Wenn die ADO-API-Referenz sagt, dass eine Methode oder Eigenschaft ein Array aufnimmt oder zurückgibt, bedeutet dies, dass die Methode oder Eigenschaft ein SafeArray aufnimmt oder zurückgibt, nicht ein natives C/C++-Array.

Beispielsweise erfordert der zweite Parameter der OpenSchema-Methode des Connection-Objekts ein Array mit Variant-Werten. Diese Variant-Werte müssen als Elemente eines SafeArray übergeben werden, und dieses SafeArray muss als Wert einer anderen Variante festgelegt werden. Es ist, dass andere Variante, die als zweites Argument von OpenSchema übergeben wird.

Weitere Beispiele: Beim ersten Argument der Methode Find handelt es sich um eine Variante, deren Wert ein eindimensionales SafeArray ist; jedes der optionalen ersten und zweiten Argumente von AddNew ist ein eindimensionales SafeArray; und der Rückgabewert der Methode GetRows ist eine Variante, deren Wert ein zweidimensionales SafeArray ist.

Fehlende und Standardparameter

Visual Basic ermöglicht fehlende Parameter in Methoden. Die Methode Open des Recordset-Objekts hat beispielsweise fünf Parameter, doch Sie können Zwischenparameter auslassen und Parameter am Ende weglassen. Ein Standard-BSTR oder Variante wird je nach Datentyp des fehlenden Operanden ersetzt.

In C/C++ müssen alle Operanden angegeben werden. Wenn Sie einen fehlenden Parameter angeben möchten, dessen Datentyp eine Zeichenfolge ist, geben Sie ein _bstr_t an, das eine Nullzeichenfolge enthält. Wenn Sie einen fehlenden Parameter angeben möchten, dessen Datentyp eine Variante ist, geben Sie ein _variant_t mit einem Wert von DISP_E_PARAMNOTFOUND und einem Typ von VT_ERROR an. Als Alternative können Sie die äquivalente Konstante _variant_tvtMissing angeben, die von der Anweisung #import geliefert wird.

Drei Methoden sind Ausnahmen von der typischen Verwendung von vtMissing. Dies sind die Execute-Methoden der Objekte Connection und Command und die Methode NextRecordset des Recordset-Objekts. Im Folgenden sind ihre Signaturen aufgeführt:

_RecordsetPtr <A HREF="mdmthcnnexecute.htm">Execute</A>( _bstr_t CommandText, VARIANT * RecordsAffected,   
        long Options );  // Connection  
_RecordsetPtr <A HREF="mdmthcmdexecute.htm">Execute</A>( VARIANT * RecordsAffected, VARIANT * Parameters,   
        long Options );  // Command  
_RecordsetPtr <A HREF="mdmthnextrec.htm">NextRecordset</A>( VARIANT * RecordsAffected );  // Recordset  

Die Parameter, RecordsAffected und Parameters, sind Zeiger auf eine Variante. Parameters ist ein Eingabeparameter, der die Adresse einer Variant angibt, die einen einzelnen Parameter oder ein Array von Parametern enthält, die den auszuführenden Befehl verändern werden. RecordsAffected ist ein Ausgabeparameter, welcher die Adresse einer Variant angibt, in der die Anzahl der von der Methode betroffenen Zeilen zurückgegeben wird.

In der Methode Execute des Command-Objekts geben Sie an, dass keine Parameter angegeben sind, indem Sie Parameters auf entweder &vtMissing (was empfohlen wird) oder auf den Null-Zeiger (d. h. NULL oder zero (0)) setzen. Wenn Parameters auf den Null-Zeiger gesetzt wird, ersetzt die Methode intern das Äquivalent von vtMissing und schließt dann den Vorgang ab.

Bei allen Methoden geben Sie an, dass die Anzahl der betroffenen Datensätze nicht zurückgegeben werden soll, indem Sie RecordsAffected auf den Null-Zeiger setzen. In diesem Fall ist der Null-Zeiger nicht so sehr ein fehlender Parameter als vielmehr ein Hinweis darauf, dass die Methode die Anzahl der betroffenen Datensätze verwerfen sollte.

Daher ist es für diese drei Methoden gültig, um etwas wie folgt zu coden:

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

Fehlerbehandlung

In COM wird bei den meisten Operationen ein HRESULT-Rückgabecode zurückgegeben, der angibt, ob eine Funktion erfolgreich abgeschlossen wurde. Die Anweisung #import erzeugt Wrapper-Code um jede "rohe" Methode oder Eigenschaft und überprüft den zurückgegebenen HRESULT. Wenn der HRESULT einen Fehler anzeigt, löst der Wrapper-Code einen COM-Fehler aus, indem er _com_issue_errorex() mit dem HRESULT-Rückgabecode als Argument aufruft. COM-Fehlerobjekte können in einem try-catch-Block abgefangen werden. (Der Effizienz willen fangen Sie einen Verweis auf ein _com_error-Objekt auf.)

Bedenken Sie, dass es sich hierbei um ADO-Fehler handelt: Sie entstehen, wenn die ADO-Operation fehlschlägt. Fehler, die vom zugrunde liegenden Anbieter zurückgegeben werden, erscheinen als Error-Objekte in der Sammlung Connection-Objekt Errors.

Die Anweisung #import erstellt nur Fehlerbehandlungsroutinen für Methoden und Eigenschaften, die in der ADO .dll deklariert sind. Sie können jedoch denselben Fehlerbehandlungsmechanismus nutzen, indem Sie Ihr eigenes Makro oder Ihre eigene Inline-Funktion zur Fehlerprüfung schreiben. Beispiele finden Sie im Abschnitt Visual C++ Extensions oder im Code in den folgenden Abschnitten.

Äquivalente von Visual C++ zu Visual Basic-Konventionen

Im Folgenden finden Sie eine Zusammenfassung verschiedener Konventionen in der ADO-Dokumentation, die in Visual Basic kodiert sind, sowie ihre Entsprechungen in Visual C++.

Ein ADO-Objekt deklarieren

In Visual Basic wird eine ADO-Objektvariable (in diesem Fall für ein Recordset-Objekt) wie folgt deklariert:

Dim rst As ADODB.Recordset  

Die Klausel "ADODB.Recordset" ist die ProgID des Recordset-Objekts, wie es in der Registrierung definiert ist. Eine neue Instanz eines Record-Objekts wird wie folgt deklariert:

Dim rst As New ADODB.Recordset  

- oder -

Dim rst As ADODB.Recordset  
Set rst = New ADODB.Recordset  

In Visual C++ erzeugt die Anweisung#import intelligente zeigerartige Deklarationen für alle ADO-Objekte. Zum Beispiel ist eine Variable, die auf ein _Recordset-Objekt zeigt, vom Typ _RecordsetPtr, und wird wie folgt deklariert:

_RecordsetPtr  rs;  

Eine Variable, die auf eine neue Instanz eines _Recordset-Objekts zeigt, wird wie folgt deklariert:

_RecordsetPtr  rs("ADODB.Recordset");  

- oder -

_RecordsetPtr  rs;  
rs.CreateInstance("ADODB.Recordset");  

- oder -

_RecordsetPtr  rs;  
rs.CreateInstance(__uuidof(_Recordset));  

Nach dem Aufruf der Methode CreateInstance kann die Variable wie folgt verwendet werden:

rs->Open(...);  

Beachten Sie, dass in einem Fall der Operator "." so verwendet wird, als ob die Variable eine Instanz einer Klasse wäre (rs.CreateInstance), und in einem anderen Fall wird der Operator "->" so verwendet, als ob die Variable ein Zeiger auf eine Schnittstelle wäre (rs->Open).

Eine Variable kann auf zwei Arten verwendet werden, denn der Operator "->" ist überladen, damit sich eine Instanz einer Klasse wie ein Zeiger auf eine Schnittstelle verhalten kann. Ein privates Klassenmitglied der Instanzvariablen enthält einen Zeiger auf die Schnittstelle _Recordset; der Operator "->" gibt diesen Zeiger zurück; und der zurückgegebene Zeiger greift auf die Mitglieder des _Recordset-Objekts zu.

Codieren eines fehlenden Parameters – Zeichenfolge

Wenn Sie in Visual Basic eine fehlende Zeichenfolge codieren müssen, lassen Sie die Zeichenfolge einfach weg. Sie müssen den Operanden in Visual C++ angeben. Codieren Sie einen _bstr_t, der eine leere Zeichenfolge als Wert hat.

_bstr_t strMissing(L"");  

Codieren eines fehlenden Parameters – Zeichenfolge

Wenn Sie in Visual Basic eine fehlende Variante codieren müssen, lassen Sie die Zeichenfolge einfach weg. Sie müssen alle Operanden in Visual C++ angeben. Codieren Sie einen fehlenden Variant-Parameter mit einem _variant_t, der auf den speziellen Wert DISP_E_PARAMNOTFOUND und den Typ VT_ERROR gesetzt ist. Alternativ können Sie auch vtMissing angeben, eine äquivalente vordefinierte Konstante, die von der Anweisung #import geliefert wird.

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);   

-oder verwenden Sie -

...vtMissing...;  

Deklaration einer Variante

In Visual Basic wird eine Variant mit der Dim-Anweisung wie folgt deklariert:

Dim VariableName As Variant  

In Visual C++ deklarieren Sie eine Variable als Typ _variant_t. Im Folgenden finden Sie einige schematische _variant_t-Deklarationen.

Hinweis

Diese Deklarationen vermitteln lediglich eine grobe Vorstellung davon, was Sie in Ihrem eigenen Programm codieren würden. Weitere Informationen finden Sie in den Beispielen unten und in der Visual C++-Dokumentation.

_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);  

Arrays von Varianten verwenden

In Visual Basic lassen sich Arrays von Varianten mit der Dim-Anweisung kodieren, oder Sie können die Funktion Array verwenden, wie im folgenden Beispielcode gezeigt:

Public Sub ArrayOfVariants  
Dim cn As ADODB.Connection  
Dim rs As ADODB.Recordset  
Dim fld As ADODB.Field  
  
    cn.Open "DSN=pubs"  
    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  

Das folgende Visual C++ Beispiel demonstriert die Verwendung eines SafeArray in Verbindung mit einer _variant_t.

Hinweise

Die folgenden Hinweise entsprechen den kommentierten Abschnitten im Codebeispiel.

  1. Auch hier wird die Inlinefunktion TESTHR() definiert, um den bestehenden Mechanismus zur Fehlerbehandlung zu nutzen.

  2. Weil Sie nur ein eindimensionales Array benötigen, können Sie SafeArrayCreateVector anstelle der allgemeinen Deklaration SAFEARRAYBOUND und der Funktion SafeArrayCreate verwenden. Im Folgenden sehen Sie, wie dieser Code unter Verwendung von SafeArrayCreate aussehen würde:

       SAFEARRAYBOUND   sabound[1];  
       sabound[0].lLbound = 0;  
       sabound[0].cElements = 4;  
       pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);  
    
  3. Das Schema, das durch die enumerierte Konstante adSchemaColumns gekennzeichnet ist, ist mit vier Constraint-Spalten verbunden: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME und COLUMN_NAME. Daher wird ein Array von Variant-Werten mit vier Elementen erstellt. Dann wird ein Einschränkungswert angegeben, der der dritten Spalte, TABLE_NAME, entspricht.

    Das Recordset, das zurückgegeben wird, besteht aus mehreren Spalten, von denen eine Teilmenge die Constraint-Spalten sind. Die Werte der Constraint-Spalten für jede zurückgegebene Zeile müssen mit den entsprechenden Constraint-Werten übereinstimmen.

  4. Diejenigen, die mit SafeArrays vertraut sind, werden vielleicht überrascht sein, dass SafeArrayDestroy() nicht vor dem Exit aufgerufen wird. Der Aufruf von SafeArrayDestroy() führt in diesem Fall zu einer Laufzeitausnahme. Das liegt daran, dass der Destruktor für vtCriteriaVariantClear() aufruft, wenn _variant_t den Anwendungsbereich verlässt, wodurch das SafeArray freigegeben wird. Der Aufruf von SafeArrayDestroy, ohne die _variant_t manuell zu löschen, würde dazu führen, dass der Destruktor versucht, einen ungültigen SafeArray-Zeiger zu löschen.

    Wenn SafeArrayDestroy aufgerufen wird, würde der Code wie folgt aussehen:

          TESTHR(SafeArrayDestroy(pSa));  
       vtCriteria.vt = VT_EMPTY;  
          vtCriteria.parray = NULL;  
    

    Es ist aber viel einfacher, das SafeArray von _variant_t verwalten zu lassen.

// Visual_CPP_ADO_Prog_1.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
// Note 1  
inline void TESTHR( HRESULT _hr ) {   
   if FAILED(_hr)   
      _com_issue_error(_hr);   
}  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _RecordsetPtr pRs("ADODB.Recordset");  
      _ConnectionPtr pCn("ADODB.Connection");  
      _variant_t vtTableName("authors"), vtCriteria;  
      long ix[1];  
      SAFEARRAY *pSa = NULL;  
  
      pCn->Provider = "sqloledb";  
      pCn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", adConnectUnspecified);  
      // Note 2, Note 3  
      pSa = SafeArrayCreateVector(VT_VARIANT, 1, 4);  
      if (!pSa)   
         _com_issue_error(E_OUTOFMEMORY);  
  
      // Specify TABLE_NAME in the third array element (index of 2).   
      ix[0] = 2;        
      TESTHR(SafeArrayPutElement(pSa, ix, &vtTableName));  
  
      // There is no Variant constructor for a SafeArray, so manually set the   
      // type (SafeArray of Variant) and value (pointer to a SafeArray).  
  
      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();  
}  

Verwenden der Eigenschaft Get/Put/PutRef

In Visual Basic wird der Name einer Eigenschaft nicht dadurch qualifiziert, ob sie abgerufen, zugewiesen oder mit einer Referenz versehen wird.

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;Integrated Security=SSPI;"  
rs.PageSize = 10  
sz = rs.PageSize  
rs.ActiveConnection = cn  
rs.Open "authors",,adOpenStatic  
' ...  
rs.Close  
cn.Close  
End Sub  

In diesem Visual C++-Beispiel wird die Get/Put/PutRef-Eigenschaft veranschaulicht.

Hinweise

Die folgenden Hinweise entsprechen den kommentierten Abschnitten im Codebeispiel.

  1. Dieses Beispiel verwendet zwei Formen eines fehlenden Zeichenfolgenarguments: eine explizite Konstante, strMissing, und eine Zeichenfolge, die der Compiler verwendet, um eine temporäre _bstr_t zu erstellen, die für den Anwendungsbereich der Methode Open existiert.

  2. Es ist nicht notwendig, den Operanden von rs->PutRefActiveConnection(cn) nach (IDispatch *) zu casten, da der Typ des Operanden bereits (IDispatch *).

// Visual_CPP_ado_prog_2.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _bstr_t strMissing(L"");  
      long oldPgSz = 0, newPgSz = 5;  
  
      // Note 1  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", 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();  
}  

Verwenden von GetItem(x) und Item[x]

Dieses Visual Basic-Beispiel demonstriert die Standard- und die alternative Syntax für 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  

In diesem Visual C++-Beispiel wird Item veranschaulicht.

Hinweis

Der folgende Hinweis bezieht sich auf die kommentierten Abschnitte im Codebeispiel: Wenn auf die Sammlung mit Item zugegriffen wird, muss der Index 2 in long gecastet werden, damit ein entsprechender Konstruktor aufgerufen wird.

// Visual_CPP_ado_prog_3.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
void main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _variant_t vtFirstName;  
  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", "", "", adConnectUnspecified);  
  
      rs->PutRefActiveConnection( cn );  
      rs->Open("authors", vtMissing, adOpenStatic, adLockOptimistic, adCmdTable);  
      rs->MoveFirst();  
  
      // Note 1. Get a field.  
      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);  
  
      // Restore name  
      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();  
}  

Casting von ADO-Objektzeigern mit (IDispatch *)

Das folgende Visual C++-Beispiel veranschaulicht die Verwendung (IDispatch *) zum Cast von ADO-Objektzeigern.

Hinweise

Die folgenden Hinweise entsprechen den kommentierten Abschnitten im Codebeispiel.

  1. Geben Sie ein offenes Connection-Objekt in einer explizit kodierten Variant an. Casten Sie es mit (IDispatch *), damit der richtige Konstruktor aufgerufen wird. Stellen Sie außerdem den zweiten Parameter _variant_t explizit auf den Standardwert true ein, damit die Anzahl der Objektreferenzen korrekt ist, wenn der Vorgang Recordset::Open beendet ist.

  2. Der Ausdruck (_bstr_t) ist kein Cast, sondern ein _variant_t-Operator, der einen _bstr_t-String aus der von Value zurückgegebenen Variante extrahiert.

Der Ausdruck (char*) ist kein Cast, sondern ein _bstr_t-Operator, der einen Zeiger auf die gekapselte Zeichenfolge in einem _bstr_t-Objekt extrahiert.

Dieser Abschnitt des Codes demonstriert einige der nützlichen Verhaltensweisen der Operatoren _variant_t und _bstr_t.

// Visual_CPP_ado_prog_4.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr pConn("ADODB.Connection");  
      _RecordsetPtr pRst("ADODB.Recordset");  
  
      pConn->Provider = "sqloledb";  
      pConn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", 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();  
}