安全传输平台项目——配置管理终端-框架搭建-初始化
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-安全传输平台项目-第09天(配置管理终端-框架搭建-初始化)
目录:
一、复习
1、复习数据库写入
2、复习框架导入
》创建MFC项目
二、安全传输平台项目——配置管理终端-框架搭建-初始化
1、重画背景onDraw
2、界面切换-CCfgView
3、界面切换-网点信息管理界面NetInfo
4、多态知识回顾
5、多态分析和开源源码走读
6、小结
7、管理终端功能简析
8、初始化-获取配置文件路径
9、创建对话框
10、弹窗供用户输入信息
11、将用户录入数据保存ini文件
12、全局变量赋值及取消按钮
13、测试连接数据库
14、总结
一、复习
1、复习数据库写入
2、复习框架导入
》创建MFC项目:
主函数(类似于main)在SecMngAdmin0325.cpp中。
界面部分在MainFrm.cpp中,需要在其中添加启动的样式:
1)切换到“类视图”,在“CMainFrame”右键选择“类向导”,选择“类向导”,点击“虚函数”,点击“OnCreateClient”,点击“应用”,点击“确定”。
然后在其中添加代码:
>CMainFrame::OnCreateClient重写代码如下:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // TODO: 在此添加专用代码和/或调用基类 if (!wndSplitter.CreateStatic(this, 1, 2)) return false; if (!wndSplitter.CreateView(0, 1,RUNTIME_CLASS(CFormBackground), CSize(0,0), pContext)) return false; //pContext->m_pNewViewClass RUNTIME_CLASS(CFormUserManager) CFormUserManager DWORD dwf = CGfxOutBarCtrl::fDragItems| CGfxOutBarCtrl::fEditGroups| CGfxOutBarCtrl::fEditItems| CGfxOutBarCtrl::fRemoveGroups| CGfxOutBarCtrl::fRemoveItems| CGfxOutBarCtrl::fAddGroups| CGfxOutBarCtrl::fAnimation; wndBar.Create(WS_CHILD|WS_VISIBLE, CRect(0,0,0,0), &wndSplitter, wndSplitter.IdFromRowCol(0, 0), dwf); wndBar.SetOwner(this); imaLarge.Create(32, 32, true, 2, 1); HICON hIcon; for (int i=0; i<7; ++i) { hIcon= AfxGetApp()->LoadIcon (nImages[i]); imaLarge.Add (hIcon); } wndBar.SetImageList(&imaLarge, CGfxOutBarCtrl::fLargeIcon); wndBar.SetImageList(&imaSmall, CGfxOutBarCtrl::fSmallIcon); wndTree.Create(WS_CHILD|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS, CRect(0,0,0,0), &wndBar, 1010); wndTree.SetImageList(&imaSmall, TVSIL_NORMAL); wndBar.SetAnimationTickCount(20); wndBar.SetAnimSelHighlight(200); wndBar.AddFolder("SecMngServer配置管理", 0); wndBar.AddFolder("SecMngAdmin网点管理", 1); wndBar.AddFolder("人员管理", 2); wndBar.AddFolder("设备管理", 3); //wndBar.AddFolder("CA人员操作查询", 4); wndBar.InsertItem(0, 0, "SecMngServer配置", 0, 0); wndBar.InsertItem(0, 1, "网点信息管理", 1, 0); wndBar.InsertItem(0, 2, "后台业务管理", 4, 0); // wndBar.InsertItem(0, 3, "后台交易查询", 5, 0); // wndBar.InsertItem(1, 0, "前台体系管理", 6, 0); wndBar.InsertItem(1, 1, "前台业务管理", 4, 0); wndBar.InsertItem(1, 2, "前台交易查询", 3, 0); wndBar.InsertItem(2, 0, "创建人员", 3, 0); wndBar.InsertItem(2, 1, "冻结人员", 4, 0); wndBar.InsertItem(2, 2, "解冻", 2, 0); wndBar.InsertItem(2, 3, "注销人员", 1, 0); wndBar.InsertItem(3, 0, "创建设备", 5, 0); wndBar.InsertItem(3, 1, "注销管理", 4, 0); wndBar.SetSelFolder(0); CRect r; GetClientRect(&r); int w1 = r.Width()/5; int w2 = r.Width()/4; wndSplitter.SetColumnInfo( 0, w1, 0 ); wndSplitter.SetColumnInfo( 1, w2, 0 ); wndSplitter.RecalcLayout(); return true; //return CFrameWndEx::OnCreateClient(lpcs, pContext); }
2)在MainFrm.h中,包含相应头文件,添加对象变量定义
添加
#include "GfxSplitterWnd.h" #include "GfxOutBarCtrl.h"
然后在class CMainFrame: public CFrameWnd中添加声明:
// 特性 public: CGfxSplitterWnd wndSplitter; CGfxOutBarCtrl wndBar; CImageList imaLarge, imaSmall; CTreeCtrl wndTree;
3)这时编译运行,只剩两个错误:
先解决第二个错误:
在 CMainFrame::OnCreateClient函数之前添加定义nImages:
static UINT nImages[] = { IDI_ICON_SET, IDI_ICON_USER, IDI_ICON_OP_LOG, IDI_ICON_LOG, IDI_ICON_REQ, IDI_ICON_RE, IDI_ICON_REPORT, };
然后添加7张图片:
ico目录下有(log.ico、op_log.ico、re.ico、report.ico、req.ico、set.ico、user.ico)7个ico图片放入项目的文件夹的下一级res目录下;然后打开“资源视图”,在“secmngAdmin411.rc”右键“添加资源”,点击“导入”,选择这7个ico文件,点击“打开”(这时多了一个Icon目录,里面有这7个文件),右键“属性”进行设置ID:
其ID 更改:IDC_ICON7(人图标)更改为:IDI_ICON_USER;
IDC_ICON6更改为:IDI_ICON_SET;
IDC_ICON5更改为:IDI_ICON_REQ;
IDC_ICON4更改为:IDI_ICON_REPORT;
IDC_ICON3更改为:IDI_ICON_RE;
IDC_ICON2更改为:IDI_ICON_OP_LOG;
IDC_ICON1更改为:IDI_ICON_LOG;
注意,每步更改后,点击“都保存”,会弹出警告-修改影响多处,点击“是”。
4)这时编译运行,只剩1个错误:
打开“资源视图”,添加“Dialog”:
在资源视图左侧“Dialog”处右键“插入Dialog”,然后删除“确定”和“取消”按钮,修改对话框的ID(在“IDD_DIALOG1”右键“属性”进行修改ID为:IDD_FORMBACKGROUND_FORM);
点击对话框,修改属性:修改Border属性“外框对话框”配置成None;Style属性“Popup”配置成child
点击对话框,右键“添加类”(或者“双击”),然后输入类名:CFormBackground,基类选择: CFormView
然后,包含类对象:在MainFrm.h头文件中添加 #include "FormBackground.h"
最后点击“运行”,会弹出:
二、安全传输平台项目——配置管理终端-框架搭建-初始化
1、重画背景onDraw
1)切换到“类视图”,选择“CFormBackground”,右键点击“类向导”,选择“虚函数”:
然后在其中添加代码:
>CFormBackground::OnDraw重写代码如下:(注意:函数注释掉的参数pDC打开!)
void CFormBackground::OnDraw(CDC* pDC) { // TODO: 在此添加专用代码和/或调用基类 CRect clientRect; GetClientRect(&clientRect); for(int k = 13 ;k< 18;k++) //绘图示例—显示位图 { CBrush* pTempBrush = NULL; CBrush OrigBrush; OrigBrush.FromHandle((HBRUSH)pTempBrush); CRect rc; GetClientRect(&rc); //ScreenToClient(&rc); try { CBitmap bmp; bmp.LoadBitmap(IDB_BITMAP_BACK); CBrush brush(&bmp); pTempBrush = (CBrush*)pDC->SelectObject(brush); pDC->Rectangle(rc.left,rc.top, rc.Width(), rc.Height()); pDC->SelectObject(&OrigBrush); break; } catch(CResourceException* e) { e->ReportError(); e->Delete(); } } CRect rect; GetClientRect(&rect); CRect rect2; GetClientRect(&rect2); CFont font ; font.CreatePointFont(340, _T("Arial")); CString string = "欢迎使用KeyMngServer管理终端"; pDC->SelectObject(&font); pDC->SetBkMode(TRANSPARENT); rect.OffsetRect(6, -90); //背影 pDC->SetTextColor( RGB(192, 192, 192)); pDC->DrawText(string, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); rect2.OffsetRect(0, -100); pDC->SetTextColor( RGB(0, 0, 0)); pDC->DrawText(string, &rect2, DT_SINGLELINE|DT_CENTER|DT_VCENTER); }
2)bmp目录下有(background.bmp)1个bmp图片放入项目的文件夹的下一级res目录下;然后打开“资源视图”,在“secmngAdmin411.rc”右键“添加资源”,点击“导入”,选择这1个bmp文件,点击“打开”(这时多了一个Bitmap目录,里面有这7个文件),右键“属性”进行设置ID:
其ID 更改:IDB_BITMAP1更改为:IDB_BITMAP_BACK;
最后点击“运行”,会弹出:
2、界面切换-CCfgView
CMainFram类中添加视图切换自定义消息:
1)在MainFrm.h中添加自定义消息声明:
// 生成的消息映射函数 protected: DECLARE_MESSAGE_MAP() afx_msg long OnOutbarNotify(WPARAM wParam, LPARAM lParam);//声明
2)在MainFrm.cpp中添加自定义消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
… …
ON_MESSAGE(WM_OUTBAR_NOTIFY, OnOutbarNotify) // 注意不能添加分号!!!
……
END_MESSAGE_MAP()
3)在MainFrm.cpp中添加消息映射函数的实现
long CMainFrame::OnOutbarNotify(WPARAM wParam, LPARAM lParam) { switch (wParam) { case NM_OB_ITEMCLICK: // cast the lParam to an integer to get the clicked item { int index = (int) lParam; CString cText, cs1; cText = wndBar.GetItemText(index); CCreateContext Context; if (cText==_T("SecMngServer配置")) { Context.m_pNewViewClass = RUNTIME_CLASS(CCfgView); Context.m_pCurrentFrame = this; Context.m_pLastView = (CView *)wndSplitter.GetPane(0,1); wndSplitter.DeleteView(0,1); wndSplitter.CreateView(0,1,RUNTIME_CLASS(CCfgView),CSize(500,0),&Context); CCfgView *pNewView=(CCfgView *)wndSplitter.GetPane(0,1); wndSplitter.RecalcLayout(); pNewView->OnInitialUpdate(); wndSplitter.SetActivePane(0,1); //IsHisKeyRecovry=0; } else if (cText==_T("网点信息管理")) { Context.m_pNewViewClass = RUNTIME_CLASS(CDlgNetInfo); Context.m_pCurrentFrame = this; Context.m_pLastView = (CView *)wndSplitter.GetPane(0,1); wndSplitter.DeleteView(0,1); wndSplitter.CreateView(0,1,RUNTIME_CLASS(CDlgNetInfo),CSize(500,0),&Context); CDlgNetInfo *pNewView=(CDlgNetInfo *)wndSplitter.GetPane(0,1); wndSplitter.RecalcLayout(); pNewView->OnInitialUpdate(); wndSplitter.SetActivePane(0,1); } else if (cText==_T("后台业务管理")) { //AfxMessageBox(cText); Context.m_pNewViewClass = RUNTIME_CLASS(CSysAll); Context.m_pCurrentFrame = this; Context.m_pLastView = (CView *)wndSplitter.GetPane(0,1); wndSplitter.DeleteView(0,1); wndSplitter.CreateView(0,1,RUNTIME_CLASS(CSysAll),CSize(500,0),&Context); CSysAll *pNewView=(CSysAll *)wndSplitter.GetPane(0,1); wndSplitter.RecalcLayout(); pNewView->OnInitialUpdate(); wndSplitter.SetActivePane(0,1); } } return 0; case NM_OB_ONLABELENDEDIT: // cast the lParam to an OUTBAR_INFO * struct; it will contain info about the edited item // return 1 to do the change and 0 to cancel it { /* if(IsLoginOutFlag == 1) { AfxMessageBox("人员已登出,请重新登录CA人员以进行相应操作。"); return 1; } */ OUTBAR_INFO * pOI = (OUTBAR_INFO *) lParam; TRACE2("Editing item %d, new text:%s\n", pOI->index, pOI->cText); } return 1; case NM_OB_ONGROUPENDEDIT: // cast the lParam to an OUTBAR_INFO * struct; it will contain info about the edited folder // return 1 to do the change and 0 to cancel it { /* if(IsLoginOutFlag == 1) { AfxMessageBox("人员已登出,请重新登录人员以进行相应操作。"); return 1; } */ OUTBAR_INFO * pOI = (OUTBAR_INFO *) lParam; TRACE2("Editing folder %d, new text:%s\n", pOI->index, pOI->cText); } return 1; case NM_OB_DRAGITEM: // cast the lParam to an OUTBAR_INFO * struct; it will contain info about the dragged items // return 1 to do the change and 0 to cancel it { /* if(IsLoginOutFlag == 1) { AfxMessageBox("人员已登出,请重新登录人员以进行相应操作。"); return 1; }*/ OUTBAR_INFO * pOI = (OUTBAR_INFO *) lParam; TRACE2("Drag item %d at position %d\n", pOI->iDragFrom, pOI->iDragTo); } return 1; } return 0; }
4)打开“资源视图”,添加“Dialog”:
(1)在资源视图左侧“Dialog”处右键“插入Dialog”,然后删除“确定”和“取消”按钮,修改对话框的ID(在“IDD_DIALOG2”右键“属性”进行修改ID为:IDD_DIALOG_CFG);
(2)点击对话框,修改属性:修改Border属性“外框对话框”配置成None;Style属性“Popup”配置成child;
(3)点击对话框,右键“添加类”,然后输入类名:CCfgView,基类选择: CFormView
(4)然后,包含类对象:在MainFrm.h头文件中添加 #include "CfgView.h"
(5)在IDD_DIALOG_CFG的Dialog中添加:
3、界面切换-网点信息管理界面NetInfo
(1)在资源视图左侧“Dialog”处右键“插入Dialog”,然后删除“确定”和“取消”按钮,修改对话框的ID(在“IDD_DIALOG3”右键“属性”进行修改ID为:IDD_DIALOG_NETMNG);
(2)点击对话框,修改属性:修改Border属性“外框对话框”配置成None;Style属性“Popup”配置成child;
(3)点击对话框,右键“添加类”,然后输入类名:CDlgNetInfo,基类选择: CFormView
(4)然后,包含类对象:在MainFrom头文件中添加 #include "DlgNetInfo.h"
(5)在IDD_DIALOG_CFG的Dialog中添加:
同理,
为COpTranView类添加Dialog资源(id可以命名:IDD_DIALOG_TRAN)
为CSysAll类添加Dialog资源(id可以命名:IDD_DIALOG_SYSALL)
4、多态知识回顾
面向对象:封装(功能相近或相似的代码集成)、继承(子类可以使用父类的变量和函数)、多态(主要发生在函数调用区间,同样的函数传递不同的参数,调用的结果不同)
多态机制:
1)父类指针指向子类对象;2)继承;3)虚函数重写
》测试:
class parent { public: virtual void print() { printf("I am parent.\n"); } } class child:public parent { public: virtual void print() { printf("I am child.\n"); } } void howToPrint(parent *p) { p->print(); } int main() { parent p1; child c1; howToPrint(&p1); howToPrint(&c1);//多态 }
多态实现的原理:1)每个类对象有一个vptr指针,指向虚函数表首地址;2)虚函数表
5、多态分析和开源源码走读
为什么重写了OnCreateClient函数,可以实现左侧的样式?
》项目中的多态分析:
6、小结:
》对接开源框架相关:
1)创建MFC项目;
2)导入开源文件 9个(.cpp .h);
3)CString --> const char * 在项目上右键--> 属性 --> 字符集:使用unicode字符集 --> 使用多字节字符集;
VS2013:如果,上述操作不生效。使用管理员身份运行:vc_mbcsmfc.exe
4)导入.cur图片资源 --> MFC项目资源目录res中;
5)“资源视图”标签,任意一个目录上 右键“添加资源”-->“导入”选择res中的.cur 文件(3个)添加到项目中。
6)根据错误提示,修改ID;
7)添加消息宏定义 resource.h -->正常编译
#define ID_GFX_SMALLICON 50000 #define ID_GFX_LARGEICON 50001 #define ID_GFX_RENAMEITEM 50002 #define ID_GFX_REMOVEITEM 50003 #define ID_GFX_GROUPICON 50004 #define ID_GFX_FONTCICON 50005 #define ID_GFX_BACKCICON 50006
8)给CMainFrame类 重写父类的 onCreateClient() 虚函数;
9)添加代码实现 QQ 抽屉效果:(1)视图的切分;(2)视图的切换;
10)添加类对应头文件 --MainFrm.h
#include "GfxSplitterWnd.h" #include "GfxOutBarCtrl.h"
11)添加类变量 --MainFrm.h
public: CGfxSplitterWnd wndSplitter; CGfxOutBarCtrl wndBar; CImageList imaLarge, imaSmall; CTreeCtrl wndTree;
12)添加控件Dialog
在“资源视图”的“Dialog”目录上右击,添加一个新的Dialog
修改ID、修改Dialog属性 Border --> None、修改 Style属性 Popup -->child
13)给Dialog添加类CFormBackground
继承自CFormView 类
#include "FormBackground.h" 添加到 MainFrm.cpp或MainFrm.h
14)onCreateClient() 函数实现之前 nImages 数组
static UINT nImages[] = { IDI_ICON_SET, IDI_ICON_USER, IDI_ICON_OP_LOG, IDI_ICON_LOG, IDI_ICON_REQ, IDI_ICON_RE, IDI_ICON_REPORT, };
15)导入.ico图片到项目res目录中
一次修改ID(7个)
16)添加背景图片
给CFormBackground 重写onDraw函数 -->用到bmp 图片资源
在函数内部添加代码。
导入 background.bmp图片
修改ID
17)完成视图切换功能
(1)添加消息声明 ---> MainFrm.h
afx_msg long OnOutbarNotify(WPARAM wParam, LPARAM lParam);
(2)注册消息映射 -->MainFrm.cpp
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) … … ON_MESSAGE(WM_OUTBAR_NOTIFY, OnOutbarNotify) // 注意不能添加分号!!! …… END_MESSAGE_MAP()
(3)对应消息声明添加实现。-->MainFrm.cpp
在函数内部添加代码。
18)添加控件Dialog
(1)在“资源视图”的“Dialog”目录上右击,添加一个新的Dialog
修改ID、修改Dialog属性 Border --> None、修改 Style属性 Popup -->child
给Dialog添加类CCfgView
#include "CfgView.h" 添加到MainFrm.cpp 或 MainFrm.h
【可选】在Dialog中添加控件
(2)在“资源视图”的“Dialog”目录上右击,添加一个新的Dialog
修改ID、修改Dialog属性 Border --> None、修改 Style属性 Popup -->child
给Dialog添加类CDlgNetInfo
#include "DlgNetInfo.h" 添加到MainFrm.cpp 或 MainFrm.h
【可选】在Dialog中添加控件
7、管理终端功能简析
》“管理终端”的作用:
新增一个网点时,服务器应通过管理终端给其分配编号、授权码(类似邮箱),并录入该网点信息到数据库。
管理终端的管理员,应能通过管理终端配置服务器的属性,如:服务器IP、端口、最大网点个数等。
》功能模块:
>系统初始化
录入配置信息,写配置文件。下次启动时直接读该配置文件。 xxx.ini
>参数配置管理
数据源参数配置
服务器的启动参数
根网点的配置信息
>网点生命周期的管理
网点的增、删、改、查。模糊查询、精确查询。
8、初始化-获取配置文件路径
》系统初始化
第一次启动时,读配置文件
若文件不存在,弹出对话框,让用户输入,保存到xxx.ini配置文件,同时赋值给全局变量
若文件存在,赋值给全局变量,用于后续连接数据库。
--实现读配置文件:
1)在 SecMngAdmin.cpp的 InitInstance()中添加读配置文件的功能函数 readSecMngCfg:
int ret = readSecMngCfg();
2)在 SecMngAdmin.cpp中添加 readSecMngCfg 函数定义:
>int CSecMngAdminAPP::readSecMngCfg()代码:
int CSecMngAdminAPP::readSecMngCfg() { //找到.exe文件所在目录位置 //C:\Users\afei\Documents\Visual Studio 2015\Projects\SecMngAdmin\Debug\MySecAdmin.exe //C:\Users\afei\Documents\Visual Studio 2015\Projects\SecMngAdmin\Debug\secmngadmin.ini char fileName[1024] = { 0 }; //获取当前工作模块文件路径 GetModuleFileName(AfxGetInstanceHandle(), fileName, sizeof(fileName)); //AfxMessageBox(fileName); //拼接文件路径 CString g_strINIPath = fileName;//定义 CString 类变量,使用 CString API int i = g_strINIPath.ReverseFind('\\');//自右向左查找‘\’ g_strINIPath = g_strINIPath.Left(i);//根据找到的位置提取 //AfxMessageBox(fileName); g_strINIPath = g_strINIPath + "\\" + "secmngadmin.ini" //AfxMessageBox(fileName); //获取配置项 GetCfgItem在cfg_op.h中 //ret = GetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen); return 0; }
3)在 SecMngAdmin.h 中添加声明:
public: int readSecMngCfg();
4)实现 readSecMngCfg 函数:
(1)配置文件应该存放在 exe 文件所在目录下,应先,求当前应用程序所在路径。
(2)封装自定义函数(cfg_op.c和cfg_op.h中),读配置文件。添加对应头文件。
将 CString 类转化成 char * 用到宏 LPCTSTR 和 LPTSTR
》补充:
(1)获取当前工作模块文件路径函数
/* DWORD GetModuleFileName( HMODULE hModule,//获得 hModule 所指的文件的名字,常配合 AfxGetInstanceHandle() 使用 LPTSTR lpFilename,//存放返回的名字的内存块的指针 DWORD nSize //内存块的大小 ); */ char strFileName[2048] = { 0 }; //获取当前工作模块文件路径 GetModuleFileName(AfxGetInstanceHandle(), strFileName, sizeof(strFileName)); //拼接文件路径 CString g_strINIPath = strFileName;//定义 CString 类变量,使用 CString API int i = g_strINIPath.ReverseFind('\\');//自右向左查找‘\’ g_strINIPath = g_strINIPath.Left(i);//根据找到的位置提取 g_strINIPath = g_strINIPath + "\\" + "待补充的字符串"
(2)获取配置项和写配置项 的函数
>cfg_op.h
// cfg_op.h #ifndef __CFG_OP_H__ #define __CFG_OP_H__ #ifdef __cplusplus extern "C" { #endif //获取配置项 int GetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen); //写配置项 int WriteCfgItem(char *pFileName, char *pItemName, char *pItemValue, int itemValueLen); #ifdef __cplusplus } #endif #endif
>cfg_op.c
#include "stdafx.h" //别忘记 增加这个头文件的说明 #define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <string.h> #include <stdio.h> #include "cfg_op.h" #define MaxLine 2048 //获取配置项 int GetCfgItem(char *pFileName, char *pKey, char * pValue, int * pValueLen) { int ret = 0; FILE *fp = NULL; char *pTmp = NULL, *pEnd = NULL, *pBegin = NULL; char lineBuf[MaxLine]; fp = fopen(pFileName, "r"); if (fp == NULL) { ret = -1; return ret; } while (!feof(fp)) { memset(lineBuf, 0, sizeof(lineBuf)); //fgets(_Out_z_cap_(_MaxCount) char * _Buf, _In_ int _MaxCount, _Inout_ FILE * _File); fgets(lineBuf, MaxLine, fp); //printf("lineBuf:%s ",lineBuf ); pTmp = strchr(lineBuf, '='); // key = value if (pTmp == NULL) //没有=号 { continue; } pTmp = strstr(lineBuf, pKey); if (pTmp == NULL) //判断key是不是在 //所在行 是不是有key { continue; } pTmp = pTmp + strlen(pKey); //mykey1 = myvalude11111111 ==> "= myvalude1111111" pTmp = strchr(pTmp, '='); if (pTmp == NULL) //判断key是不是在 //所在行 是不是有key { continue; } pTmp = pTmp + 1; //获取value 起点 while (1) { if (*pTmp == ' ') { pTmp++; } else { pBegin = pTmp; if (*pBegin == '\n') { //没有配置value //printf("配置项:%s 没有配置value \n", pKey); goto End; } break; } } //获取valude结束点 while (1) { if ((*pTmp == ' ' || *pTmp == '\n')) { break; } else { pTmp++; } } pEnd = pTmp; //赋值 *pValueLen = pEnd - pBegin; memcpy(pValue, pBegin, pEnd - pBegin); } End: if (fp == NULL) { fclose(fp); } return 0; } //写配置项 //循环读每一行,检查key配置项是否存在 若存在修改对应value值 //若不存在,在文件末尾 添加 "key = value" //难点:如何修改文件流中的值 //int WriteCfgItem(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in*/, int ValueLen /*in*/) int WriteCfgItem(char *pFileName, char *pKey, char * pValue, int ValueLen) { int rv = 0, iTag = 0, length = 0; FILE *fp = NULL; char lineBuf[MaxLine]; char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL; char filebuf[1024 * 8] = { 0 }; if (pFileName == NULL || pKey == NULL || pValue == NULL) { rv = -1; printf("SetCfgItem() err. param err \n"); goto End; } fp = fopen(pFileName, "r+"); if (fp == NULL) { rv = -2; printf("fopen() err. \n"); //goto End; } if (fp == NULL) { fp = fopen(pFileName, "w+t"); if (fp == NULL) { rv = -3; printf("fopen() err. \n"); goto End; } rv = 0; //bug } fseek(fp, 0L, SEEK_END); //把文件指针从0位置开始,移动到文件末尾 //获取文件长度; length = ftell(fp); fseek(fp, 0L, SEEK_SET); if (length > 1024 * 8) { rv = -3; printf("文件超过1024*8, nunsupport"); goto End; } while (!feof(fp)) { //读每一行 memset(lineBuf, 0, sizeof(lineBuf)); pTmp = fgets(lineBuf, MaxLine, fp); if (pTmp == NULL) { break; } //key关键字是否在本行 pTmp = strstr(lineBuf, pKey); if (pTmp == NULL) //key关键字不在本行, copy到filebuf中 { strcat(filebuf, lineBuf); continue; } else //key关键在在本行中,替换旧的行,再copy到filebuf中 { sprintf(lineBuf, "%s = %s\n", pKey, pValue); strcat(filebuf, lineBuf); //若存在key iTag = 1; } } //若key关键字,不存在 追加 if (iTag == 0) { fprintf(fp, "%s = %s\n", pKey, pValue); } else //若key关键字,存在,则重新创建文件 { if (fp != NULL) { fclose(fp); fp = NULL; //避免野指针 } fp = fopen(pFileName, "w+t"); if (fp == NULL) { rv = -4; printf("fopen() err. \n"); goto End; } fputs(filebuf, fp); //fwrite(filebuf, sizeof(char), strlen(filebuf), fp); } End: if (fp != NULL) { fclose(fp); } return rv; }
9、创建对话框
(1)将cfg_op.c和cfg_op.h拷贝到放入项目的文件夹(在SecMngAdmin.cpp上右键打开的文件夹),然后在项目“secmngAdmin”右键,添加“现有项”,添加这2个文件;
(2)在 SecMngAdmin.cpp中修改 readSecMngCfg 函数实现:
>int CSecMngAdminAPP::readSecMngCfg()
int CSecMngAdminAPP::readSecMngCfg() { int ret = 0; char pValue[64] = {0}; int pValueLen = sizeof(pValue); //找到.exe文件所在目录位置 //C:\Users\afei\Documents\Visual Studio 2015\Projects\SecMngAdmin\Debug\MySecAdmin.exe //C:\Users\afei\Documents\Visual Studio 2015\Projects\SecMngAdmin\Debug\secmngadmin.ini char fileName[1024] = { 0 }; //获取当前工作模块文件路径 GetModuleFileName(AfxGetInstanceHandle(), fileName, sizeof(fileName)); //拼接文件路径 CString g_strINIPath = fileName;//定义 CString 类变量,使用 CString API int i = g_strINIPath.ReverseFind('\\');//自右向左查找‘\’ g_strINIPath = g_strINIPath.Left(i);//根据找到的位置提取 g_strINIPath = g_strINIPath + "\\" + "secmngadmin.ini"; // 获取配置项 GetCfgItem在cfg_op.h中 // 将 CString 类转化成 char * 用到宏 LPCTSTR 和 LPTSTR // 读取 DSN ret = GetCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "DSN", pValue, &pValueLen); if(ret != 0) { //AfxMessageBox("读取 DSN 失败"); return ret; } // 读取 UID memset(pValue, 0, sizeof(pValue)); ret = GetCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "UID", pValue, &pValueLen); if(ret != 0) { //AfxMessageBox("读取 UID 失败"); return ret; } // 读取 PWD memset(pValue, 0, sizeof(pValue)); ret = GetCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "PWD", pValue, &pValueLen); if(ret != 0) { //AfxMessageBox("读取 PWD 失败"); return ret; } return 0; }
(3)在 SecMngAdmin.h中增加头文件:
#include "cfg_op.h"
(4)在 SecMngAdmin.cpp的 InitInstance()中增加弹出对话框提供用户输入信息的Dialog:
打开“资源视图”,添加“Dialog”:
在资源视图左侧“Dialog”处右键“插入Dialog”,然后删除“确定”和“取消”按钮,修改对话框的ID(在“IDD_DIALOG6”右键“属性”进行修改ID为:IDD_DIALOG_INITDB);
点击对话框,修改属性:修改Caption属性“:配置数据库信息;
数据源ID: IDC_EDIT_DSN
用户名ID: IDC_EDIT_USER
密码ID: IDC_EDIT_PASSWD
10、弹窗供用户输入信息
(1)点击上步创建的IDD_DIALOG_INITDB对话框,右键“添加类”(或者“双击”),然后输入类名:CDlgInitCfg,基类选择: CDialog,点击“完成”:
(2)在IDD_DIALOG_INITDB对话框右键选择“类向导”,选择“成员变量”,选中“IDC_EDIT_DSN”,然后点击右侧的“添加变量”,依次添加(3个)成员变量:
目的是从 Dialog 中提取数据
将试图资源关联 类 成员变量
CString m_dbDSN
CString m_dbUID
CString m_dbPWD
添加完成,如图:
(3)在IDD_DIALOG_INITDB对话框双击“确定”按钮,添加回调函数:
>代码如下:
void CDlgInitCfg::OnBnClickedOk() { // TODO:在此添加控件通知处理程序代码 //CDialog::OnOK(); UpdateData(TRUE); if(m_dbDSN.IsEmpty() || m_dbPWD.IsEmpty() || m_dbUID.IsEmpty()) { AfxMessageBox("录入信息不允许为空!"); return; } }
(4)在 SecMngAdmin.cpp的 InitInstance()中添加调用(弹出对话框供用户输入)的函数:
>BOOL CSecMngAdminApp::InitInstance()代码如下:
BOOL CSecMngAdminApp::InitInstance() { int ret = 0; CWinApp::InitInstance(); EnableTaskbarInteraction(FALSE); // 使用 RichEdit 控件需要 AfxInitRichEdit2() //AfxInitRichEdit2(); //标准初始化 ret = readSecMngCfg(); if(ret != 0) { AfxMessageBox("读取配置信息出错"); //弹出对话框供用户输入信息 CDlgInitCfg dlgInitCfg; dlgInitCfg.DoModal(); } else { // 借助全局变量 连接数据库 } ... }
在 SecMngAdmin.h中增加头文件:#include "DlgInitCfg.h"
(5)设置 测试、确定、取消 3个按钮的ID:
测试ID:IDC_BUTTON_LINKTEST、确定ID:IDOK、取消ID:IDCANCLE
11、将用户录入数据保存ini文件
(1)在DlgInitCfg.cpp中修改void CDlgInitCfg::OnBnClickedOk()代码:
void CDlgInitCfg::OnBnClickedOk() { // TODO:在此添加控件通知处理程序代码 //CDialog::OnOK(); int ret = -1; UpdateData(TRUE); if(m_dbDSN.IsEmpty() || m_dbPWD.IsEmpty() || m_dbUID.IsEmpty()) { AfxMessageBox("录入信息不允许为空!"); return; } char fileName[1024] = { 0 }; //获取当前工作模块文件路径 GetModuleFileName(AfxGetInstanceHandle(), fileName, sizeof(fileName)); //拼接文件路径 CString g_strINIPath = fileName;//定义 CString 类变量,使用 CString API int i = g_strINIPath.ReverseFind('\\');//自右向左查找‘\’ g_strINIPath = g_strINIPath.Left(i);//根据找到的位置提取 g_strINIPath = g_strINIPath + "\\" + "secmngadmin.ini"; // 获取配置项 GetCfgItem在cfg_op.h中 // 将 CString 类转化成 char * 用到宏 LPCTSTR 和 LPTSTR // 写入 DSN ret = WriteCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "DSN", (LPTSTR)(LPCTSTR)m_dbDSN, m_dbDSN.getLength); if(ret != 0) { //AfxMessageBox("写入 DSN 失败"); return; } // 写入 UID ret = WriteCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "UID", (LPTSTR)(LPCTSTR)m_dbUID, m_dbUID.getLength); if(ret != 0) { //AfxMessageBox("写入 UID 失败"); return; } // 写入 PWD ret = WriteCfgItem((LPTSTR)(LPCTSTR)g_strINIPath, "PWD", (LPTSTR)(LPCTSTR)m_dbPWD, m_dbPWD.getLength); if(ret != 0) { //AfxMessageBox("写入 PWD 失败"); return; } }
(2)测试:
打开工程目录的上一级目录,查看,增加了secmngadmin.ini文件:
12、全局变量赋值及取消按钮
(1)增加全局变量,添加文件“secmng_globvar.h”
// secmng_globvar.h #ifndef SECMNG_GLOB_VAR_H_ #define SECMNG_GLOB_VAR_H_ #include "stdafx.h" //数据源全局变量 CString g_dbSource, g_dbUser, g_dbpwd; //连接数据全局变量 CDatabase myDB; CDatabase *g_pDB = NULL; //配置文件全局变量 CString g_SecMngIniName = "secmngadmin.ini"; #endif
将secmng_globvar.h拷贝到放入项目的文件夹(在SecMngAdmin.cpp上右键打开的文件夹),然后在项目“secmngAdmin”右键,添加“现有项”,添加这1个文件;
然后在SecMngAdmin.cpp中添加头文件:#include "secmng_globvar.h"
secmng_globvar.h中为方便调试,先注释掉:
//连接数据全局变量 //CDatabase myDB; //CDatabase *g_pDB = NULL;
注意:secmng_globvar.h里面是全局变量,所以只能添加到一个文件中!!!
在SecMngAdmin.cpp中增加调用
>BOOL CSecMngAdminApp::InitInstance()代码如下:
BOOL CSecMngAdminApp::InitInstance() { int ret = 0; CWinApp::InitInstance(); EnableTaskbarInteraction(FALSE); // 使用 RichEdit 控件需要 AfxInitRichEdit2() //AfxInitRichEdit2(); //标准初始化 ret = readSecMngCfg(); if(ret != 0) { AfxMessageBox("读取配置信息出错"); //弹出对话框供用户输入信息 CDlgInitCfg dlgInitCfg; dlgInitCfg.DoModal(); } else { // 借助全局变量 连接数据库 AfxMessageBox("DSN:" + g_dbSource + "|UID:" + g_dbUser + "|PWD:" + g_dbpwd ); } ... }
运行结果如下:
注意:void CDlgInitCfg::OnBnClickedOk()注释掉:CDialog::OnOK();后打开主界面后原界面不会退出!
所以,应该修改为:
另外,
(1)不论配置文件是否存在,都可以连接数据库;
(2)取消按钮功能简单,所以不设置回调,借助于判断if(dlgInitCfg.DoModal() == "取消按钮ID");
所以,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() == IDCANCLE) { return FALSE; } g_dbSource = dlgInitCfg.m_dbDSN; g_dbUser = dlgInitCfg.m_dbUID; g_dbpwd = dlgInitCfg.m_dbPWD; } else { // 配置文件,存在,直接读取,然后连接数据库 AfxMessageBox("DSN:" + g_dbSource + "|UID:" + g_dbUser + "|PWD:" + g_dbpwd ); } // 借助全局变量--连接数据库 ... }
13、测试连接数据库
连接数据库(借助ODBC)示意图:
问题来源:当创建MFC项目的时候,数据库支持选择的“无”,但是如果选择“尽头文件”,客户端类型选择“ODBC”,会添加特别多的文件,所以自己手动增加ODBC的头文件。
(1)secmng_globvar.h中把之前注释的代码打开
//连接数据全局变量 CDatabase myDB; CDatabase *g_pDB = NULL;
然后,在stdafx.h中增加头文件:
#include <afxdb.h>
按“F12”跳转到定义,可以查看OpenEx函数
(2)双击“测试”按钮,增加回调函数,
1)使用MFC连接数据库的ODBC的调用函数OpenEx;
2)增加try...catch...
>void CDlgInitCfg::OnBnClickedButtonLinktest()
void CDlgInitCfg::OnBnClickedButtonLinktest() { // TODO: 在此添加控件通知处理程序代码 UpdateData(TRUE);//提取文本框 CDatabase myDB; CString strCon; TRY { strCon.Format("DSN=%s;UID=%s;PWD=%s", m_dbDSN, m_dbUID, m_dbPWD); if( myDB.OpenEx(strCon, CDatabase::noOdbcDialog) == FALSE) { AfxMessageBox("创建一条到数据源的连接发生错误, 请检查数据源配置是否正确!"); return; } else { AfxMessageBox("连接数据库成功!"); } myDB.Close();//这只是测试,所以测试完毕,需要关闭与数据库的连接 } CATCH_ALL(e) { e->ReportError(); } END_CATCH_ALL return; }
》测试:
点击“运行”,先输入错误的数据源、用户名和密码:
点击“测试”,会捕捉到错误:
点击“运行”,输入正确的数据源、用户名和密码:
14、总结
》系统初始化
第一次启动时,读配置文件
若文件不存在,弹出对话框,让用户输入,保存到xxx.ini配置文件,同时赋值给全局变量
若文件存在,赋值给全局变量,用于后续连接数据库。
--实现读配置文件:
在 SecMngAdmin.cpp的 InitInstance()中添加读配置文件的功能函数 ReadSecMngCfg:
int ret = ReadSecMngCfg();
在 CSecMngAdminAPP.cpp中添加 ReadSecMngCfg 函数定义;
在 CSecMngAdminAPP.h 中添加声明:
public:
int ReadSecMngCfg();
实现 ReadSecMngCfg 函数:
(1)配置文件应该存放在 exe 文件所在目录下,应先,求当前应用程序所在路径。
(2)封装自定义函数,读配置文件(cfg_op.h和cfg_op.c)。添加对应头文件。
将 CString 类转化成 char * 用到宏 LPCTSTR 和 LPTSTR
--创建 Dialog,读配置文件失败,弹窗显示
“资源视图”---“Dialog目录”右键,插入 Dialog 控件。
设计弹窗界面,修改 Dialog 的 ID:IDD_DIALOG_INITDB
数据源ID: IDC_EDIT_DSN
用户名ID: IDC_EDIT_USER
密码ID: IDC_EDIT_PASSWD
Ctrl + T 预览界面
给 Dialog 添加类:CDlgInitCfg 包含头文件 DlgInitCfg.h
CDlgInitCfg dlgInitCfg;
读取失败,弹窗显示 if(dlgInitCfg.DoModal() == "取消按钮ID");
--从 Dialog 中提取数据
将试图资源关联 类 成员变量 --Dialog --类向导 --成员变量
CString m_dbDSN
CString m_dbUID
CString m_dbPWD
若用户选择“确定”按钮,UpdateData(TRUE);将界面数据保存到成员变量中。
若变量值为空 IsEmpty(),提示用户:AfxMseesageBox("不能为空");
若用户选择“取消”按钮,dlgInitCfg.DoModel() == IDCANCEL 直接 return FALSE;
否则,获取成员变量,数据保存至 全局变量。
--定义全局变量文件,统一管理 secmng_globvar.h 【注意:该头文件只能被引用一次!!!】
CDatabase 类需要引用 #include <atxdb.h> -->stdafx.h
创建项目时,数据库支持页面中,选择“仅头文件”可以包含该头文件。也可以手动添加。
成功读取配置文件中的数据和弹窗后,用户输入的数据,对应存储到全局变量中。
TRY
{
}
CATCH_ALL(e)
{
e->ReportError();
}
END_CATCH_ALL
--测试按钮:
UpdateData(TRUE),将界面数据保存到成员变量中。
CDataBase db;
CString strCon;
strCon.Format("DSN=%s;UID=%s;PWD=%s", m_dbDSN, m_dbUID, m_dbPWD);
db->OpenEx(strCon, CDatabase::noOdbcDialog);
db.Close();
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
posted on 2020-07-31 11:32 Alliswell_WP 阅读(323) 评论(0) 编辑 收藏 举报