zt:
1.配置ODBC,建立ODBC和SQL SERVER的连接ODBCTEST
2.在VC++通过该ODBC调用SQL SERVER的STORED PROCEDURE(szTypes )
#include "stdafx.h"
#include "DatabaseServer.h"
#include <stdarg.h>#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////CDatabaseServer::CDatabaseServer()
{

}CDatabaseServer::~CDatabaseServer()
{}bool CDatabaseServer::getConnectionString(char *szConnectionString)
{
char szServerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwSize=sizeof(szServerName) ;
if(!GetComputerName(szServerName,&dwSize))
return false ;
if(!szConnectionString)
return false ; char szUserName[] = "SA";
char szPassword[] = "";
char szDatabase[] = "IPLOMA";//ADD YOU DATEBASE NAME

sprintf(szConnectionString,"DSN=ODBCTEST;uid=%s;pwd=%s;",szServerName,szDatabase,szUserName,szPassword); //建立CONNECTION STRING return true;
}VARIANT CDatabaseServer::getExecStoredProcedure(char *szTypes,SAFEARRAY *pSPFields)
{
    _variant_t vtResultRows;
    try
    {
_CommandPtr   pCmdPtr;
        _RecordsetPtr pRecordset;
        HRESULT hr ;  hr = pCmdPtr.CreateInstance(__uuidof(Command)); char szConnectionString[255];
getConnectionString(szConnectionString);
_variant_t vtConnectionString(szConnectionString);
pCmdPtr->put_ActiveConnection(vtConnectionString);        pCmdPtr->CommandType = adCmdStoredProc; //CALL SQL SP
pCmdPtr->CommandText =  szTypes ; //YOU SP NAME
hr = pCmdPtr->Parameters->Refresh();    long lBound,uBound ;
   HRESULT hresult ;
   // Getting Safe Array's Lower and Upper Bounds
   hresult = SafeArrayGetLBound(pSPFields, 1, &lBound);
       hresult = SafeArrayGetUBound(pSPFields, 1, &uBound);    variant_t vtParamVal;
_variant_t Index;
Index.vt = VT_I2;
Index.iVal = 1 ;
   for (long iElements=lBound;iElements<=uBound;iElements++)
   {
hresult = SafeArrayGetElement(pSPFields, &iElements, &vtParamVal);
pCmdPtr->GetParameters()->GetItem(Index)->PutValue(vtParamVal) ;
Index.iVal++ ;
   }    //Execute current Stored Procedure
        _variant_t vEffected ;
pRecordset = pCmdPtr->Execute(&vEffected,NULL,NULL);
if (pRecordset->BOF || pRecordset->EndOfFile)
throw ;
// Get result set in the form of array
        vtResultRows = pRecordset->GetRows(-1);
        return vtResultRows.Detach() ;
}
catch(_com_error &e)
    {
ATLTRACE((LPCSTR)e.Description());
    }
vtResultRows.vt = VT_EMPTY ;
    return vtResultRows.Detach();
}long CDatabaseServer::setExecStoredProcedure(char *szTypes,SAFEARRAY *pSPFields)
{
    _variant_t vtResultRows;
    try
    {
        _CommandPtr   pCmdPtr;
        _RecordsetPtr pRecordset;
        HRESULT hr ;  hr = pCmdPtr.CreateInstance(__uuidof(Command)); char szConnectionString[255];
getConnectionString(szConnectionString);
_variant_t vtConnectionString(szConnectionString);
pCmdPtr->put_ActiveConnection(vtConnectionString);        pCmdPtr->CommandType = adCmdStoredProc;
pCmdPtr->CommandText =  szTypes ;
hr = pCmdPtr->Parameters->Refresh();    long lBound,uBound;
   HRESULT hresult;
   // Getting Safe Array's Lower and Upper Bounds
   hresult = SafeArrayGetLBound(pSPFields, 1, &lBound);
       hresult = SafeArrayGetUBound(pSPFields, 1, &uBound);    variant_t vtParamVal;
_variant_t Index;
Index.vt = VT_I2;
Index.iVal = 1 ;
   for (long iElements=lBound;iElements<=uBound;iElements++)
   {
hresult = SafeArrayGetElement(pSPFields, &iElements, &vtParamVal);
pCmdPtr->GetParameters()->GetItem(Index)->PutValue(vtParamVal) ;
Index.iVal++ ;
   }        _variant_t vEffected ;
pCmdPtr->Execute(&vEffected,NULL,NULL);

// We Are Expecting That Stored Procedures Return ID for Entity to which 
// NSERT/UPDATE/DELETE  operation is being performed
        return (long)pCmdPtr->Parameters->Item["RETURN_VALUE"]->Value  ;
    }
    catch(_com_error &e)
    {
       ATLTRACE((LPCSTR)e.Description());
    }
    return 0;
}

解决方案 »

  1.   

    zt:ODBC数据库访问方法及其VC实现要点在应用程序中,往往需要数据库的支持,以统一管理和维护数据,同时可以提供高效地对数据进行排序、检索、查询等功能。但是数据库的访问方式比较多,实现方法也很多,且有很多编程注意事项。因此,正确理解数据库访问的方式、程序结构及相关的编程技巧是很重要的。本文将就这几个方面进行介绍。 
    1.ODBC数据库访问结构 
    通过ODBC访问数据库的结构如图1所示。 
      
     
    图1 ODBC数据库访问结构
     
    ODBC是通过使用驱动程序(Driver)来实现应用程序对数据库的独立性的。各个模块的功能分别是: 
    ● ODBC应用程序通过ODBC函数向数据库发送SQL语句并处理返回结果 
    ● ODBC驱动程序管理器负责管理和装载特定的驱动程序 
    ● ODBC驱动程序负责处理ODBC函数调用,提交SQL请求给特定的数据源并返回结果给应用程序。 
    ● 数据源指要存取的数据及其相关的操作系统、数据库管理系统和网络系统。 
     
    2.VC++数据库应用程序结构 
    典型的VC++数据库应用程序的结构如图2所示。 
     
     
    图2 VC++数据库应用程序结构
     
    下面详述各个模块之间的关系: 
    1) 数据源与记录集 
    在VC的MFC类库中,提供了数据源类(CDatabase)来实现对数据源的一个连接,又提供了记录集类(CRecordset)来管理数据源中选定的记录集合。开发时要从MFC的CRecordset类继承出自己的记录集类。 
    数据源对象与程序中的记录集对象关联在一起,通过在程序中操纵记录集对象来实现对数据源的访问。在记录集对象中维护着数据源中被访问的当前记录,通过Move之类的函数(如CRecordset::MoveFirst,CRecordset::MoveNext)可以改变当前记录,从而实现对整个数据源的访问。 
    数据源与记录集之间的信息交换是通过“记录域交换”(RFX)机制来实现的,如CRecordset:: DoFieldExchange。这个信息交换操作会由打开记录集、重新检索数据源和更新数据源等操作触发(对应于CRecordset::Open、CRecordset::Requery和CRecordset::Update等函数)。 
    2)视图与记录集 
    VC的MFC类库中提供了CRecordView类,是用来快速构建数据操作界面的视图类。开发时要从MFC的CRecordView类继承出自己的视图类。 
    视图对象与一个记录集对象绑定在一起,通过“对话框数据交换”(DDX)机制来实现视图与记录集间的信息传递,如CRecordView:: DoDataExchange。这由CWnd::UpdateData触发。 
    除了视图与记录集绑定这种方式外,也可以用非绑定的方式来实现视图与记录集间的交互。即用一般的视图(如CFormView)来创建数据操作界面。这种情况也是很普遍的,如果在想在一个已有程序中添加数据库操作,但是原有程序的视图类并没有从CRecordView继承,那么用非绑定的方式就可以最小限度的修改原有程序。 
    构造记录集对象时要把它所连接的数据源对象作为参数传递给记录集对象的构造函数,但在绑定和非绑定这两种情况下创建记录集的方式有所不同,应当引起注意: 
    ● 在绑定的方式下,在视图中创建了记录集,并以NULL为参数传给记录集类的构造函数。具体过程是这样的,VC自动生成了一个数据源对象,并自动根据生成记录集时定义的数据源信息(在重载的CRecordset::GetDefaultConnect函数中)来打开该数据源对象,建立到相应数据源的一个连接。 
    ● 在非绑定的方式下,记录集要自己手动创建,所以要先创建一个数据源对象,并将其作为参数传给记录集类的构造函数。如m_pMyRecordSet=new CMyRecordSet(&m_MyDBOdbc);。 
    3.编程技巧及注意事项 
    ● 编程技巧及注意事项 
    1) 在调用CRecordset::Update函数更新数据源之前,要调用CRecordset::Edit函数做好对数据源进行更新操作的准备,否则用CWnd::UpdateData触发的对记录集的更新以及直接修改记录集数据的操作将反映不到数据源中。 
    2) 在调用Move之类函数(如CRecordset::MoveFirst,CRecordset::MoveNext)之前,应当调用CRecordset::IsBOF和CRecordset::IsEOF来防止出现异常。CRecordset::IsBOF判断记录集是否为空,CRecordset::IsEOF判断是否越出最后一条记录。两者都是用来判断当前记录是否有效的。 
    3) 当手动添加或删除记录集中对应于数据源表格字段的成员变量时,不要忘了在记录集的构造函数中修改表征字段个数的成员变量(CRecordset::m_nFields)。同样,对于参数个数的改变也要相应修改成员变量CRecordset::m_nParams。 
    4) 由于不同的ODBC驱动程序所支持的数据库操作能力不同,所以,对于记录集类的一些操作,在执行之前要先判断该操作是否被支持。比如Requery之前,先调用CanRestart判断能否重新检索。 
    5) 可以在记录集重载的CRecordset::GetDefaultSQL()函数中指定默认的SQL语句。SQL语句可先用相应的数据库管理系统(如SQL SERVER,ACCESS等)生成,保证其正确性,再写入程序中。对于设定记录集的过滤和排序等成员变量时也可采取这种办法,由于不同DBMS对表现形式定义不同,这样做可以避免出现错误。 
    6) 在非绑定的方式下创建记录集时,要注意数据源对象的生存期问题。一般在视图对象中维护一个记录集的成员变量,并通过如下方式来生成记录集对象: 
    void CmyView::CreateRecordset() 

    … 
    m_pMyRecordset=new CMyRecordset(&m_MyDBOdbc); 
    … 

    这时,由于m_pMyRecordset的作用域是整个类,而传递的数据源参数是以引用方式传递的,所以,数据源对象m_MyDBOdbc不能定义为局部变量,否则,在CreateRecordset函数之外的其它函数中使用m_pMyRecordset时,由于m_MyDBOdbc在内存中已经销毁,所以会产生异常。一般可以将m_MyDBOdbc定义为成员变量来解决此问题。 
    ● 特殊注意事项 
    由于VC6.0开发环境本身还不是很完善,所以有时候会遇到意想不到的操作结果,或是与自己的预想不一样的情况,甚至会出现错误。 
    1) 在生成记录集时要指定记录集的类型,如动态集(dynaset),快照(snapshot)等。由于不同的ODBC驱动程序所支持的记录集类型不同,如果打开一个不支持的记录集类型时会出现异常。如果要在建立记录集后修改其类型,可以选择MFC ClassWizard的Member Variables标签页中的Update Columns,但是在VC6.0中,这样做并不能自动刷新表征记录集类型的成员变量(CRecordset::m_nDefaultTyp)。这时必须手动在记录集的构造函数中对m_nDefaultType重新赋值才行(比如把dynaset改为snapshot)。 
    2) 在给记录集添加查询参数时,要注意在函数DoFieldExchange中查询参数的绑定顺序,该顺序必须与定制记录集的m_strFilter属性时的顺序一致。VC的绑定参数的方法是:在函数DoFieldExchange函数中找到第一个绑定函数(以RFX_开头),以此作为第一个参数变量赋值的字段名,然后依次顺序往下进行。 
    如pSet->m_strFilter=” Field1=? and Field2=?”; 
    pSet->ParamField1=value1; 
    pSet->ParamField2=value2;//(参数变量赋值顺序不会产生任何不良影响) 
    则在记录集的DoFieldExchange函数中应以如下顺序定义参数 
    RFX_Long(pDX,”Field1”,ParamField1); 
    RFX_Long(pDX,”Field2”,ParamField2); 
    只要符合上述顺序,如果在后面再加上其他绑定函数,如 
    RFX_Long(pDX,”Field3”,ParamField3); 
    也不会影响,但是此函数不能放在最前面或插在中间其他地方,否则就打乱了定义的次序,从而造成变量赋值与字段名不匹配的问题。 
    4.结论 
    从以上对数据库访问的方式、程序结构及相关的编程技巧的介绍,可以看出,数据库访问的结构和程序结构是很重要的,只有在理解了这些概念后,才能在遇到实际困难时找到正确处理的办法。同样,由于数据库访问中的一些规则限制,所以数据库编程应当掌握一些基本的编程技巧和注意事项,这样才可以提高工作效率。
      

  2.   

    楼上两位老大,你们可能没看清楚我的意思。我是想编一道直接调用ODBC API的C++程序,而不通过VC的封装类CRecord等。我把程序改为以下情形就可以了,可是只能用printf输出,如果include <iostream.h>后,用cout却不行,这又为何?
    #include <stdio.h>
    //#include <iostream.h>
    #include <afxtempl.h>
    #include <sql.h>
    #include <sqlext.h>
    #include <sqltypes.h>
    void main()
    {
    SQLHANDLE   hEnv,
            hDbc,
        hStmt;
    SQLRETURN   reCode; reCode = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &hEnv);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n");
    reCode = SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n"); reCode = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n");
    reCode = SQLConnect(hDbc, (SQLTCHAR *)("stdreg_ex"), SQL_NTS, (SQLTCHAR *)("spanxzw"),SQL_NTS, (SQLTCHAR *)("1111111"), SQL_NTS);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n");    reCode = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n");
    reCode = SQLExecDirect(hStmt, (SQLTCHAR *)("select * from Student"), SQL_NTS);
    if(reCode == SQL_SUCCESS)
    printf("ok!\n"); while(SQLFetch(hStmt) != SQL_NO_DATA)
    {
    SQLINTEGER   studentID, gradYear, len1, len2, len3;
    SQLCHAR     name[40];

    SQLGetData(hStmt, 1, SQL_C_SLONG, &studentID, 0, &len1);
    SQLGetData(hStmt, 2, SQL_C_CHAR,  name, 40, &len2);
    SQLGetData(hStmt, 3, SQL_C_SLONG, &gradYear, 0, &len3);
    printf("%ld  %s  %ld\n", studentID, name, gradYear);
    //cout << studentID << "  " << name << "  " << gradYear;
    } reCode = SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    reCode = SQLDisconnect(hDbc);
    reCode = SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    reCode = SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
        
    }
      

  3.   

    必知道原因了,第二次的编译问题是多线程设置的缘故,在VC project->setting中将单线程改为多线程就可以了,只是不知是否因为ODBC API会启动多线程的缘故了?