• 安全传输平台项目——配置管理终端-读写数据库


    在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    10-安全传输平台项目-第10天(配置管理终端-读写数据库)

    目录:
    一、复习
    二、安全传输平台项目——配置管理终端-读写数据库
    1、连接数据库
    2、管理终端操作数据库原理
    3、服务器配置参数管理界面布局
    4、记录类的概念
    5、生成记录类
    6、记录类常用API
    7、借助记录类读取数据库
    8、记录类查询
    9、总结
    10、写数据库实现思路
    11、写数据库实现
    12、踩内存错误
    13、网点信息管理-初始化
    14、网点信息管理-创建网点
    15、网点信息管理-查询网点信息
    16、网点信息管理-删除网点和修改网点介绍

    一、复习

    1、对接开源框架相关
    2、多态
    3、管理终端的功能模块——系统初始化

    二、安全传输平台项目——配置管理终端-读写数据库

    1、连接数据库

    不论配置文件是否存在,只要读取成功,都应该连接数据库。

    在SecMngAdmin.cpp中InitInstance的调用:

    >BOOL CSecMngAdminApp::InitInstance()

    BOOL CSecMngAdminApp::InitInstance()
    {
        int ret = 0;
        CWinApp::InitInstance();
    
        EnableTaskbarInteraction(FALSE);
    
        // 使用 RichEdit 控件需要 AfxInitRichEdit2()    
        // AfxInitRichEdit2();
    
        // 弹出对话框供用户输入 信息
        CDlgInitCfg dlgInitCfg;
    
        // 标准初始化
        ret = readSecMngCfg();
        if (ret != 0) 
        {
            AfxMessageBox("配置文件不存在,请输入");
            if (dlgInitCfg.DoModal()== IDCANCEL)
            {
                return FALSE;
            }
            g_dbSource = dlgInitCfg.m_dbDSN;
            g_dbUser = dlgInitCfg.m_dbUID;
            g_dbpwd = dlgInitCfg.m_dbPWD;
        }
    
        // 借助全局变量 ----连接数据库
        ret = CSecMngAdminApp::NewOdbc_Connet();
        if (ret != 0) 
        {
            AfxMessageBox("连接数据库失败");
            return FALSE;
        }
        
        .....
    }
    BOOL CSecMngAdminApp::InitInstance()

    然后实现 int CSecMngAdminApp::NewOdbc_Connet()函数:

    int CSecMngAdminApp::NewOdbc_Connet()
    {
        g_pDB = &myDB;            // g_pDB 代表一条数据库连接,用于数据库操作
        CString strCon;
    
        TRY
        {
            strCon.Format("DSN=%s;UID=%s;PWD=%s", g_dbSource, g_dbUser, g_dbpwd);
    
            if (g_pDB->OpenEx(strCon, CDatabase::noOdbcDialog) == FALSE)
            {
                return -1;
            }
        }
        CATCH_ALL(e)
        {
            e->ReportError();
        }
        END_CATCH_ALL
    
        return 0;
    }
    int CSecMngAdminApp::NewOdbc_Connet()

    在SecMngAdmin.h的类CSecMngAdminApp中声明:

    public:
        int NewOdbc_Connet();

    注意:直 接 使用 全局变量 myDB 和 g_pDB; 方便后续逻辑获取数据。

        CDataBase myDB;
        CDataBase *g_pDB = &myDB; 

    2、管理终端操作数据库原理

    以第2个(服务器启动参数配置)为例讲解,第1和3实现类似,但是第1个(数据源参数配置)点击“保存”时需要验证!

    3、服务器配置参数管理界面布局

    修改 IDD_DIALOG_CFG 界面 ( 配置管理界面 )成 demo 中的样子:

    4、记录类的概念

    IDD_DIALOG_CFG 界面:
        服务器后台配置参数的ID:

            >配置信息:
            数据源:
            用户名称:
            密码: 

           >服务器启动参数配置:
            服务器IP:    IDC_EDIT_IP
            服务器端口:    IDC_EDIT_PORT
            最大网点个数:    IDC_EDIT_MAXNODE

      修改 “保存”按钮 ID:IDOK_SRVCFG

           >网点信息配置:
            网点编号:
            网点名称:
            网点描述:

    针对第2个,添加类成员变量:

    IDC_EDIT_IP、IDC_EDIT_PORT、IDC_EDIT_MAXNODE依次添加成员变量,类型和变量名为:

            CString m_strsrvip
            CString m_strsrvport
            CString m_strsrvmaxnode

    可以切换到“类视图”,在CCfgView类中检查增加了3个成员变量:

    》wind连接数据库流程分析及记录类说明:

    要理解:一旦记录类的函数操作成功,一定会把表中的数据存储到记录类的变量中。

    5、生成记录类

    (1)切换到“类视图”,在左侧随便选择一个类,右键选择“类向导”,在左侧“添加类”处下拉,选择“MFC ODBC 使用者”:

    (2)在“MFC ODBC 使用者”中点击“数据源”,然后弹出“选择数据源”,选择“机器数据源”,点击“itcast411”,点击“确定”:

    (3)弹出“Oracle ODBC Driver Connect”,输入用户名和密码,点击“OK”:

    (4)弹出“选择数据库对象”,然后选择用户“SECMNG”,选择“SEVCFG”表,然后点击“确定”:

    (5)然后出现下图对话框,可以看到自动生成了CSECMNGSRVCFG类和SECMNGSRVCFG.h头文件和SECMNGSRVCFG.cpp:

    (6)弹出下图“安全警告”,点击“确定”。注意:此处警告我们生成的代码包含明文密码。

    (7)编译代码,报错如下:

    (8)双击错误,定位到错误,然后注释掉#error

    注意:这里转换后的密码是明文的,可以通过base64或自己的加密方式将其加密掩盖住!

    (9)重新编译代码,成功。

    》注意:因为环境不同,如果遇到无法打开数据源,可以直接使用这两个文件:

    >SECMNGSRVCFG.h

    // SECMNGSRVCFG.h : CSECMNGSRVCFG 的声明
    
    #pragma once
    
    
    class CSECMNGSRVCFG : public CRecordset
    {
    public:
        CSECMNGSRVCFG(CDatabase* pDatabase = NULL);
        DECLARE_DYNAMIC(CSECMNGSRVCFG)
    
    // 字段/参数数据
    
    // 以下字符串类型(如果存在)反映数据库字段(ANSI 数据类型的 CStringA 和 Unicode
    // 数据类型的 CStringW)的实际数据类型。
    //  这是为防止 ODBC 驱动程序执行可能
    // 不必要的转换。如果希望,可以将这些成员更改为
    // CString 类型,ODBC 驱动程序将执行所有必要的转换。
    // (注意: 必须使用 3.5 版或更高版本的 ODBC 驱动程序
    // 以同时支持 Unicode 和这些转换)。
    
        CString    m_KEY;
        CString    m_VALUDE;
    
    // 重写
    // 向导生成的虚函数重写
        public:
        virtual CString GetDefaultConnect();    // 默认连接字符串
    
        virtual CString GetDefaultSQL();     // 记录集的默认 SQL
        virtual void DoFieldExchange(CFieldExchange* pFX);    // RFX 支持
    
    // 实现
    #ifdef _DEBUG
        virtual void AssertValid() const;
        virtual void Dump(CDumpContext& dc) const;
    #endif
    
    };
    SECMNGSRVCFG.h

    >SECMNGSRVCFG.c

    // SECMNGSRVCFG.h : CSECMNGSRVCFG 类的实现
    
    
    // CSECMNGSRVCFG 实现
    
    #include "stdafx.h"
    #include "SECMNGSRVCFG.h"
    IMPLEMENT_DYNAMIC(CSECMNGSRVCFG, CRecordset)
    
    CSECMNGSRVCFG::CSECMNGSRVCFG(CDatabase* pdb)
        : CRecordset(pdb)
    {
        m_KEY = "";
        m_VALUDE = "";
        m_nFields = 2;
        m_nDefaultType = dynaset;
    }
    //#error 安全问题:连接字符串可能包含密码。
    // 此连接字符串中可能包含明文密码和/或其他重要
    // 信息。请在查看完此连接字符串并找到所有与安全
    // 有关的问题后移除 #error。可能需要将此密码存
    // 储为其他格式或使用其他的用户身份验证。
    CString CSECMNGSRVCFG::GetDefaultConnect()
    {
        return _T("DSN=SECMNGADMIN;UID=SECMNGADMIN;PWD=123456;DBQ=SECMNGADMIN;DBA=W;APA=T;EXC=F;FEN=T;QTO=T;FRC=10;FDL=10;LOB=T;RST=T;GDE=F;FRL=F;BAM=IfAllSuccessful;NUM=NLS;DPM=F;MTS=T;MDI=F;CSR=F;FWC=F;FBS=64000;TLO=0;");
    }
    
    CString CSECMNGSRVCFG::GetDefaultSQL()
    {
        return _T("[SECMNG].[SRVCFG]");
    }
    
    void CSECMNGSRVCFG::DoFieldExchange(CFieldExchange* pFX)
    {
        pFX->SetFieldType(CFieldExchange::outputColumn);
    // RFX_Text() 和 RFX_Int() 这类宏依赖的是
    // 成员变量的类型,而不是数据库字段的类型。
    // ODBC 尝试自动将列值转换为所请求的类型
        RFX_Text(pFX, _T("[KEY]"), m_KEY);
        RFX_Text(pFX, _T("[VALUDE]"), m_VALUDE);
    
    }
    /////////////////////////////////////////////////////////////////////////////
    // CSECMNGSRVCFG 诊断
    
    #ifdef _DEBUG
    void CSECMNGSRVCFG::AssertValid() const
    {
        CRecordset::AssertValid();
    }
    
    void CSECMNGSRVCFG::Dump(CDumpContext& dc) const
    {
        CRecordset::Dump(dc);
    }
    #endif //_DEBUG
    SECMNGSRVCFG.cpp

    6、记录类常用API

    简单分析了SECMNGSRVCFG.h基类CSECMNGSRVCFG和父类CRecordset中记录(表)打开(Open)、是否有记录(IsEOF)、是否为空(IsEmpty)、新增数据(AddNew后一定要Update)、游标(表比较简单,暂不需要)等。

    7、借助记录类读取数据库

    在“类视图”的左侧“CCfgView”,右键“类向导”,重写虚函数OnInitialUpdate:

    会添加CfgView.h和CfgView.cpp及OnInitialUpdate函数

    8、记录类查询

    (1)在CfgView.h中添加头文件:#include "SECMNGSRVCFG.h";

    (2)在CfgView.cpp中实现OnInitialUpdate函数:

    >void CCfgView::OnInitialUpdate()完整代码:

    extern CDatabase *g_pDB;
    
    void CCfgView::OnInitialUpdate()
    {
        CFormView::OnInitialUpdate();
        // TODO: 在此添加专用代码和/或调用基类
    
        CSECMNGSRVCFG srvCfgSet(g_pDB);            // 数据库表名 SRVCFG
    
    
        // 查询 SRVCFG 表 secmng_server_ip
        srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_ip"); // where 子句
        if (!srvCfgSet.Open(CRecordset::snapshot, NULL, CRecordset::none))    // select * from
        {
            AfxMessageBox("记录类打开数据库查询失败。");
            return ;
        }
        if (!srvCfgSet.IsEOF())                // 有结果
        {
            srvCfgSet.m_VALUDE.TrimLeft();
            srvCfgSet.m_VALUDE.TrimRight();
            m_strsrvip = srvCfgSet.m_VALUDE;
        }
        else
        {
            m_strsrvip = "";
        }
    
        // 查询 SRVCFG 表 secmng_server_port
        srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_port");
        if (!srvCfgSet.Requery())
        {
            AfxMessageBox("Requery port 查询失败。");
            return ;
        }
        if (!srvCfgSet.IsEOF())                // 有结果
        {
            srvCfgSet.m_VALUDE.TrimLeft();
            srvCfgSet.m_VALUDE.TrimRight();
            m_strsrvport = srvCfgSet.m_VALUDE;
        }
        else
        {
            m_strsrvport = "";
        }
    
        // 查询 SRVCFG 表 secmng_server_maxnetnum
        srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_maxnetnum");
        if (!srvCfgSet.Requery())
        {
            AfxMessageBox("Requery maxnode 查询失败。");
            return;
        }
        if (!srvCfgSet.IsEOF())                // 有结果
        {
            srvCfgSet.m_VALUDE.TrimLeft();
            srvCfgSet.m_VALUDE.TrimRight();
            m_strsrvmaxnode = srvCfgSet.m_VALUDE;
        }
        else
        {
            m_strsrvmaxnode = "";
        }
    
        UpdateData(FALSE);
    }
    void CCfgView::OnInitialUpdate()

    掌握如何拼接"select * from SECMNG.SRVCFG where 'key = secmng_server_ip'"?如何查询?

    9、总结

    ============ 连接数据库 ============================================
    ----连接数据库:
        添加 int CSecMngAdminApp::NewOdbc_Connet() 函数。     MyAdmin.cpp   MyAdmin.h

      CDataBase myDB;
            CString strCon.format("DSN=%s;uid=%s;pwd=%s", dsn,UID, pwd)  --- ODBC
            myDB.openEx(strCon);
        在 InitInstance() 函数的 中 ReadSecMngCfg(); 之后,调用 ConnectByodbc() 函数 连接数据库。 并判断。
        实现 ConnectByodbc函数,修改 strCon.Format() 函数内部调用,使用全局变量。
        直 接 使用 全局变量 myDB 和 g_pDB; 方便后续逻辑获取数据。
        CDataBase myDB;
        CDataBase *g_pDB = &myDB;

    ===========参数配置管理 ============================================
    ----界面:
        修改 IDD_DIALOG_CFG 界面 ( 配置管理界面 )成我们 demo 中的样子:
        服务器后台配置参数--------------------
            配置信息:
            数据源:
            用户名称:
            密码:
            服务器启动参数配置:
            服务器IP:    IDC_EDIT_IP
            服务器端口:    IDC_EDIT_PORT
            最大网点个数:    IDC_EDIT_MAXNODE
            网点信息配置:
            网点编号:
            网点名称:
            网点描述:

    =======通过odbc驱动 【读】 数据库============================================
    select * from SRVCFG where key = 'secmng_server_ip';  --IP
    select * from SRVCFG where key = 'secmng_server_port';  --Port
    -----用odbc机制,生成 关联类 操作数据库。
        -----借助odbc驱动 操作数据库 【原理】 :
            VS编译器 会给我生成一个【记录类】:就是数据库中的表,在MFC程序中的映射。
            记录类对象 <-------> 保存数据库 (把C++对象数据 持久化,保存到数据库中
        -----生成记录类:对应两个文件:SECMNGSRVCFG.cpp     SECMNGSRVCFG.h
        使用“类向导” 生成 数据库中 SRVCFG 表对应的记录类 ---> CSECMNGSRVCFG
            生成方式参见:讲义
        查看 记录类头文件:SECMNGSRVCFG.h:        SECMNGSRVCFG.CPP:
            CSECMNGSRVCFG 类有两个 成员变量: CStringA m_KEY 和 CStringA m_VALUDE
            CSECMNGSRVCFG 类的父类: CRecordset :       
                            包含:    构造函数使用时需要一个连接。CDatabase* pDatabase = NULL
                                open()函数。
                                游标操作://cursor operations 
                                       void MoveNext(); ......
                                AddNew(); Edit(); Update(); Delete();
        CStringA --> CString;
        #error --> //#error                        

    -----使用关联类:
        在 CfgView.cpp 中引入头文件: #include "SECMNGSRVCFG.h"
        给 IDD_DIALOG_CFG(对应 CfgView 类)中的 IP、端口、网点个数。  使用“类向导”关联成员变量:
        CfgView.h 中会自动添加下面的变量:
            CString m_strsrvip
            CString m_strsrvport
            CString m_strsrvmaxnode

    -----使用变量操作数据库, 查询IP :
        在 CCfgView 类上,使用“类向导”重载 OnInitialUpdate 函数。
        extern CString *g_pDB;             声明全局变量
        CSECMNGSRVCFG srvCfgSet(g_pDB);     使用全局变量 定义对象 srvCfgSet。并传参入 连接数据库用的连接串 g_pDB。
        正常查 SRVCFG 表中 服务器ip SQL语句:    select * from secmng.srvcfg where key = 'secmng_server_ip';
        在记录类中,表达该功能使用:    srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_ip");  是一种odbc固定转换SQL的语法

        Open() 函数:
            参1:4种取值:
                dynaset,        // uses SQLExtendedFetch, keyset driven cursor
                snapshot,       // uses SQLExtendedFetch, static cursor         快照
                forwardOnly,    // uses SQLFetch
                dynamic
            参2: NULL    
                表示使用 select *
            参3:enum OpenOptions { 12 种取值 }
            srvCfgSet.Open(CRecordset::snapshot, NULL, CRecordset::none)  表示按照过虑条件使用 select * 查询。
            如果有结果, 成员 m_KEY、m_VALUDE 中则包含值。  

            【注意 空格】: srvCfgSet.m_KEY.TrimLeft(); srvCfgSet.m_KEY.TrimRight();

        使用 TRY...catch... 捕获异常
        TRY
        {

        }
        CATCH_ALL(e)
        {
            e->ReportError();
            return;
        }
        END_CATCH_ALL;

        srvCfgSet.TrimLeft/Right()
        m_strsrvip = srvCfgSet.m_VALUDE;  使用变量承接 value 值。
        UpdateData(FALSE);  将变量值,传递给 界面显示。

    -------查询 port:
        由于查询的是同一张表,不应该打开多次。而应一次打开,反复查询。
        srvCfgSet.Requery(); 使用这个函数,可以完成该功能。
        但,应该在此之前,修改过滤器:    srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_port");
        if (!srvCfgSet.IsEOF()) 判断是否到达末尾位置。(验证数据库是否中有数据值)

    ------反复查询 易出现问题
        win 平台odbc驱动,对 Oracle 9 以后的数据库支持不稳定。
        srvCfgSet.Requery();调用的时候,常会出现崩溃错误

    10、写数据库实现思路

    =========通过odbc驱动 【写】 数据库==============================================

    ------用到的函数:

        srvCfgSet.m_strFilter.Format
        srvCfgSet.Open(CRecordset::snapshot, NULL, CRecordset::none)  
        g_pDB->BeginTrans();            开启事务。                   
        g_pDB->Rollback();            回退事务。
        g_pDB->CommitTrans();            提交事务。
        srvCfgSet.IsEOF()            判断是否有数据。【1】       
            有:update --- SQL
                srvCfgSet.Edit();            修改该数据。【2】
                srvCfgSet.m_VALUDE = m_strsrvip;    修改成新值。
                srvCfgSet.Update();            更新数据。 【3】
            无:insert--SQL
                srvCfgSet.AddNew();            添加新值。【4】           
                srvCfgSet.m_KEY = "secmng_server_ip"    根据KEY确定位置。
                srvCfgSet.m_VALUDE = m_strsrvip;    修改成新值。
                srvCfgSet.Update();            更新数据。

    -------修改 “保存”按钮回调函数:
        ID:IDC_BUTTON_SERVERCFGSAVE
        UpdateData(TRUE); 获取界面的值保存到变量,并判断是否为空值。
        开始事务。g_pDB->BeginTrans();
        借助数据库连接串g_pDB ,创建关联类对象。    CSECMNGSRVCFG srvCfgSet(g_pDB);
        设置过滤器:    srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_ip");
        按过滤条件,使用查询语句,打开查询。    srvCfgSet.Open(CRecordset::snapshot, NULL, CRecordset::none)
        srvCfgSet.IsEOF() 判断是否有数据修改。   
            有:
                srvCfgSet.Edit();    修改该数据。           
                srvCfgSet.m_VALUDE = m_strsrvip;    修改成新值。
                srvCfgSet.Update();            更新数据
            没有:
                srvCfgSet.AddNew();            添加新值。                   
                srvCfgSet.m_KEY = "secmng_server_ip"    根据KEY确定位置。
                srvCfgSet.m_VALUDE = m_strsrvip;    修改成新值。
                srvCfgSet.Update();            更新数据。
        重新修改过滤器, 查询port。    srvCfgSet.m_strFilter.Format("key  = '%s'", "secmng_server_port");
        srvCfgSet.Requery();     再次执行查询。
        根据 srvCfgSet.IsEOF() 判断是否有数据修改。      有:Edit();、修改 m_VALUDE、Update();
                                无:AddNew(); 、修改 m_KEY 和 m_VALUDE、Update();
        再修改过滤器, 查询 最大网点个数maxNode。    srvCfgSet.m_strFilter.Format("key  = '%s'", "secmng_server_port");
        srvCfgSet.Requery();     再次执行查询。
        根据 srvCfgSet.IsEOF() 判断是否有数据修改。      有:Edit();、修改 m_VALUDE、Update();
                                无:AddNew(); 、修改 m_KEY 和 m_VALUDE、Update();
        提交事务 或 rollback 回滚事务。   
        srvCfgSet.Close(); 关闭连接。【坑!】

    11、写数据库实现

    切换到“资源视图”,在“Dialog”下选择“IDD_DIALOG_CFG”,双击“保存”按钮,在CfgView.cpp中写保存按钮的回调函数:

    >void CCfgView::OnBnClickedSrvcfg()

    void CCfgView::OnBnClickedSrvcfg()
    {
        int dbflg = 0;
    
        // TODO: 在此添加控件通知处理程序代码
        UpdateData(TRUE);
    
        if (m_strsrvip.IsEmpty() || m_strsrvmaxnode.IsEmpty() || m_strsrvport.IsEmpty())
        {
            AfxMessageBox("输入数据不允许为空值");
            return;
        }
    
        CSECMNGSRVCFG srvCfgSet(g_pDB);            // 数据库表名 SRVCFG
    
        g_pDB->BeginTrans();//开启事务
    
        TRY
        {
            // 查询 SRVCFG 表 secmng_server_ip
            srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_ip"); // where 子句
            if (!srvCfgSet.Open(CRecordset::snapshot, NULL, CRecordset::none))    // select * from
            {
                AfxMessageBox("记录类打开数据库查询失败。");
                return;
            }
            if (!srvCfgSet.IsEOF())                // 有结果 -- update
            {
                srvCfgSet.Edit();                // 打招呼
            }
            else
            {
                srvCfgSet.AddNew();                // 打招呼
            }
            srvCfgSet.m_KEY = "secmng_server_ip";
            srvCfgSet.m_VALUDE = m_strsrvip;
            srvCfgSet.Update();                    // 提交更新。
    
            // 查询 SRVCFG 表 secmng_server_port
            srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_port"); // where 子句
            if (!srvCfgSet.Requery())    // select * from
            {
                AfxMessageBox("Requery数据库查询 port 失败。");
                return;
            }
            if (!srvCfgSet.IsEOF())                // 有结果 -- update
            {
                srvCfgSet.Edit();                // 打招呼
            }
            else
            {
                srvCfgSet.AddNew();                // 打招呼
            }
            srvCfgSet.m_VALUDE = m_strsrvport;
            srvCfgSet.Update();                    // 提交更新。
    
            // 查询 SRVCFG 表 m_strsrvmaxnode
            srvCfgSet.m_strFilter.Format("key = '%s'", "secmng_server_maxnetnum"); // where 子句
            if (!srvCfgSet.Requery())    // select * from
            {
                AfxMessageBox("Requery数据库查询 maxnode 失败。");
                return;
            }
            if (!srvCfgSet.IsEOF())                // 有结果 -- update
            {
                srvCfgSet.Edit();                // 打招呼
            }
            else
            {
                srvCfgSet.AddNew();                // 打招呼
            }
            srvCfgSet.m_VALUDE = m_strsrvmaxnode;
            srvCfgSet.Update();                    // 提交更新。
        }
        CATCH_ALL(e)
        {
            e->ReportError();
            dbflg = 1;
        }
        END_CATCH_ALL;
    
        if (dbflg == 0) 
        {
            g_pDB->CommitTrans(); //提交事务
            AfxMessageBox("保存新数据到数据库成功");
        }
        else
        {
            g_pDB->Rollback(); //回滚
        }
        
        if (srvCfgSet.IsOpen())
        {
            srvCfgSet.Close();
        }    
    
    }
    void CCfgView::OnBnClickedSrvcfg()

    这时候运行,会报错:(先点击“忽略”,待解决。)

    12、踩内存错误

    上步的错误是踩内存错误,Debug调试发现这是由于执行到记录类的析构时发生的错误,但是数据库可以更新正常,而且win7、win8执行不会报这个错误,win10暂时未找到解决方法!

    13、网点信息管理-初始化

    (1)创建Dialog(ID:IDC_LIST_SECNODE)布局如下,提示—创建日期的控件为:Date Time Picker;

    (2)使用“类向导”添加“成员变量”:

    注意:自定义变量m_imageList不是通过类向导定义的,是通过代码定义的!

    (3)“类向导”中重写OnInitialUpdate函数:

    代码见DlgNetInfo.cpp和DlgNetInfo.h中void CDlgNetInfo::OnInitialUpdate()

    (4)添加资源:secnode_images.bmp

    14、网点信息管理-创建网点

    (1)实现“创建网点”的回调函数void CDlgNetInfo::OnBnClickedButton1()

    (2)封装函数int CDlgNetInfo::DbInitListSecNode(CString &ID, CString &Name,  CTime &time, int state, int authcode)

    15、网点信息管理-查询网点信息

    (1)实现“查询”按钮的回调函数void CDlgNetInfo::OnBnClickedButtonSearch()

    1)需要结合时间段查询的复选框(对于小型功能的控件,MFC提供了DlgItem类

    2)需要记录类,添加的是SECNODE表,生成SECMNGSECNODE.h和SECMNGSECNODE.cpp

    注意:此处的图片是16*16*8像素的,不是一个,是8个图标,以后开发如果有此需求,查询:MFC如何引入图片列表。

    (2)新增“修改网点”的Dialog及功能分析:

    由于“修改网点”实际包含了“创建网点”、“删除网点”和“查询”的功能,所以最后实现!

    对于新增的Dialog中“确定”按钮功能分析:

    16、网点信息管理-删除网点和修改网点介绍

    (1)实现“删除网点”的回调函数void CDlgNetInfo::OnBnClickedButton2()

    1)获取被选中行的位置pos;

    2)得到选中行的行号Item,从0开始;

    3)根据Item删除此行;

    注意:数据库和界面都需要删除,先删除数据库!!!

    4)为方便用户,实际需要根据编号和网点名称给出提示弹框:

    (2)实现“修改网点”的回调函数void CDlgNetInfo::OnBnClickedButton4()

    “修改网点”实际包含了“创建网点”、“删除网点”和“查询”的功能!

    void CDlgNetInfo::OnBnClickedButton4()
    {
        // TODO: 在此添加控件通知处理程序代码
    
        // 1. 获取 pos  POSITION pos = m_listSecNode.GetFirstSelectedItemPosition();
        // 2. 获取 item int nItem = m_listSecNode.GetNextSelectedItem(pos);    
        // 3. 获取文本 strID = m_listSecNode.GetItemText(nItem, 0);
        /*
                name = m_listSecNode.GetItemText(nItem, 1);
                time = m_listSecNode.GetItemText(nItem, 2);
                node = m_listSecNode.GetItemText(nItem, 3);
                state = m_listSecNode.GetItemText(nItem,4);
        */
        // 4. 在Dialog中展示  iD/name/time/node/state
        // 5. 用户重新编辑 iD/name/time/node/state 保存。
    
        // 6. 给保存按钮添加回调:{
                // 1. updatedate(T)   ---》 dialog 的成员变量中
                // 2. 创建记录类对象    format  open  isEoF()     Edit()-update();  更新数据库
                // 3. 写入listcontrl界面
            
        // 7. 更新完成。
    }

    注意:重点是理解如何把界面的数据存储到后台、文件(xxx.ini)和数据库的思想!掌握如何把控件提取出来的方法,以后在H5、QT、MFC等平台开发才会不受平台限制。

    》涉及代码如下:

    >DlgNetInfo.h

    #pragma once
    #include "afxcmn.h"
    #include "ATLComTime.h"
    
    
    
    // CDlgNetInfo 窗体视图
    
    class CDlgNetInfo : public CFormView
    {
        DECLARE_DYNCREATE(CDlgNetInfo)
    
    protected:
        CDlgNetInfo();           // 动态创建所使用的受保护的构造函数
        virtual ~CDlgNetInfo();
    
    public:
        enum { IDD = IDD_DIALOG_NETMNG };
    #ifdef _DEBUG
        virtual void AssertValid() const;
    #ifndef _WIN32_WCE
        virtual void Dump(CDumpContext& dc) const;
    #endif
    #endif
    
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    
        DECLARE_MESSAGE_MAP()
    public:
        CListCtrl m_listSecNode;
        afx_msg void OnBnClickedButton1();
        virtual void OnInitialUpdate();
    
    public:
        CImageList        m_imageList;
        int CDlgNetInfo::DbInitListSecNode(CString &ID, CString &Name, CTime &time, int state, int authcode);
        afx_msg void OnBnClickedButtonSearch();
        COleDateTime m_dateBegin;
        COleDateTime m_dateEnd;
        afx_msg void OnBnClickedButton2();
        afx_msg void OnBnClickedButton4();
    };
    DlgNetInfo.h

    >DlgNetInfo.cpp

    // DlgNetInfo.cpp : 实现文件
    //
    
    #include "stdafx.h"
    #include "MyAdmin.h"
    #include "DlgNetInfo.h"
    #include "SECMNGSECNODE.h"
    
    // CDlgNetInfo
    
    IMPLEMENT_DYNCREATE(CDlgNetInfo, CFormView)
    
    CDlgNetInfo::CDlgNetInfo()
        : CFormView(CDlgNetInfo::IDD)
        , m_dateBegin(COleDateTime::GetCurrentTime())
        , m_dateEnd(COleDateTime::GetCurrentTime())
    {
    
    }
    
    CDlgNetInfo::~CDlgNetInfo()
    {
    }
    
    void CDlgNetInfo::DoDataExchange(CDataExchange* pDX)
    {
        CFormView::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_LIST_SECNODE, m_listSecNode);
        DDX_DateTimeCtrl(pDX, IDC_DATETIMEPICKER1, m_dateBegin);
        DDX_DateTimeCtrl(pDX, IDC_DATETIMEPICKER2, m_dateEnd);
    }
    
    BEGIN_MESSAGE_MAP(CDlgNetInfo, CFormView)
        ON_BN_CLICKED(IDC_BUTTON1, &CDlgNetInfo::OnBnClickedButton1)
        ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CDlgNetInfo::OnBnClickedButtonSearch)
        ON_BN_CLICKED(IDC_BUTTON2, &CDlgNetInfo::OnBnClickedButton2)
        ON_BN_CLICKED(IDC_BUTTON4, &CDlgNetInfo::OnBnClickedButton4)
    END_MESSAGE_MAP()
    
    
    // CDlgNetInfo 诊断
    
    #ifdef _DEBUG
    void CDlgNetInfo::AssertValid() const
    {
        CFormView::AssertValid();
    }
    
    #ifndef _WIN32_WCE
    void CDlgNetInfo::Dump(CDumpContext& dc) const
    {
        CFormView::Dump(dc);
    }
    #endif
    #endif //_DEBUG
    
    
    // CDlgNetInfo 消息处理程序
    
    //int InsertItem(_In_ const LVITEM* pItem);
    
    /* 
    typedef struct tagLVITEMW
    {
        UINT mask;            //显示方式 文字和图片方式显示
        int iItem;            //插入位置,=0 在第0行插入一行
        int iSubItem;        //在第n列 的子节点
        UINT state;            //状态
        UINT stateMask;
        LPWSTR pszText;        //显示的内容 -- 字符型
        int cchTextMax;
        int iImage;            //使用的图片的序号 
        LPARAM lParam;        //可以在每一行上 隐藏一些数据
        int iIndent;
    #if (NTDDI_VERSION >= NTDDI_WINXP)
        int iGroupId;
        UINT cColumns;        // tile view columns
        PUINT puColumns;
    #endif
    #if (NTDDI_VERSION >= NTDDI_VISTA)
        int* piColFmt;
        int iGroup; // readonly. only valid for owner data.
    #endif
    } LVITEMW, *LPLVITEMW;
    */
    
    int CDlgNetInfo::DbInitListSecNode(CString &ID, CString &Name, 
                                       CTime &time, int state, int authcode)
    {
        // TODO:  在此添加控件通知处理程序代
    
        LVITEM   lvi;            //结构体变量
    
        lvi.mask = LVIF_IMAGE | LVIF_TEXT;    //按什么方式显示数据:图片、文本
        lvi.iItem = 0;            //在第0行上插入新数据 头插法
        lvi.iImage = 4;            //使用图片列表中的第4个图片    
    
        //插入第0列数据
        lvi.iSubItem = 0;        // Set subitem 0
        lvi.pszText = (LPTSTR)(LPCTSTR)ID;
        m_listSecNode.InsertItem(&lvi);
    
        //插入第1列数据
        lvi.iSubItem = 1;        // Set subitem 1
        lvi.pszText = (LPTSTR)(LPCTSTR)Name;
        m_listSecNode.SetItem(&lvi);
    
        //插入第2列数据
        CString strTime = time.Format("%Y-%m-%d %H:%M:%S");
        lvi.iSubItem = 2;        // Set subitem 1
        lvi.pszText = (LPTSTR)(LPCTSTR)strTime;
        m_listSecNode.SetItem(&lvi);
    
        //插入第3列数据
        lvi.iSubItem = 3;        // Set subitem 3
        if (state == 1)
        {
            lvi.pszText = "禁用";
        }
        else {
            lvi.pszText = "正常";
        }
        m_listSecNode.SetItem(&lvi);
    
        //插入第4列数据
        lvi.iSubItem = 4;        // Set subitem 4
        //CString strAuthcode(authcode) ;
        char buf[100];
        sprintf(buf, "%d", authcode);
        lvi.pszText = buf;
        m_listSecNode.SetItem(&lvi);
    
        return 0;
    }
    
    // “创建”一条记录
    void CDlgNetInfo::OnBnClickedButton1()
    {
        CString ID = "0001";
        CString Name = "北京建设总行网点";
        CTime time = CTime::GetCurrentTime();
        int state = 0;
        int authcode = 1;
    
        DbInitListSecNode(ID, Name, time, state, authcode);
    }
    
    
    void CDlgNetInfo::OnInitialUpdate()
    {
        CFormView::OnInitialUpdate();
    
        // 按 16 x 16 排列,一共有8个图标,组成一个图片列表。
        HIMAGELIST hList = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 8, 1);
        m_imageList.Attach(hList);
    
        CBitmap cBmp;
        cBmp.LoadBitmap(IDB_BITMAP_SECNODE);
        m_imageList.Add(&cBmp, RGB(255, 0, 255));
        cBmp.DeleteObject();
    
        m_listSecNode.SetImageList(&m_imageList, LVSIL_SMALL);
    
        //获取控件的显示状态
        DWORD dwExStyle = ListView_GetExtendedListViewStyle(m_listSecNode.m_hWnd);
        dwExStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;    //修改 状态
        ListView_SetExtendedListViewStyle(m_listSecNode.m_hWnd, dwExStyle); //设置
        
        CRect rect; //msdn
        m_listSecNode.GetClientRect(&rect);
        int nColInterval = rect.Width() / 5;
    
        m_listSecNode.SetRedraw(FALSE);
    
        m_listSecNode.InsertColumn(0, "网点编号", LVCFMT_LEFT, nColInterval);
        m_listSecNode.InsertColumn(1, "网点名称", LVCFMT_LEFT, nColInterval);
        m_listSecNode.InsertColumn(2, "网点创建时间", LVCFMT_LEFT, nColInterval);
        m_listSecNode.InsertColumn(3, "网点状态", LVCFMT_LEFT, nColInterval);
        m_listSecNode.InsertColumn(4, "网点授权码", LVCFMT_LEFT, rect.Width() - 4 * nColInterval);
    
        m_listSecNode.SetRedraw(TRUE);
    }
    
    extern CDatabase    *g_pDB;
    
    void CDlgNetInfo::OnBnClickedButtonSearch()
    {
        // TODO:  在此添加控件通知处理程序代码
        int            dbtag = 0;
        CWnd        *myWnd = NULL;
        CButton        *But = NULL;
        int            rv = 0, tag = 0;    //表示没有检索到记录
        int            dbTag = 0;        //数据库操作是否失败0成功
        CString        strFilter;
    
        //根据控件资源,获取类对象。适用于小而简单的控件。 
        myWnd = (CWnd *)GetDlgItem(IDC_CHECK_TIME);
        But = (CButton *)myWnd;
    
        UpdateData(TRUE); // 把界面的值 传递给C++变量
    
        if (But->GetCheck() == BST_CHECKED)
        {
            CSECMNGSECNODE    rsetMngSecNode(g_pDB);
            TRY 
            {
                //select * from secmng.secnode;
                if (!rsetMngSecNode.Open(CRecordset::snapshot, NULL, CRecordset::none))
                {
                    MessageBox("打开CSECMNGSECNODE表失败!", "数据库操作", MB_MODEMASK);
                    return;
                }
                CTime sqlTime1(m_dateBegin.GetYear(), m_dateBegin.GetMonth(), m_dateBegin.GetDay(), 0, 0, 0);
                CTime sqlTime2(m_dateEnd.GetYear(), m_dateEnd.GetMonth(), m_dateEnd.GetDay(), 23, 59, 59);
    
                if (sqlTime1 >= sqlTime2)
                {
                    MessageBox("开始时间不能大于结束时间!", "时间查询", MB_MODEMASK);
                    return;
                }
                m_listSecNode.DeleteAllItems();
    
                while (!rsetMngSecNode.IsEOF())
                {            
                    if (rsetMngSecNode.m_CREATETIME < sqlTime1 || 
                        rsetMngSecNode.m_CREATETIME > sqlTime2)
                    {
                        rsetMngSecNode.MoveNext();
                        continue;
                    }  
                    rsetMngSecNode.m_ID.TrimLeft(); rsetMngSecNode.m_ID.TrimRight();
                    rsetMngSecNode.m_NAME.TrimLeft(); rsetMngSecNode.m_NAME.TrimRight();
                    rsetMngSecNode.m_NODEDESC.TrimLeft(); rsetMngSecNode.m_NODEDESC.TrimRight();
                    //rsetMngSecNode.m_CREATETIME;
                    //rsetMngSecNode.m_AUTHCODE;  //编译器自动生成 可以修改 
                    //rsetMngSecNode.m_STATE;
    
                    //逐行向界面挂数据
                    DbInitListSecNode(rsetMngSecNode.m_ID, rsetMngSecNode.m_NAME, rsetMngSecNode.m_CREATETIME,
                        rsetMngSecNode.m_STATE, rsetMngSecNode.m_AUTHCODE);
    
                    rsetMngSecNode.MoveNext();
                }
            }
            CATCH_ALL (e)
            {
                e->ReportError();
                tag = 1;//有异常 状态    
            }
            END_CATCH_ALL
        
            if (rsetMngSecNode.IsOpen())
            {
                rsetMngSecNode.Close();
            }
        }
    
        if (tag == 1)
        {
            AfxMessageBox("检索数据表失败");
        }
    }
    
    void CDlgNetInfo::OnBnClickedButton2()
    {
        // TODO:  在此添加控件通知处理程序代码
        int dbTag = 0;
        CString strTmp, strID, strFilter;
    
        POSITION pos = m_listSecNode.GetFirstSelectedItemPosition();    //获取被选中行的位置。
        int nItem = m_listSecNode.GetNextSelectedItem(pos);  //得到选中行的行号,从0开始。
    
        strID = m_listSecNode.GetItemText(nItem, 0);
        CString strname = m_listSecNode.GetItemText(nItem, 1);
    
        //AfxMessageBox(strid + strname);
    
        strFilter.Format("id = '%s' ", strID);
    
        strTmp.Format("是否要删除编号为【%s】的网点信息吗?", strID);
        if (AfxMessageBox(strTmp, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDNO)
        {
            return;
        }
    
        g_pDB->BeginTrans();
    
        CSECMNGSECNODE    rsetMngSecNode(g_pDB);
        TRY
        {
            rsetMngSecNode.m_strFilter = strFilter;
            if (!rsetMngSecNode.Open(CRecordset::snapshot, NULL, CRecordset::none))
            {
                g_pDB->Rollback();
                MessageBox("打开网点信息表失败!", "数据库操作", MB_MODEMASK);
                return;
            }
            //删除记录
            if (!rsetMngSecNode.IsEOF())
            {
                rsetMngSecNode.Delete();    //Edit() AddNew()
            }
            else
            {
                MessageBox("没有检索到符合条件的记录!", "数据库操作", MB_MODEMASK);
                dbTag = 1;
            }
            rsetMngSecNode.Close();
        }
        CATCH_ALL(e)
        {
            dbTag = 1;
            e->ReportError();
            if (rsetMngSecNode.IsOpen())
            {
                rsetMngSecNode.Close();
            }
        }
        END_CATCH_ALL
    
        if (dbTag == 1)
        {
            g_pDB->Rollback();
            MessageBox("检索数据库失败!", "数据库操作", MB_MODEMASK);
            return;
        }
        else
        {
            g_pDB->CommitTrans();
        }
    
        m_listSecNode.DeleteItem(nItem);
    }
    
    void CDlgNetInfo::OnBnClickedButton4()
    {
        // TODO: 在此添加控件通知处理程序代码
    
        // 1. 获取 pos  POSITION pos = m_listSecNode.GetFirstSelectedItemPosition();
        // 2. 获取 item int nItem = m_listSecNode.GetNextSelectedItem(pos);    
        // 3. 获取文本 strID = m_listSecNode.GetItemText(nItem, 0);
        /*
                name = m_listSecNode.GetItemText(nItem, 1);
                time = m_listSecNode.GetItemText(nItem, 2);
                node = m_listSecNode.GetItemText(nItem, 3);
                state = m_listSecNode.GetItemText(nItem,4);
        */
        // 4. 在Dialog中展示  iD/name/time/node/state
        // 5. 用户重新编辑 iD/name/time/node/state 保存。
    
        // 6. 给保存按钮添加回调:{
                // 1. updatedate(T)   ---》 dialog 的成员变量中
                // 2. 创建记录类对象    format  open  isEoF()     Edit()-update();  更新数据库
                // 3. 写入listcontrl界面
            
        // 7. 更新完成。
    }
    DlgNetInfo.cpp

    >SECMNGSECNODE.h

    // SECMNGSECNODE.h : CSECMNGSECNODE 的声明
    
    #pragma once
    
    
    class CSECMNGSECNODE : public CRecordset
    {
    
    public:
        CSECMNGSECNODE(CDatabase* pDatabase = NULL);
        DECLARE_DYNAMIC(CSECMNGSECNODE)
    
    // 字段/参数数据
    
    // 以下字符串类型(如果存在)反映数据库字段(ANSI 数据类型的 CStringA 和 Unicode
    // 数据类型的 CStringW)的实际数据类型。
    //  这是为防止 ODBC 驱动程序执行可能
    // 不必要的转换。如果希望,可以将这些成员更改为
    // CString 类型,ODBC 驱动程序将执行所有必要的转换。
    // (注意: 必须使用 3.5 版或更高版本的 ODBC 驱动程序
    // 以同时支持 Unicode 和这些转换)。
    
        CString    m_ID;
        CString    m_NAME;
        CString    m_NODEDESC;
        CTime    m_CREATETIME;
        int    m_AUTHCODE;  //编译器给我自动生成的 我们可以修改 微调
        int    m_STATE;
    
    // 重写
        // 向导生成的虚函数重写
        public:
        virtual CString GetDefaultConnect();    // 默认连接字符串
    
        virtual CString GetDefaultSQL();     // 记录集的默认 SQL
        virtual void DoFieldExchange(CFieldExchange* pFX);    // RFX 支持
    
    // 实现
    #ifdef _DEBUG
        virtual void AssertValid() const;
        virtual void Dump(CDumpContext& dc) const;
    #endif
    
    };
    SECMNGSECNODE.h

    >SECMNGSECNODE.cpp

    // SECMNGSECNODE.h : CSECMNGSECNODE 类的实现
    
    
    // CSECMNGSECNODE 实现
    
    
    #include "stdafx.h"
    #include "SECMNGSECNODE.h"
    #include "SECMNGSECNODE.h"
    
    IMPLEMENT_DYNAMIC(CSECMNGSECNODE, CRecordset)
    
    CSECMNGSECNODE::CSECMNGSECNODE(CDatabase* pdb)
        : CRecordset(pdb)
    {
        m_ID = "";
        m_NAME = "";
        m_NODEDESC = "";
        m_CREATETIME;
        m_AUTHCODE = 0;
        m_STATE = 0;
        m_nFields = 6;
        m_nDefaultType = dynaset;
    }
    //#error 安全问题:连接字符串可能包含密码。
    // 此连接字符串中可能包含明文密码和/或其他重要
    // 信息。请在查看完此连接字符串并找到所有与安全
    // 有关的问题后移除 #error。可能需要将此密码存
    // 储为其他格式或使用其他的用户身份验证。
    CString CSECMNGSECNODE::GetDefaultConnect()
    {
        return _T("DSN=SECMNGADMIN;UID=SECMNGADMIN;PWD=123456;DBQ=SECMNGADMIN;DBA=W;APA=T;EXC=F;FEN=T;QTO=T;FRC=10;FDL=10;LOB=T;RST=T;GDE=F;FRL=F;BAM=IfAllSuccessful;NUM=NLS;DPM=F;MTS=T;MDI=F;CSR=F;FWC=F;FBS=64000;TLO=0;");
    }
    
    CString CSECMNGSECNODE::GetDefaultSQL()
    {
        return _T("[SECMNG].[SECNODE]");
    }
    
    void CSECMNGSECNODE::DoFieldExchange(CFieldExchange* pFX)
    {
        pFX->SetFieldType(CFieldExchange::outputColumn);
    // RFX_Text() 和 RFX_Int() 这类宏依赖的是
    // 成员变量的类型,而不是数据库字段的类型。
    // ODBC 尝试自动将列值转换为所请求的类型
        RFX_Text(pFX, _T("[ID]"), m_ID);
        RFX_Text(pFX, _T("[NAME]"), m_NAME);
        RFX_Text(pFX, _T("[NODEDESC]"), m_NODEDESC);
        RFX_Date(pFX, _T("[CREATETIME]"), m_CREATETIME);
        RFX_Int(pFX, _T("[AUTHCODE]"), m_AUTHCODE);
        RFX_Int(pFX, _T("[STATE]"), m_STATE);
    
    }
    /////////////////////////////////////////////////////////////////////////////
    // CSECMNGSECNODE 诊断
    
    #ifdef _DEBUG
    void CSECMNGSECNODE::AssertValid() const
    {
        CRecordset::AssertValid();
    }
    
    void CSECMNGSECNODE::Dump(CDumpContext& dc) const
    {
        CRecordset::Dump(dc);
    }
    #endif //_DEBUG
    SECMNGSECNODE.cpp

    在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    页面加载完毕相关信息淡入效果
    导航菜单底部滑动条跟随效果
    我要成为优秀的前端一员!
    (转)git合并多个commit
    Windows 7 + PHP 5.3 + WAMP 下 Imagick 扩展安装
    使用 PHP 框架 Yii 访问 MS SQL 的尝试
    拼合逐月数据系列
    编程视频教程推荐
    Java 实现 Domino邮箱自动注册
    二、 编写一个类,用两个栈实现队列,支持队列的基本操作(add,poll,peek)
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project10.html
Copyright © 2020-2023  润新知