在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主: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; } ..... }
然后实现 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; }
在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.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
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); }
掌握如何拼接"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(); } }
这时候运行,会报错:(先点击“忽略”,待解决。)
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.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. 更新完成。 }
>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.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
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。