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

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

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project10.html