📄 ch15.htm
字号:
Âsizeof(m_szFirstName), lFirstNameStatus, TRUE)
12: ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szLastName,
Âsizeof(m_szLastName), lLastNameStatus, TRUE)
13: ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szSpouseName,
Âsizeof(m_szSpouseName), lSpouseNameStatus, TRUE)
14: ADO_VARIABLE_LENGTH_ENTRY2(5, adVarChar, m_szAddress,
Âsizeof(m_szAddress), lAddressStatus, TRUE)
15: ADO_VARIABLE_LENGTH_ENTRY2(6, adVarChar, m_szCity, Âsizeof(m_szCity),lCityStatus, TRUE)
16: ADO_VARIABLE_LENGTH_ENTRY2(7, adVarChar, m_szStateOrProvince,
Âsizeof(m_szStateOrProvince), lStateOrProvinceStatus, TRUE)
17: ADO_VARIABLE_LENGTH_ENTRY2(8, adVarChar, m_szPostalCode,
Âsizeof(m_szPostalCode), lPostalCodeStatus, TRUE)
18: ADO_VARIABLE_LENGTH_ENTRY2(9, adVarChar, m_szCountry,
Âsizeof(m_szCountry), lCountryStatus, TRUE)
19: ADO_VARIABLE_LENGTH_ENTRY2(10, adVarChar, m_szEmailAddress,
Âsizeof(m_szEmailAddress), lEmailAddressStatus, TRUE)
20: ADO_VARIABLE_LENGTH_ENTRY2(11, adVarChar, m_szHomePhone,
Âsizeof(m_szHomePhone), lHomePhoneStatus, TRUE)
21: ADO_VARIABLE_LENGTH_ENTRY2(12, adVarChar, m_szWorkPhone,
Âsizeof(m_szWorkPhone), lWorkPhoneStatus, TRUE)
22: ADO_VARIABLE_LENGTH_ENTRY2(13, adVarChar, m_szWorkExtension,
Âsizeof(m_szWorkExtension), lWorkExtensionStatus, TRUE)
23: ADO_VARIABLE_LENGTH_ENTRY2(14, adVarChar, m_szFaxNumber,
Âsizeof(m_szFaxNumber), lFaxNumberStatus, TRUE)
24: ADO_FIXED_LENGTH_ENTRY(15, adDate, m_dtBirthdate, ÂlBirthdateStatus,TRUE)
25: ADO_FIXED_LENGTH_ENTRY(16, adBoolean, m_bSendCard, ÂlSendCardStatus,TRUE)
26: ADO_VARIABLE_LENGTH_ENTRY2(17, adLongVarChar, m_szNotes,
Âsizeof(m_szNotes), lNotesStatus, TRUE)
27: END_ADO_BINDING()
28:
29: public:
30: LONG m_lAddressID;
31: ULONG lAddressIDStatus;
32: CHAR m_szFirstName[51];
33: ULONG lFirstNameStatus;
34: CHAR m_szLastName[51];
35: ULONG lLastNameStatus;
36: CHAR m_szSpouseName[51];
37: ULONG lSpouseNameStatus;
38: CHAR m_szAddress[256];
39: ULONG lAddressStatus;
40: CHAR m_szCity[51];
41: ULONG lCityStatus;
42: CHAR m_szStateOrProvince[21];
43: ULONG lStateOrProvinceStatus;
44: CHAR m_szPostalCode[21];
45: ULONG lPostalCodeStatus;
46: CHAR m_szCountry[51];
47: ULONG lCountryStatus;
48: CHAR m_szEmailAddress[51];
49: ULONG lEmailAddressStatus;
50: CHAR m_szHomePhone[31];
51: ULONG lHomePhoneStatus;
52: CHAR m_szWorkPhone[31];
53: ULONG lWorkPhoneStatus;
54: CHAR m_szWorkExtension[21];
55: ULONG lWorkExtensionStatus;
56: CHAR m_szFaxNumber[31];
57: ULONG lFaxNumberStatus;
58: DATE m_dtBirthdate;
59: ULONG lBirthdateStatus;
60: VARIANT_BOOL m_bSendCard;
61: ULONG lSendCardStatus;
62: CHAR m_szNotes[65536];
63: ULONG lNotesStatus;
64: };
</PRE>
<P>Once you've created this class, you need to add a variable to the document class.
Add a new member variable to the document class, specifying the variable type as
CCustomRs, the name as m_rsRecSet, and the access as private. You'll also need to
include the custom record class header file in the document source code file, as
in Listing 15.2.</P>
<P>
<H4>LISTING 15.2. THE DOCUMENT SOURCE CODE INCLUDES.</H4>
<PRE> 1: // dbadoDoc.cpp : implementation of the CDbAdoDoc class
2: //
</PRE>
<H3><A NAME="Heading16"></A>3:</H3>
<PRE> 4: #include "stdafx.h"
5: #include "dbado.h"
6:
7: #include "CustomRs.h"
8: #include "dbadoDoc.h"
9: #include "dbadoView.h"
</PRE>
<P>Another detail that you need to attend to before going any further is providing
a way for the view to get a pointer to the record class from the document class.
This function should return a pointer to the record class variable. To add this function
to your application, add a new member function to the document class, specifying
the function type as CCustomRs*, the function declaration as GetRecSet, and the function
access as public. Edit this function, adding the code in Listing 15.3.</P>
<P>
<H4>LISTING 15.3. THE CDbAdoDoc GetRecSet FUNCTION.</H4>
<PRE> 1: CCustomRs* CDbAdoDoc::GetRecSet()
2: {
3: // Return a pointer to the record object
4: return &m_rsRecSet;
5: }
</PRE>
<P>One last piece of functionality that you'll add before getting to the real heart
of ADO programming is the function for reporting ADO and database errors. This function
will display a message to the user, reporting that an error occurred and displaying
the error code and error message for the user. To add this function to your application,
add a new member function to your document class. Specify the function type as void,
the function declaration as GenerateError(HRESULT hr, PWSTR pwszDescription), and
the access as public. Edit the function, entering the code in Listing 15.4.</P>
<P>
<H4>LISTING 15.4. THE CDbAdoDoc GenerateError FUNCTION.</H4>
<PRE> 1: void CDbAdoDoc::GenerateError(HRESULT hr, PWSTR pwszDescription)
2: {
3: CString strError;
4:
5: // Format and display the error message
6: strError.Format("Run-time error `%d (%x)'", hr, hr);
7: strError += "\n\n";
8: strError += pwszDescription;
9:
10: AfxMessageBox(strError);
11: }
</PRE>
<H3><A NAME="Heading17"></A>Connecting and Retrieving Data</H3>
<P>You can perform all of the connecting to the database and retrieving the record
set in the OnNewDocument function in the document class. Before you can add this
functionality, you need to add a few more variables to the document class. You'll
need a Recordset object pointer, an IADORecordBinding interface pointer, a couple
of string variables for holding the database connection string, and the SQL command
to execute to populate the record set. Add all of these variables to the document
class as specified in Table 15.3.</P>
<P>
<H4>TABLE 15.3. DOCUMENT CLASS MEMBER VARIABLES.</H4>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT"><I>Name</I></TD>
<TD ALIGN="LEFT"><I>Type</I></TD>
<TD ALIGN="LEFT"><I>Access</I></TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">m_pRs </TD>
<TD ALIGN="LEFT">_RecordsetPtr </TD>
<TD ALIGN="LEFT">Private </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">m_piAdoRecordBinding </TD>
<TD ALIGN="LEFT">IADORecordBinding* </TD>
<TD ALIGN="LEFT">Private </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">m_strConnection </TD>
<TD ALIGN="LEFT">CString </TD>
<TD ALIGN="LEFT">Private </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">m_strCmdText </TD>
<TD ALIGN="LEFT">CString </TD>
<TD ALIGN="LEFT">Private </TD>
</TR>
</TABLE>
</P>
<P>In the OnNewDocument function, you'll perform a series of steps for connecting
and retrieving the record set. First, you'll set the strings for the database connection
and the SQL command to be run. Next, you'll initialize the COM environment and initialize
the two pointers so that they are both NULL. You'll create the Recordset object using
the CreateInstance function. Open the Recordset, connecting to the database and running
the SQL command at the same time. Bind the record class to the record set using the
IADORecordBinding interface pointer. Finally, tell the view class to refresh the
bound data, displaying the initial record for the user using a view class function
that you'll add in a little while. To add all this functionality, edit the OnNewDocument
function in the document class, adding the code starting with line 8 in Listing 15.5.</P>
<P>
<H4>LISTING 15.5. THE CDbAdoDoc OnNewDocument FUNCTION.</H4>
<PRE> 1: BOOL CDbAdoDoc::OnNewDocument()
2: {
3: if (!CDocument::OnNewDocument())
4: return FALSE;
5:
6: // TODO: add reinitialization code here
7: // (SDI documents will reuse this document)
8: // Set the connection and SQL command strings
9: m_strConnection = _T("Provider=MSDASQL.1;Data Source=TYVCDB");
10: m_strCmdText = _T("select * from Addresses");
11:
12: // Initialize the Recordset and binding pointers
13: m_pRs = NULL;
14: m_piAdoRecordBinding = NULL;
15: // Initialize the COM environment
16: ::CoInitialize(NULL);
17: try
18: {
19: // Create the record set object
20: m_pRs.CreateInstance(__uuidof(Recordset));
21:
22: // Open the record set object
23: m_pRs->Open((LPCTSTR)m_strCmdText, (LPCTSTR)m_strConnection,
24: adOpenDynamic, adLockOptimistic, adCmdUnknown);
25:
26: // Get a pointer to the record binding interface
27: if (FAILED(m_pRs->QueryInterface(__uuidof(IADORecordBinding),
28: (LPVOID *)&m_piAdoRecordBinding)))
29: _com_issue_error(E_NOINTERFACE);
30: // Bind the record class to the record set
31: m_piAdoRecordBinding->BindToRecordset(&m_rsRecSet);
32:
33: // Get a pointer to the view
34: POSITION pos = GetFirstViewPosition();
35: CDbAdoView* pView = (CDbAdoView*)GetNextView(pos);
36: if (pView)
37: // Sync the data set with the form
38: pView->RefreshBoundData();
39: }
40: // Any errors?
41: catch (_com_error &e)
42: {
43: // Display the error
44: GenerateError(e.Error(), e.Description());
45: }
46:
47: return TRUE;
48: }
</PRE>
<P>Before moving any further, it's a good idea to make sure that you add all the
code necessary to clean up as your application is closing. You need to close the
record set and release the pointer to the record binding interface. You'll also shut
down the COM environment. To add all this functionality to your application, add
a function to the DeleteContents event message in the document class. Edit this function,
adding the code in Listing 15.6.</P>
<P>
<H4>LISTING 15.6. THE CDbAdoDoc DeleteContents FUNCTION.</H4>
<PRE> 1: void CDbAdoDoc::DeleteContents()
2: {
3: // TODO: Add your specialized code here and/or call the base class
4: // Close the record set
5: if (m_pRs)
6: m_pRs->Close();
7: // Do we have a valid pointer to the record binding?
8: if (m_piAdoRecordBinding)
9: // Release it
10: m_piAdoRecordBinding->Release();
11: // Set the record set pointer to NULL
12: m_pRs = NULL;
13:
14: // Shut down the COM environment
15: CoUninitialize();
16:
17: CDocument::DeleteContents();
18: }
</PRE>
<H3><A NAME="Heading18"></A>Populating the Form</H3>
<P>To display the record column values for the user, you'll add a function for copying
the values from the record class to the view variables. This function first needs
to get a pointer to the record class from the document class. Next, it will check
the status of each individual field in the record class to make sure that it's okay
to copy, and then it will copy the value. Once all values have been copied, you can
call UpdateData to display the values in the controls on the form. To add this functionality
to your application, add a new member function to the view class. Specify the function
type as void, the function declaration as RefreshBoundData, and the access as public.
Edit this new function, adding the code in Listing 15.7.</P>
<P>
<H4>LISTING 15.7. THE CDbAdoView RefreshBoundData FUNCTION.</H4>
<PRE> 1: void CDbAdoView::RefreshBoundData()
2: {
3: CCustomRs* pRs;
4:
5: // Get a pointer to the document object
6: pRs = GetDocument()->GetRecSet();
7:
8: // Is the field OK
9: if (adFldOK == pRs->lAddressIDStatus)
10: // Copy the value
11: m_lAddressID = pRs->m_lAddressID;
12: else
13: // Otherwise, set the value to 0
14: m_lAddressID = 0;
15: // Is the field OK
16: if (adFldOK == pRs->lFirstNameStatus)
17: // Copy the value
18: m_strFirstName = pRs->m_szFirstName;
19: else
20: // Otherwise, set the value to 0
21: m_strFirstName = _T("");
22: if (adFldOK == pRs->lLastNameStatus)
23: m_strLastName = pRs->m_szLastName;
24: else
25: m_strLastName = _T("");
26: if (adFldOK == pRs->lSpouseNameStatus)
27: m_strSpouseName = pRs->m_szSpouseName;
28: else
29: m_strSpouseName = _T("")
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -