⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch15.htm

📁 这是一本关于VC++自学教程的书籍 对于初学者以及编程有一定基础的人均有帮助
💻 HTM
📖 第 1 页 / 共 3 页
字号:
for use with ADO. If you don't want to use the controls approach, then what are youroptions? Do you have to create the classes yourself? No, Microsoft has provided othermeans for you to create and use classes for each of the objects in ADO, through theuse of a new C++ precompiler directive called #import.</P><P>The #import precompiler directive was first added to Visual C++ with the 5.0 release.You can use this directive to import an ActiveX DLL that has been built with theIDispatch interface description included in the DLL. This directive tells the VisualC++ compiler to import the DLL specified by the #import directive and to extractthe object information from the DLL, creating a couple of header files that are automaticallyincluded in your project. These header files have the filename extensions .TLH and.TLI and are in the output directory for your project (the Debug or Release directory,the same directory where you'll find the executable application after you've compiledyour project). These two files contain definitions of classes for each of the objectsin the DLL that you can use in your code. The #import directive also tells the compilerto include the DLL as part of the project, eliminating the need to include the .LIBfile for the DLL in your project.</P><P>You can import the ADO DLL by placing the following code at the beginning of theheader file in which you are defining any database objects:</P><P><PRE>#define INITGUID#import &quot;C:\Program Files\Common Files\System\ADO\msado15.dll&quot;        &Acirc;rename_namespace(&quot;ADOCG&quot;) rename(&quot;EOF&quot;, &quot;EndOfFile&quot;)using namespace ADOCG;#include &quot;icrsint.h&quot;</PRE><P>In these four lines of directives, the first line defines a constant that needsto be defined for ADO. The second imports the ADO DLL, creating the two header filesmentioned earlier. After the filename to be imported, this directive includes twoattributes to the #import directive. The first, rename_namespace, renames the namespaceinto which the DLL has been imported. This is followed with the line following the#import, where the renamed namespace is specified as the one used. The second attribute,rename, renames an element in the header files that are created using the #importdirective. The reason you rename elements in these header files is to prevent conflictswith another element named elsewhere. If you examine the header file, the elementspecified is not renamed in the file, but when the compiler reads the file, the elementis renamed. The final line includes the ADO header file, which contains the definitionof some macros that you will use when writing your ADO applications.</P><P><H3><A NAME="Heading5"></A>Connecting to a Database</H3><P>Before you can use any of the ADO objects, you need to initialize the COM environmentfor your application. You can do this by calling the CoInitialize API function, passingNULL as the only parameter, as follows:</P><P><PRE>::CoInitialize(NULL);</PRE><P>This enables you to make calls to ActiveX objects. If you leave out this one lineof code from your application, or don't put it before you begin interacting withthe objects, you get an COM error whenever you run your application.</P><P>When you are finished with all ADO activity, you also need to shut down the COMenvironment by calling the CoUninitialize function, as follows:</P><P><PRE>CoUninitialize();</PRE><P>This function cleans up the COM environment and prepares your application forshutting down.</P><P>Once you initialize the COM environment, you can create a connection to the database.The best way to do this is not to declare a Connection object variable, but to declarea Connection object pointer, _ConnectionPtr, and use it for all your interactionwith the Connection object. Once you declare a Connection object pointer, you caninitialize it by creating an instance of the Connection object, calling the CreateInstancefunction, passing it the UUID of the Connection object as its only parameter, asfollows:</P><P><PRE>_ConnectionPtr pConn;pConn.CreateInstance(__uuidof(Connection));</PRE><BLOCKQUOTE>	<P><HR><STRONG>TIP:</STRONG> When you work with these objects and functions, you need to use the	correct number of underscore characters in front of the various object and function	names. The _ConnectionPtr object has only a single underscore character, whereas	the __uuidof function has two.<HR></BLOCKQUOTE><P>Once you create the object, you can call the Open function to establish the connectionto the database. This function takes four parameters. The first parameter is theconnection definition string. This string defines the OLE DB data source for thedatabase. It may be an ODBC OLE DB driver, where OLE DB is sitting on top of an ODBCdata source, as you'll use in your sample application. If you are using SQL Serveror Oracle databases, it may be a direct connection to the OLE DB interface providedby the database itself. The second parameter is the user ID for connecting to thedatabase. The third parameter is the password for connecting to the database. Thefourth parameter is the cursor type to use with the database. These types are definedin the msado15.tlh header file that is created by the #import directive. A typicaluse of the Open function to connect to an ODBC data source that doesn't need a userID or password is like the following:</P><P><PRE>pConn-&gt;Open(L&quot;Provider=MSDASQL.1;Data Source=TYVCDB&quot;, L&quot;&quot;, L&quot;&quot;,     &Acirc;adOpenUnspecified);</PRE><H3><A NAME="Heading6"></A>Executing Commands and Retrieving Data</H3><P>Once you have the connection open, you can use a Command object to pass SQL commandsto the database. This is the normal method of executing SQL commands with ADO. Tocreate a Command object, follow the same process that you used to create a Connectionobject. Declare a Command object pointer, _CommandPtr, and then create an instanceof it using the UUID of the Command object, as follows:</P><P><H3><A NAME="Heading7"></A>_CommandPtr pCmd;</H3><PRE>pCmd.CreateInstance(__uuidof(Command));</PRE><P>Once you create your Command object, assuming that you have already establishedthe connection to the database, set the active connection property of the Commandobject to the open Connection object pointer, as follows:</P><P><PRE>pCmd-&gt;ActiveConnection = pConn;</PRE><P>Next, specify the SQL command to be executed by setting the CommandText propertyof the Command object, as follows:</P><P><PRE>pCmd-&gt;CommandText = &quot;Select * from Addresses&quot;;</PRE><P>At this point, you have two options for how you execute this command and retrievethe records. The first is to call the Command object's Execute method, which willreturn a new Recordset object, which you'll want to set to a Recordset object pointer,as follows:</P><P><PRE>_RecordsetPtr pRs;pRs = pCmd-&gt;Execute();</PRE><P>The other approach to running the command and retrieving the records is to specifythat the Command object is the source for the records in the Recordset. This requirescreating the Recordset object as follows:</P><P><PRE>_RecordsetPtr pRs;pRs.CreateInstance(__uuidof(Recordset));pRs-&gt;PutRefSource(pCmd);</PRE><P>Now, you'll need to create two NULL variant values to pass as the first two parametersto the Recordset's Open method. The third parameter will be the cursor type to use,followed by the locking method to use. Finally, the fifth parameter to the Recordset'sOpen method is an options flag that indicates how the database should evaluate thecommand being passed in. You do this with the following code:</P><P><PRE>// Create the variant NULL_variant_t vNull;vNull.vt = VT_ERROR;vNull.scode = DISP_E_PARAMNOTFOUND;// Open the recordsetpRs-&gt;Open(vNull, vNull, adOpenDynamic, adLockOptimistic, adCmdUnknown);</PRE><P>You could take another approach to accomplish all of the preceding tasks withonly a few lines of code. Skip the use of the Command and Connection objects altogether,placing all the necessary connection information in the Recordset's Open function.You can specify the SQL command as the first parameter and the connection informationas the second parameter, instead of the two NULLs that you passed previously. Thismethod reduces all of the preceding code to the following few lines:</P><P><PRE>_RecordsetPtr pRs;pRs.CreateInstance(__uuidof(Recordset));pRs-&gt;Open(_T(&quot;Provider=MSDASQL.1;Data Source=TYVCDB&quot;),          _T(&quot;select * from Addresses&quot;), adOpenDynamic,           adLockOptimistic, adCmdUnknown);</PRE><BLOCKQUOTE>	<P><HR><STRONG>TIP:</STRONG> Although placing all of the command and connection information into	the Recordset Open function is fine for a simple application, such as the one that	you will build today, you are better off using the Connection object with any application	that has more than a couple of database queries. This allows you to make a single	connection to the database and use that one connection for all interaction with the	database.az<HR></BLOCKQUOTE><H3><A NAME="Heading8"></A>Navigating the Recordset</H3><P>Once you've retrieved a set of records from the database, and you are holdingthem in a Recordset object, you'll need to navigate the set of records. This functionalityis available, just as you would expect, through the MoveFirst, MoveLast, MovePrevious,and MoveNext functions. None of these functions take any parameters because theyperform the functions that you would expect them to perform.</P><P>Along with these functions, the Recordset object also has two properties, BOFand EOF (which you should normally rename to prevent a collision with the defaultdefinition of EOF), which can be checked to determine if the current record in theset is beyond either end of the set of records.</P><P><H3><A NAME="Heading9"></A>Accessing Field Values</H3><P>When you need to begin accessing the data values in each of the fields is whereworking with ADO in Visual C++ begins to get interesting. Because ADO is intendedto be easy to use in Microsoft's scripting languages, VBScript and JScript, whichonly have variant data types, all data elements that you'll retrieve from fieldsin the ADO Recordset are variant values. They have to be converted into the datatypes that you need them to be. There are two ways of doing this. The first way isthe straight-forward way of retrieving the values into a variant and then convertingthem, as in the following code:</P><P><PRE>_variant_t vFirstName;CString strFirstName;vFirstName = pRs-&gt;GetCollect(_variant_t(&quot;FirstName&quot;));vFirstName.ChangeType(VT_BSTR);strFirstName = vFirstName.bstrVal;</PRE><P>The not-so-straight-forward way to do this is actually the better way, and inthe long run, is a lot easier to work with. Microsoft has created a series of macrosthat perform the conversion for you and that maintain a set of variables of the recordsin the set. To do this, you'll define a new class to use as the interface for yourrecord set. This class will be a descendent of the CADORecordBinding class, whichis defined in the icrsint.h header file, which you included just after the #importdirective. This class will not have any constructor or destructor but will have aseries of macros, along with a number of variables. Each field in the set of recordshas two variables, an unsigned long, which is used to maintain the status of thevariable, and the field variable itself. These variables must be regular C variables,and they cannot be C++ classes such as CString. A simple example of this class declarationis the following:</P><P><PRE>class CCustomRs :    public CADORecordBinding{BEGIN_ADO_BINDING(CCustomRs)    ADO_FIXED_LENGTH_ENTRY(1, adInteger, m_lAddressID, lAddressIDStatus,     &Acirc;FALSE)    ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szFirstName,         &Acirc;sizeof(m_szFirstName), lFirstNameStatus, TRUE)    ADO_FIXED_LENGTH_ENTRY(3, adDate, m_dtBirthdate, lBirthdateStatus,     &Acirc;TRUE)    ADO_FIXED_LENGTH_ENTRY(4, adBoolean, m_bSendCard, lSendCardStatus,     &Acirc;TRUE)END_ADO_BINDING()public:    LONG m_lAddressID;    ULONG lAddressIDStatus;    CHAR m_szFirstName[51];    ULONG lFirstNameStatus;    DATE m_dtBirthdate;    ULONG lBirthdateStatus;    VARIANT_BOOL m_bSendCard;    ULONG lSendCardStatus;};</PRE><P>Once you define this record layout class to match the record layout that willbe returned by your database query, you can declare a variable of this class foruse in your application, as follows:</P><P><PRE>CCustomRs m_rsRecSet;</PRE><P>Next, you need to create a pointer to an IADORecordBinding interface, as follows:</P><P><PRE>IADORecordBinding *picRs = NULL;</PRE><P>This is a pointer to a COM interface that is part of the ADO Recordset object.Once you retrieve the set of records, you need to retrieve the pointer to the IADORecordBindinginterface and bind the custom record set class to the Recordset object, as in thefollowing code:</P><P><PRE>if (FAILED(pRs-&gt;QueryInterface(__uuidof(IADORecordBinding), (LPVOID &Acirc;*)&amp;picRs)))    _com_issue_error(E_NOINTERFACE);picRs-&gt;BindToRecordset(&amp;m_rsRecSet);</PRE><P>Now, as you navigate the records in the set, you just need to access the membervariables of your custom record class to retrieve the current value for each field.</P><P><H4>The BEGIN_ADO_BINDING and END_ADO_BINDING Macros</H4><P>The key to the second method of accessing the data values in the record set isin the macros that are used in defining the record class. The set of macros startwith the BEGIN_ADO_BINDING macro, which takes the class name as its only parameter.This macro sets up the structure definition that is created with the rest of themacros that follow.</P><P>The set of macros is closed by the END_ADO_BINDING macro. This macro doesn't takeany parameters, and it wraps up the definition of the record binding structure thatis created in the class. It is in the rest of the macros, which are used betweenthese two, where the real work is done.</P><P><H4>The ADO_FIXED_LENGTH_ENTRY Macros</H4><P>The ADO_FIXED_LENGTH_ENTRY macro is used for any database fields that are fixedin size. It can be used with a date or boolean field, or even a text field that isa fixed size, with no option for any variation in the database. There are two versionsof this macro; you add a 2 to the end of the name of the second version (ADO_FIXED_LENGTH_ENTRY2).</P><P>Both versions require the same first three and last parameters. The first versionrequires an additional parameter that is not required in the second version. Thefirst parameter is the ordinal number of the field in the record set. This is theposition in the field order as returned by the SQL query that is run to populatethe record set. The second parameter is the data type of the field; the availabledata types are defined in the header file created by the #import directive. The thirdparameter is the variable into which the data value is to be copied. For the firstversion of the macro, the fourth parameter is the variable for the field status (theunsigned long that you defined with the variable for the actual value). The lastvariable is a boolean that specifies whether this field can be modified.</P><P><H4>The ADO_NUMERIC_ENTRY Macros</H4><P>You use the ADO_NUMERIC_ENTRY macros with numeric fields only. They are similarto the ADO_FIXED_LENGTH_ENTRY macros in that there are two different versions ofthe macro, named in the same way. In these macros, the first five parameters arethe same in both versions, along with the final parameter. Like with the ADO_FIXED_LENGTH_ENTRYmacros, the first version has an additional parameter that is not used in the secondversion.</P><P>The first three parameters for the ADO_NUMERIC_ENTRY macros are the same as thosefor the ADO_FIXED_LENGTH_ENTRY macros, as are the last parameter and the next to

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -