软件课程设计 中期报告

华中科技大学电子科学与技术系

软件课程设计

中期进展报告

题目:学生成绩管理系统

  组长:

组员:

  组员:

  组员:

指导老师:

目录

1.   设计任务................................................ 1

2. 基本思路................................................ 1

3. 方案设计................................................ 1

3.1  主要算法说明................................................................................................................ 1

3.1.1 ADO基本介绍........................................................................................................ 4

3.1.2 ADO中的主要函数使用方法................................................................................... 8

3.2  程序框架设计............................................................................................................... 11

4. 程序的源代码........................................... 11

5. 调试过程中出现的问题及相应解决办法...................... 16

6. 总结................................................... 17

参考文献.................................................. 17


1.     设计任务

我们组选的题目是:学生成绩管理系统。中期基本完成了所要求的大部分功能,可以添加、修改、删除、查找、排序,并且设计了登陆界面。但是仍然存在很多不足的地方,比如说不能保存排序得到的结果,不能新建表,使用ADO访问数据库的时候遇到了很多困难。

打开登陆界面,可以输入学号和密码登陆;添加学生信息部分可以添加和修改学生的姓名、学号、年龄、各科成绩,也可以添加班级;修改部分除了和添加部分相同的项目基本相同;查找部分可以按学号或姓名查找;排序可以按照各科成绩或学号排序;删除部分可以删除整个学生信息;“显示全部”部分可以显示全部的学生信息。

2. 基本思路

在完成本次任务的过程中,为了使用VC++实现界面可视化,我们首先自学了关于MFC的一些知识,并且了解了visual studio 2008的基本操作。并且在后续的过程中使用了ADO技术访问数据库。

由于之前我们对MFC也不太了解,所以花了一些时间学习了如何新建MFC工程,如何添加消息响应函数,如何添加对话框资源,如何添加事件处理函数,如何在控件里添加内容等等。其中最关键的是登录对话框的设计,使用ADO技术访问数据库。

3. 方案设计

3.1  主要算法说明

在对话框中主要采用了列表框控件其中主要用到的函数为GetDlgItem(),GetFirstSelectedItemPosition(),GetNextSelectedItem(),DeleteAllItems(),InsertItem(),SetItemText(),SetItemState(),具体作用见下表

由于在任务中使用的ADO访问数据库,所以具体的查询排序添加等等函数都不需要自己写,只需要直接调用系统的函数就可以了。主要的函数包括OnAddclass()(添加班级记录该函数还未实现),OnAddcourse()(添加课程),OnFindname()(按姓名查找),OnFindnumber()(按学号查找),OnAddstudent()(添加学生信息),OnAll()(显示全部记录),OnDelete()(删除一个记录),OnModify()(修改学生信息),OnSortage()(按年龄排序)。中间所有的操作均采用ADO提供的方法来实现。

为了让更改能够动态在列表视图控件里动态显示,每次数据发生更改以后都需要调用CListCtrl的DeleteAllItems,然后再读取出记录集里面的内容。

例如OnModify函数的实现(只列出了部分完整的见代码的OnModify函数):

判断记录集是否已经打开

     if (!(m_pRecordset->State & adStateOpen))

     {

            AfxMessageBox((_T("记录集未打开。")));

            return;

     }

获取记录集的当前位置

     CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);

     POSITION pos = pListCtrl->GetFirstSelectedItemPosition();

     if (pos == NULL)

     {

            return;

     }

     int nItem = pListCtrl->GetNextSelectedItem(pos);//获取当前选中的位置

     CModifyDlg dlg;                               //实例化修改对话框的对象

     dlg.m_Name = pListCtrl->GetItemText(nItem, 1);    //获取修改前的内容

     if (dlg.DoModal() != IDOK)

     {

            return;

     }

     //在响应IDOK时候文本控件的值已经被更新

     m_pRecordset->MoveFirst();                //移动到第一条记录

     m_pRecordset->Move((long)nItem);           //移动到选中记录的位置

     m_pRecordset->PutCollect(_variant_t(_T("SNAME")), //修改记录的值

            _variant_t(dlg.m_Name));

     m_pRecordset->Update();                 //更新记录集中的内容

     while (!m_pRecordset->adoEOF)

     {

            varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME")));

            if (varValue.vt != VT_NULL)

            {

                   strName = varValue.bstrVal;

            }

            else

            {

                   strName = _T("");

            }

        //刷新ListCtrl

            CString strText = _T("");

            strText.Format(_T("%d"), n + 1); 

            pListCtrl->InsertItem(n, strText);//插入一行

            strText.Format(_T("%s"), strName);

            pListCtrl->SetItemText(n, 1, strText);  //设置一行的第一个子项的值

            n++;

            m_pRecordset->MoveNext();

     }

     return TRUE;

对程序中可能出现的算法的介绍,以及算法的实现方法。注意格式,要活用格式刷。

3.1.1 ADO基本介绍

ADO简介

微软公司的ADO (ActiveX Data Objects) 是一个用于存取数据源的COM组件。它提供了编程语言和统一数据访问方式OLE DB的一个中间层。允许开发人员编写访问数据的代码而不用关心数据库是如何实现的,而只用关心到数据库的连接。

使用ADO操作数据库的步骤:

(1)COM库的初始化

使用AfxOleInit()来初始化COM库,通常在CWinApp::InitInstance()的重载函数中完成。在InitInstance()函数中添加

     if(!AfxOleInit())         //这就是初始化COM库

     {

            AfxMessageBox(L"OLE初始化出错!");

            return FALSE;

     }

(2)用#import指令引入ADO类型库

我们在stdafx.h中加入如下语句:

#import "c:\program files\common files\system\ado\msado15.dll"\

        no_namespace\     rename("EOF", "adoEOF")

(3)创建Connection对象并连接数据库首先我们需要添加一个指向Connection对象的指针:

   _ConnectionPtr m_pConnection;

   _RecordsetPtr m_pRecordset;

   _CommandPtr m_pCommand;

创建连接

     if (!SUCCEEDED(m_pConnection.CreateInstance(__uuidof(Connection))))

     {

            m_pConnection = NULL;

            TRACE(_T("Database CreateInstance failed"));

     }

     if (!SUCCEEDED(m_pRecordset.CreateInstance(__uuidof(Recordset))))

     {

            m_pRecordset = NULL;

            TRACE(_T("Recordset CreateInstance Failed!"));

     }

     if (!SUCCEEDED(m_pCommand.CreateInstance(__uuidof(Command))))

     {

            m_pCommand = NULL;

            TRACE(_T("Command CreateInstance Failed!"));

     }

(4)打开记录集和数据库连接

为了方便打开数据库和记录集构造了两个函数

BOOL CMYDlg::OpenRecordset(LPCTSTR lpszSource, long nCursorType, long nLockType, long nOptions)

{

     ASSERT(m_pConnection != NULL);

     ASSERT(m_pRecordset != NULL);

     ASSERT(lpszSource != NULL);

     ASSERT(AfxIsValidString(lpszSource));

     //打开记录集

     try

     {

            return (SUCCEEDED(m_pRecordset->Open(_variant_t(lpszSource),

                                                                    m_pConnection.GetInterfacePtr(),

                                                                    (CursorTypeEnum)nCursorType,

                                                                    (LockTypeEnum)nLockType,

                                                                    nOptions)));

     }

     catch(_com_error e)

     {

            TRACE(_T("%s\n"), e.ErrorMessage());

            return FALSE;

     }

}

打开数据库

BOOL CMYDlg::OpenDatabase(LPCTSTR lpszConnect, long nOptions)

{

     ASSERT(m_pConnection != NULL);

     ASSERT(lpszConnect != NULL);

     ASSERT(AfxIsValidString(lpszConnect));

    

     //打开数据库连接

     try

     {

            return SUCCEEDED(m_pConnection->Open(_bstr_t(lpszConnect),

                   _T(""), _T(""), nOptions));

     }

     catch (_com_error& e)

     {

            TRACE(_T("%s\n"), e.ErrorMessage());

            return FALSE;

     }

}

另外还有对应的关闭数据库和记录集的函数。

//打开数据库

CString strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source= MY.mdb");

if (!OpenDatabase(strConnect))

{

AfxMessageBox(_T("数据库打开失败。"));

return;

}

//打开记录集

CString strSource = _T("SELECT * FROM MyTable");

if (!OpenRecordset(strSource))

{

AfxMessageBox(_T("记录集打开失败。"));

return;

}

(5)执行SQL命令

m_pCommand->put_ActiveConnection(_variant_t((IDispatch*)m_pConnection));

m_pCommand->ActiveConnection=m_pConnection;

CString strCommand= L"CREATE TABLE Persons(Id_P int,LastName varchar(255), FirstName varchar(255),Address varchar(255),City varchar(255))";    //使用sql命令创建新表

m_pCommand->CommandType=adCmdText;

m_pCommand->CommandText = _bstr_t(strCommand);

try

{

m_pCommand->Execute(NULL, NULL, adCmdUnknown);

}

catch (_com_error& e)

{

TRACE(_T("%s\n"), e.ErrorMessage());

return;

}

3.1.2 ADO中的主要函数使用方法

(1)Open函数

Open方法的原型是:

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

其中:

①Source是数据查询字符串

②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)

③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:

enum CursorTypeEnum

{

adOpenUnspecified = -1,

///不作特别指定

adOpenForwardOnly = 0,

///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用

adOpenKeyset = 1,

///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。

adOpenDynamic = 2,

///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。

adOpenStatic = 3

///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。

};

④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:

enum LockTypeEnum

{

adLockUnspecified = -1,

///未指定

adLockReadOnly = 1,

///只读记录集

adLockPessimistic = 2,

悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制

adLockOptimistc = 3,

乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作

adLockBatchOptimistic = 4,

乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。

};

(2)Find函数

索 Recordset 中满足指定标准的记录。如果满足标准,则记录集位置设置在找到的记录上,否则位置将设置在记录集的末尾。

语法

Find (criteria, SkipRows, searchDirection, start)

参数

criteria   字符串,包含指定用于搜索的列名、比较操作符和值的语句。

SkipRows    可选,长整型值,其默认值为零,它指定当前行或 start 书签的位移以开始搜索。

searchDirection    可选的 SearchDirectionEnum 值,指定搜索应从当前行还是下一个有效行开始。其值可为 adSearchForward 或 adSearchBackward。搜索是在记录集的开始还是末尾结束由 searchDirection 值决定。

start    可选,变体型书签,用作搜索的开始位置。

criteria 中的“比较操作符”可以是“>”(大于)、“<”(小于)、“=”(等于)、“>=”(大于或等于)、“<=”(小于或等于)、“<>”(不等于)或“like”(模式匹配)。

criteria 中的值可以是字符串、浮点数或者日期。字符串值以单引号分界(如“state = 'WA'”)。日期值以“#”(数字记号)分界(如“start_date > #7/22/97#”)。

如“比较操作符”为“like”,则字符串“值”可以包含“*”(某字符可出现一次或多次)或者“_”(某字符只出现一次)。(如“state like M_*”与 Maine 和 Massachusetts 匹配。)

关于函数的使用可以在ADO使用手册里找到。这里也不再详细介绍。

3.2  程序框架设计

这一部分可以是流程图,以及对流程图的说明。注意格式,要活用格式刷。

4. 程序的源代码

这里只列出两个比较关键的函数具体的函数代码比较繁琐,见文件源代码

显示所有记录的函数Read()

BOOL CMYDlg::Read()

{

       CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);

       pListCtrl->DeleteAllItems();   //删除列表空间里面的所有记录

       m_pRecordset->MoveFirst();

       int n = 0;

       while (!m_pRecordset->adoEOF)

       {

              _variant_t varValue;

              CString strName = _T("");

              int Age = 0 , LiangZi = 0 , ShuLi = 0 , MoDian = 0 , XinHao = 0 ,Number = 0;

              varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME")));  //获取字段的值

              if (varValue.vt != VT_NULL)

              {

                     strName = varValue.bstrVal;

              }

              else

              {

                     strName = _T("");

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("AGE")));

              if (varValue.vt != VT_NULL)

              {

                     Age = varValue.intVal;

              }

              else

              {

                     Age = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("NUMBER")));

              if (varValue.vt != VT_NULL)

              {

                     Number = varValue.intVal;

              }

              else

              {

                     Number = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("LIANGZI")));

              if (varValue.vt != VT_NULL)

              {

                     LiangZi = varValue.intVal;

              }

              else

              {

                     LiangZi = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("SHULI")));

              if (varValue.vt != VT_NULL)

              {

                     ShuLi = varValue.intVal;

              }

              else

              {

                     ShuLi = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("MODIAN")));

              if (varValue.vt != VT_NULL)

              {

                     MoDian = varValue.intVal;

              }

              else

              {

                     MoDian = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("XINHAO")));

              if (varValue.vt != VT_NULL)

              {

                     XinHao = varValue.intVal;

              }

              else

              {

                     XinHao = 0;

              }

              //刷新ListCtrl 在列表空间上显示记录

              CString strText = _T("");

              strText.Format(_T("%d"), n + 1);

              pListCtrl->InsertItem(n, strText);

              strText.Format(_T("%s"), strName);

              pListCtrl->SetItemText(n, 1, strText);

              strText.Format(_T("%d"), Age);

              pListCtrl->SetItemText(n, 2, strText);

              strText.Format(_T("%d"), Number);

              pListCtrl->SetItemText(n, 3, strText);

              strText.Format(_T("%d"), LiangZi);

              pListCtrl->SetItemText(n, 4, strText);

              strText.Format(_T("%d"), ShuLi);

              pListCtrl->SetItemText(n, 5, strText);

              strText.Format(_T("%d"), MoDian);

              pListCtrl->SetItemText(n, 6, strText);

              strText.Format(_T("%d"), XinHao);

              pListCtrl->SetItemText(n, 7, strText);

              n++;

              m_pRecordset->MoveNext();

       }

       return TRUE;

}

按照学号查找

void CMYDlg::OnFindnumber()

{

       // TODO: Add your command handler code here

       CFindNumberDlg dlg;

       if(dlg.DoModal()!=IDOK)

              return ;

       //查找条件

       CString strCriteria = _T("");

       strCriteria.Format(_T("NUMBER like '%d'"), dlg.m_Number);

//     int number = dlg.m_Number;

       //查找记录集

       CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);

       pListCtrl->DeleteAllItems();

       if (m_pRecordset->BOF && m_pRecordset->adoEOF)

       {

              return;

       }

       m_pRecordset->MoveFirst();

       int n = 0;

       m_pRecordset->Find(_bstr_t(strCriteria), 0, adSearchForward);     //从当前行开始向下查找

       while (!m_pRecordset->adoEOF)

       {

              _variant_t varValue;

              CString strName = _T("");

              int Number = 0, Age = 0 , LiangZi = 0 , ShuLi = 0 , MoDian = 0 , XinHao = 0 ;

              varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME")));

              if (varValue.vt != VT_NULL)

              {

                     strName = varValue.bstrVal;

              }

              else

              {

                     strName = _T("");

              }

                       //获取记录集的内容

              varValue = m_pRecordset->GetCollect(_variant_t(_T("AGE")));

              if (varValue.vt != VT_NULL)

              {

                     Age = varValue.intVal;

              }

              else

              {

                     Age = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("NUMBER")));

              if (varValue.vt != VT_NULL)

              {

                     Number = varValue.intVal;

              }

              else

              {

                     Number = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("LIANGZI")));

              if (varValue.vt != VT_NULL)

              {

                     LiangZi = varValue.intVal;

              }

              else

              {

                     LiangZi = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("SHULI")));

              if (varValue.vt != VT_NULL)

              {

                     ShuLi = varValue.intVal;

              }

              else

              {

                     ShuLi = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("MODIAN")));

              if (varValue.vt != VT_NULL)

              {

                     MoDian = varValue.intVal;

              }

              else

              {

                     MoDian = 0;

              }

              varValue = m_pRecordset->GetCollect(_variant_t(_T("XINHAO")));

              if (varValue.vt != VT_NULL)

              {

                     XinHao = varValue.intVal;

              }

              else

              {

                     XinHao = 0;

              }

              //刷新ListCtrl,在控件上显示查找得到的记录

              CString strText = _T("");

              strText.Format(_T("%d"), n + 1);

              pListCtrl->InsertItem(n, strText);

              strText.Format(_T("%s"), strName);

              pListCtrl->SetItemText(n, 1, strText);

              strText.Format(_T("%d"), Age);

              pListCtrl->SetItemText(n, 2, strText);

              strText.Format(_T("%d"), Number);

              pListCtrl->SetItemText(n, 3, strText);

              strText.Format(_T("%d"), LiangZi);

              pListCtrl->SetItemText(n, 4, strText);

              strText.Format(_T("%d"), ShuLi);

              pListCtrl->SetItemText(n, 5, strText);

              strText.Format(_T("%d"), MoDian);

              pListCtrl->SetItemText(n, 6, strText);

              strText.Format(_T("%d"), XinHao);

              pListCtrl->SetItemText(n, 7, strText);

              n++;

//            m_pRecordset->MoveNext();

              m_pRecordset->Find(_bstr_t(strCriteria), 1, adSearchForward);        //跳过当前行向下查找

       }

}

5. 调试过程中出现的问题及相应解决办法

1,程序最初中需要的问题包括汉字使用导致的编译出现问题。

解决办法:在需要输入汉字的地方加上L,或者_T例如L"数据库未打开",_T("记录集已打开")

2,在Access中新建表格的时候输入字段错误,导致程序运行的时候出现问题。由于调试不能发现错误,所以需要自己检查,最后自己花费了很长时间检查才发现了这样的错误。

3,执行SQL语句出现错误,由于自己对于sql也不太熟悉,所以编写sql语句的时候出现了问题。后来通过自己简单学习了一点sql的知识,能够成功编写基本sql语句。

4,使用_CommandPtr指针执行sql的设置出现问题。由于我对ado的执行方法也是初次接触,所以到网上查询了好长时间最终找到了一种适合自己的方法。具体成功以后的代码见OnSortage()函数。

5,设计登录界面的时候遇到的一个很麻烦的问题,如何设置部分控件的背景为透明,使它们显示在背景图像上面而不会遮住背景。到网上搜索了并且自己尝试了很多种办法,最后发现除了需要使用SetBkMode(TRANSPARENT);设置以外还必须得勾选控件的sample才行。不过后来我自己也发现直接使用TextOut()函数也是很方便的。

6. 总结

中期实验的过程是项目的关键部分,虽然距离完成全部任务还是有一些距离,但却是项目的骨架。期间遇到了很多难以解决的困难,需要花费更多的时间到网上查找资料,阅读相关书籍。但是在调试程序的过程中,大家确实学习到了很多知识。比如说关于MFC的基本操作,比如使用ADO数据库,比如自己独立发现问题、检索资料和解决问题的能力。

其中有很多问题,在网上是可以找到答案的。但是关于MFC其实比较方便的就是msdn了,里面给出了每一个函数的十分详细具体的介绍。网上查到的资料大都是翻译的msdn。ado也有专门的说明。这些都对我们的项目进展有很大的帮助。

写代码的过程中经常会出现编译错误或者得不到自己预期的效果,其中很大部分都是由于粗心大意造成的。比如说变量名字拼写错误,把代码从一处复制粘贴的时候忘记了修改关键的部分等等。

另外,项目的大部分工作均由徐洁同学完成,小组其他成员应该多多向他学习,为项目的完成做出更多的贡献。

参考文献

【1】http://blog.csdn.net/tangbo6/article/details/740814   vc下ado开发实践

【2】http://hi.baidu.com/pecoco/blog/item/e05ee5ccffd4d72bf8dc61f4.html  VC中利用ADO共同实现数据库的操作

【3】《精通——Visual C++实效编程280例》作者:曲扬 编著 ,出 版 社:人民邮电出版社出版,时间:20##-5-1

【4】《VC++深入详解》作者:孙鑫,余安萍 编著,出 版 社:电子工业出版社出版,时间:20##-6-1

相关推荐