安全传输平台项目——配置管理终端-框架搭建-初始化

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

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);    

}
void CFormBackground::OnDraw(CDC* pDC)

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
", 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
", 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
", pOI->iDragFrom, pOI->iDragTo);
            }
            return 1;
    }
    return 0;
}
long CMainFrame::OnOutbarNotify(WPARAM wParam, LPARAM lParam)

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.
");
        }
}

class child:public parent
{
    public:
        virtual void print()
        {
            printf("I am child.
");
        }
}

void howToPrint(parent *p)
{
    p->print();
}

int main()
{
    parent p1;
    child c1;
    howToPrint(&p1);
    howToPrint(&c1);//多态
}
test.cpp

多态实现的原理: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:UsersafeiDocumentsVisual Studio 2015ProjectsSecMngAdminDebugMySecAdmin.exe
    //C:UsersafeiDocumentsVisual Studio 2015ProjectsSecMngAdminDebugsecmngadmin.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;
}
int CSecMngAdminAPP::readSecMngCfg()

    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.h

>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 == '
')
                {
                    //没有配置value
                    //printf("配置项:%s 没有配置value 
", pKey);
                    goto End;
                }
                break;
            }
        }

        //获取valude结束点
        while (1)
        {
            if ((*pTmp == ' ' || *pTmp == '
'))
            {
                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 
");
        goto End;
    }

    fp = fopen(pFileName, "r+");
    if (fp == NULL)
    {
        rv = -2;
        printf("fopen() err. 
");
        //goto End;
    }

    if (fp == NULL)
    {
        fp = fopen(pFileName, "w+t");
        if (fp == NULL)
        {
            rv = -3;
            printf("fopen() err. 
");
            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
", pKey, pValue);
            strcat(filebuf, lineBuf);
            //若存在key
            iTag = 1;
        }
    }

    //若key关键字,不存在 追加
    if (iTag == 0)
    {
        fprintf(fp, "%s = %s
", pKey, pValue);
    }
    else //若key关键字,存在,则重新创建文件
    {
        if (fp != NULL)
        {
            fclose(fp);
            fp = NULL; //避免野指针
        }

        fp = fopen(pFileName, "w+t");
        if (fp == NULL)
        {
            rv = -4;
            printf("fopen() err. 
");
            goto End;
        }
        fputs(filebuf, fp);
        //fwrite(filebuf, sizeof(char), strlen(filebuf), fp);
    }

End:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return rv;
}
cfg_op.c

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:UsersafeiDocumentsVisual Studio 2015ProjectsSecMngAdminDebugMySecAdmin.exe
    //C:UsersafeiDocumentsVisual Studio 2015ProjectsSecMngAdminDebugsecmngadmin.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;
}
int CSecMngAdminAPP::readSecMngCfg()

(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;
    }
}
void CDlgInitCfg::OnBnClickedOk()

(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
    {
        // 借助全局变量 连接数据库
        
    }
    
    ...

}
BOOL CSecMngAdminApp::InitInstance()

在 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;
    }
    
    
}
void CDlgInitCfg::OnBnClickedOk()

(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

将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 );
    }
    
    ...

}
BOOL CSecMngAdminApp::InitInstance()

运行结果如下:

注意:void CDlgInitCfg::OnBnClickedOk()注释掉:CDialog::OnOK();后打开主界面后原界面不会退出!

所以,应该修改为:

void CDlgInitCfg::OnBnClickedOk()

 另外,

(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 );
    }
    
    // 借助全局变量--连接数据库
    
    ...

}
BOOL CSecMngAdminApp::InitInstance()

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;
}
void CDlgInitCfg::OnBnClickedButtonLinktest()

》测试:

点击“运行”,先输入错误的数据源、用户名和密码:

点击“测试”,会捕捉到错误:

点击“运行”,输入正确的数据源、用户名和密码:

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,转载请注明出处。

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