MFC应用程序概述.ppt

上传人:牧羊曲112 文档编号:5439897 上传时间:2023-07-07 格式:PPT 页数:73 大小:861.50KB
返回 下载 相关 举报
MFC应用程序概述.ppt_第1页
第1页 / 共73页
MFC应用程序概述.ppt_第2页
第2页 / 共73页
MFC应用程序概述.ppt_第3页
第3页 / 共73页
MFC应用程序概述.ppt_第4页
第4页 / 共73页
MFC应用程序概述.ppt_第5页
第5页 / 共73页
点击查看更多>>
资源描述

《MFC应用程序概述.ppt》由会员分享,可在线阅读,更多相关《MFC应用程序概述.ppt(73页珍藏版)》请在三一办公上搜索。

1、第 2 章 MFC应用程序概述 2.1 Windows应用程序概述 2.2 MFC应用程序框架 2.3 消息映射与消息处理 2.4 程序调试 2.5 应用实例,Visual C+程序设计与应用教程,2,学习MFC的艰难其主要原因有:(1)MFC采用的是C+语言,对C+本身的技术没有掌握。因此想学好MFC,必须把第一篇的内容搞清楚;(2)MFC庞大的类库中类的命名及成员变量或成员函数的命名没有清楚,其实很简单,MFC中的类及其成员的命名都采用的是匈牙利命名法,把这些名字的英文直接翻译过来,就知道它是什么意思。(3)MFC编程,首先生成的是一个应用程序架构,对这个架构的不了解导致生成的代码不知其为

2、何如此。本章就是要给大家一个架构的概貌;(4)传统的API编程的步骤统统不见了,使人们不知道编写的Windows程序什么时候建立起来、什么时候消亡.事实上,MFC的应用程序架构把类似于API编程所要求的步骤都隐藏了,都封装到架构中了,3,2.1 Windows应用程序概述,Windows应用程序运行于Windows操作系统上,与DOS应用程序有着本质的区别。所有的Windows应用程序都是由消息驱动的,消息处理是所有Windows应用程序的核心。,4,Windows应用程序,操作系统,计算机硬件之间的相互关系,http:/,5,关于API,向下的箭头表示应用程序可以通知操作系统执行某个具体的动

3、作,如操作系统能够控制声卡发出声音,但它并不知道应该何时发出何种声音,需要应用程序告诉操作系统该发出什么样的声音。这个关系好比有个机器人能够完成行走的功能,但是,如果人们不告诉它往哪个方向上走,机器人是不会主动行走的。这里的机器人就是操作系统,人们就是应用程序。,http:/,6,关于API,那么,应用程序是如何通知操作系统执行某个功能的呢?有过编程经验的读者都应该知道,在应用程序中要完成某个功能,都是以函数调用的形式实现的,同样,应用程序也是以函数调用的方式来通知操作系统执行相应的功能的。操作系统所能够完成的每一个特殊功能通常都有一个函数与其对应,也就是说,操作系统把它所能够完成的功能以函数

4、的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称Windows API。如CreateWindow就是一个API函数,应用程序中调用这个函数,操作系统就会按照该函数提供的参数信息产生一个相应的窗口。,http:/,7,关于消息及消息队列,向上的箭头表示操作系统能够将输入设备的变化上传给应用程序。如用户在某个程序活动时按了一下键盘,操作系统马上能够感知到这一事件,并且能够知道用户按下的是哪一个键,操作系统并不决定对这一事件如何作出反应,而是

5、将这一事件转交给应用程序,由应用程序决定如何对这一事件作出反应。好比有个蚊子叮了我们一口,我们的神经末梢(相当于操作系统)马上感知到这一事件,并传递给了我们的大脑(相当于应用程序),我们的大脑最终决定如何对这一事件作出反应,如将蚊子赶走,或是将蚊子拍死。对事件作出反应的过程就是消息响应。,http:/,8,关于消息及消息队列,操作系统是怎样将感知到的事件传递给应用程序的呢?这是通过消息机制(Message)来实现的。操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序,参看MSDN。MSG结构定义如下:typedef struct tagMSG HWND hwnd;UINT m

6、essage;WPARAM wParam;LPARAM lParam;DWORD time;POINT pt;MSG;,http:/,9,2.1.1 窗口,窗口是用户界面中最重要的部分。它是屏幕上与一个应用程序相对应的矩形区域,是用户与产生该窗口的应用程序之间的可视界面。,10,窗口的创建,创建一个完整的窗口需要经过下面四个操作步骤:设计一个窗口类;注册窗口类;创建窗口;显示及更新窗口。,http:/,11,设计窗口类,typedef struct _WNDCLASS UINT style;WNDPROClpfnWndProc;int cbClsExtra;int cbWndExtra;HAN

7、DLE hInstance;HICON hIcon;HCURSOR hCursor;HBRUSH hbrBackground;LPCTSTR lpszMenuName;LPCTSTR lpszClassName;WNDCLASS;,http:/,12,窗口类的类型,在我们的程序中经常要用到一类变量,这个变量里的每一位(bit)都对应某一种特性。当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。当变量中的某几位同时为1时,就表示同时具有几种特性的组合。一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值

8、中仅有与该特征相对应的那一位(bit)为1,其余的bit都为0。我们使用goto definition就能发现CS_VREDRAW=0 x0001,CS_HREDRAW=0 x0002,CS_DBLCLKS=0 x0008,CS_NOCLOSE=0 x0200。他们的共同点就是只有一位为1,其余位都为0。如果我们希望某一变量的数值既有CS_VREDRAW特性,又有CS_HREDRAW特性,我们只需使用二进制OR(|)操作符将他们进行或运算相组合,如style=CS_VREDRAW|CS_HREDRAW|CS_NOCLOSE。如果我们希望在某一变量原有的几个特征上去掉其中一个特征,用取反()之后

9、再进行与(&)运算,就能够实现,如在刚才的style的基础上去掉CS_NOCLOSE特征,可以用style&CS_NOCLOSE实现。,http:/,13,窗口过程函数,第二个成员变量lpfnWndProc指定了这一类型窗口的过程函数,也称回调函数。回调函数的原理是这样的,当应用程序收到给某一窗口的消息时(还记得前面讲过的消息通常与窗口相关的吗?),就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。对于一条消息,操作系统到底调用应用程序中的哪个函数(回调函数)来处理呢?操作系统调用的就是接受消息的窗口所属的

10、类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。,http:/,14,窗口过程函数,举例:汽车厂家生产汽车好比应用程序创建窗口,用户使用汽车好比操作系统管理窗口,某种汽车在销售前就指定好了修理站(类似回调函数),当用户的汽车出现故障后(类似窗口收到消息),汽车用户(类似操作系统)自己直接找到修理站去修理,不用厂家(类似应用程序)亲自将车送到修理站去修理,但修理站还得由厂家事先建造好。,http:/,15,2.1.2 消息和事件,Windows是一个基于事件的消息驱动系统,Windows应用程序是按照“事件

11、消息处理”非顺序的机制运行的。所谓消息,就是用于描述某个事件发生的信息,而事件是对于Windows的某种操作。事件是因,消息是果,事件产生消息,消息对应事件。所谓消息的处理,其实质就是事件的处理。当有某个事件(如单击鼠标、键盘输入和执行菜单命令等)发生时,Windows会根据具体的事件产生对应的消息,并发送到指定应用程序的消息队列。应用程序从消息队列中取出消息,并根据不同的消息进行不同的处理。,16,2.1.3 基本数据类型,Windows编程中常用的基本数据类型的别名。,17,18,2.1.4 句柄,句柄是Windows编程的基础,所谓句柄就是Windows使用的一种无重复整数。句柄主要用来

12、标识应用程序中的一个对象,如窗口、实例、菜单、内存、输出设备、控制或文件等。Windows常见的公用句柄类型如下表所示:,19,20,由于对应的MFC类已对句柄进行了封装,大多数情况下不再需要访问句柄。,21,2.2 MFC应用程序框架,下面通过一个实例介绍利用MFC AppWizardexe创建应用程序框架的步骤。,2.2.1 创建MFC应用程序框架,22,【例2.1】编写一个SDI单文档应用程序Li2_1,程序运行后,通过消息框输出“这是一个单文档应用程序!”的提示信息。(1)选择File菜单下的New菜单项,系统弹出New对话框。,单击,输入,工程名,保存文件路径,默认,23,单文档界面

13、(SDI)应用程序。应用程序运行时,只能打开一个文档。例如在记事本或写字板中当选择打开新的文档时,当前显示的文件在新文件打开之前自动关闭,多文档界面(MDI)应用程序。应用程序可以同时打开多个文档。如,WORD,基于对话框的应用程序。应用程序将显示一个简单的对话框来处理用户的输入,例如计算器,(2)MFC AppWizard-Step 1对话框主要用于选择应用程序类型。AppWizard可以创建3种类型的应用程序框架。,语言种类,是否支持文档/视图结构,24,(3)MFC AppWizard-Step 2 of 6对话框中主要用于选择应用程序所支持数据库方式。,不包含任何数据库支持,包含最低限

14、度的数据库支持,创建对应表的一个数据库类和一个视图类,不附加标准文件支持,创建对应表的一个数据库类和一个视图类,并附加标准文件支持,25,(4)在MFC AppWizard-Step 3 of 6对话框中选择应用程序所支持的复合文档类型。,不支持任何复合文档,应用程序作为复合文档容器,包含可以链接或嵌入的ActiveX对象,应用程序仅仅可以创建能够嵌入到其他应用程序中的文档,但是不能作为一个单独运行的应用程序,应用程序可以单独运行,同时也可以作为一个对象嵌入到其他应用程序中,应用程序能够作为容器包含嵌入或链接的ActiveX对象,同时也能够作为一个可以嵌入到其他程序中的对象,26,(5)在MF

15、C AppWizard-Step 4 of 6对话框中设置应用程序的外观特征。,添加工具栏,自动生成状态栏,添加打印和打印预览菜单项,在帮助菜单中自动添加索引和使用帮助菜单项,使应用程序的外观呈三维显示,消息应用程序接口,应用程序具有TCP/IP通信功能,传统风格工具条,Internet Explorer风格工具条,27,(6)在MFC AppWizard-Step 5 of 6对话框中设置应用程序的风格。,创建标准的MFC应用程序,创建类似于Windows资源管理器风格的应用程序,在生成的程序框架的源文件中加入注释,不加入注释信息,使用动态链接库,使用静态链接库,28,(7)在MFC App

16、Wizard-Step 6 of 6对话框中,可以设置向导生成的文件名和类名。,设置类名,设置文件名,29,(8)打开项目工作区中的ClassView类视图,单击CLi2_1View类左边的“+”展开视图类,双击打开OnDraw()函数,并添加代码。void CLi2_1View:OnDraw(CDC*pDC)CLi2_1Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw code for native data hereMessageBox(这是一个单文档应用程序!,消息框,MB_ICONEXCLAMATION|MB_OKCANC

17、EL);,30,(9)选择Build菜单中的Build Li2_1.exe菜单项,对工程进行编译和链接,生成可执行文件。再选择Build菜单中的Execute Li2_1.exe菜单命令运行程序。,31,MFC应用程序框架结构类的继承关系:,2.2.2 MFC应用程序框架结构类,32,1.CObject类 CObject是MFC类库的根类,它几乎描述了所有MFC类的一些公共特性,并且给所有由它派生出的类提供了3种重要特性,即:串行化支持、运行时类信息支持以及诊断和调试支持。,2.CCmdTarget类 命令类CCmdTarget是CObject的子类,它是MFC库中所有具有消息映射属性的类的公

18、共基类。从CCmdTarget派生的类能在程序运行时动态创建对象和处理命令消息。,33,3.CWinApp类 应用程序类CWinApp是CWinThread的子类,封装了初始化、运行、终止应用程序的代码。可以由此派生自己的应用类。,4.CWnd类 窗口类CWnd提供了MFC中所有窗口类的基本功能。从CWnd派生的类可以拥有自己的窗口,并对它进行控制。,34,5.CFrameWnd类 CFrameWnd类是CWnd类的派生类。它是所有其他框架窗口类的基类,主要用来管理一个窗口。CFrameWnd类的对象是一个框架窗口,包括边框、标题栏、菜单、最大化按钮、最小化按钮和一个激活的视图。CFrameW

19、nd类支持单文档界面,对于多文档界面,使用它的两个派生类CMDIFrameWnd和CMDIChildWnd。CMDIFrameWnd类用于MDI的主窗口,CMDIChildWnd类用于MDI的子窗口。,35,6.CView类 视图类CView是CWnd类的子类。视图类及其派生类用于管理框架窗口的客户区。7.CDocument类 文档类CDocument负责装载和维护文档。文档包括应用程序的工作成果或环境设置数据等,可以是程序需要保存的任何内容。,36,一个MFC应用程序并不直接操作上述类,而是以上述类为基类派生新的类,从而构建Windows应用程序的基本框架。例如,应用程序Li2_1中的类与这

20、些基类的派生关系:,37,1.应用程序向导生成的文件 下面以例2.1中创建的应用程序Li2_1为例,介绍MFC AppWizardexe向导所生成的各类文件及功能。,2.2.3 MFC应用程序分析,38,(1)头文件与实现文件 MainFrm.h和MainFrm.cpp:定义和实现窗口框架类CMainFrame。CLi2_1Doc.h和CLi2_1Doc.cpp:定义和实现文档类CLi2_1Doc。CLi2_1View.h 和CLi2_1View.cpp:定义和实现视图类CLi2_1View。CLi2_1.h 和CLi2_1.cpp:定义和实现应用程序类CLi2_1App。Resource.h

21、:定义工程中所有资源标识符,给资源ID分配一个整数值。StdAfx.h和StdAfx.cpp:用于建立一个预编译的头文件CLi2_1.PCH和一个预定义的类型文件StdAfx.obj。,39,(2)资源文件Li2_1.rc和Li2_1.rc2:Li2_1.rc文件是程序所使用的所有Windows资源的列表。Li2_1.ico:应用程序的图标所使用的图标文件。Li2_1Doc.ico:应用程序的文档图标文件。文档图标一般显示在多文档程序界面上。Toolbar.bmp:工具栏按钮的位图文件。,40,(3)项目工作区文件和工程文件Li2_1.dsw:保存当前工作区所包含的工程的信息。Li2_1.ds

22、p:包含当前工程的设置、工程中的文件等信息。,41,(4)其他文件 Li2_1.clw:该文件是类信息文件。它保存了ClassWizard编辑现有类或增加新类时需使用的类信息,同时还保存了创建、编辑消息映射和成员函数所需的信息。ReadMe.txt:该文件是工程自述文件。主要介绍所创建文件的内容和功能,以及添加用户代码、更改使用语言的方法。,42,2.应用程序的执行过程,启动:创建应用程序对象theApp、调用应用程序类的构造函数初始化对象theApp。应用程序框架调用MFC提供的AfxWinMain()主函数。AfxWinMain()主函数,首先通过调用全局函数AfxGetApp()来获取t

23、heApp的指针pApp;然后通过该指针调用theApp的成员函数InitInstance()来初始化应用程序。,43,在应用程序的初始化过程中,同时还构造了文档模板,产生最初的文档、视图和主框架窗口,并生成工具栏和状态栏。当InitInstance()函数执行完毕后,WinMain()函数将调用成员函数Run(),进入消息处理循环;函数Run()收到WM_QUIT消息。MFC首先调用CWinApp类的成员函数ExitInstance();然后调用静态对象的析构函数;退出应用程序,将控制权交给操作系统。,44,消息对话框,一种简单的对话框,用户可以直接调用消息对话框函数来使用它,而不需要自己创

24、建。Visual C+提供三个消息对话框函数,它们的原型为:int AfxMessageBox(LPCTSTR lpText,UINT nType=MB_OK,UINT nIDHelp=0);int MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT nType);int CWnd:MessageBox(LPCTSTR lpText,LPCTSTR lpCaption=NULL,UINT nType=MB_OK);,45,46,47,2.2.4 文档/视图结构,文档视图体系结构是MFC应用程序框架结构的基石,它定义了一种程序结构

25、,这种结构利用文档对象保存应用程序的数据,依靠视图对象控制视图显示数据,文档与视图的关系是一对多的关系,也就是说,文档中的数据可以以不同的方式显示。,48,49,2.3 消息映射与消息处理,Windows消息主要有3种类型:标准Windows消息、控件消息和命令消息。1标准Windows消息除WM_COMMAND以外,所有以“WM_”为前缀的消息都是标准Windows消息;由窗口类或视图类处理;都有默认的处理函数,它们在CWnd类中进行了预定义。,2.3.1 消息的类别及其描述,50,标准的Windows消息又分为3类:键盘消息、鼠标消息和窗口消息。(1)键盘消息 键盘消息与键盘某个键的动作相

26、关联。常见的键盘消息有以下几种。WM_KEYDOWN、WM_KEYUP:按下、释放非系统键产生的消息。WM_CHAR:输入非系统字符时产生的消息。,51,(2)鼠标消息 鼠标消息涉及到鼠标的单击、双击、拖动等。常用的鼠标消息有以下几种。WM_MOUSEMOVE:鼠标移动时产生的消息。WM_RBUTTONDOWN:鼠标右键按下时产生的消息。WM_LBUTTONDOWN:鼠标左键按下时产生的消息。WM_LBUTTONDBLCLICK:鼠标双击时产生的消息。,52,(3)窗口消息 窗口消息一般与创建窗口、绘制窗口、移动窗口和销毁窗口等动作有关。在MFC应用程序中,窗口消息是由视图类、窗口类及它们的派

27、生类处理的。,53,2.控件消息 控件消息是指控件或其他子窗口向父窗口发送的WM_COMMAND消息。与其他标准的Windows消息一样,控件消息也应该在视图类、窗口类中进行处理。但是,如果用户单击按钮控件时,发出的控件通知消息BN_CLICKED将作为命令消息来处理。,54,3.命令消息 命令消息是由菜单项、工具栏按钮、快捷键等用户界面对象发出的WM_COMMAND消息。命令消息与其他消息不同,它可被更广泛的对象如文档、文档模板、应用程序对象、窗口和视图等处理。,55,2.3.2 MFC消息映射机制,MFC采用消息映射来处理消息;消息映射机制包括一组消息映射宏,用于把一个Windows消息和

28、其消息处理函数联系起来。所有从CCmdTarget类派生出来的类都能够拥有自己的消息映射。与MFC消息映射机制有关的宏有以下3个:DECLARE_MESSAGE_MAP()BEGIN_MESSAGE_MAP(MyClass,MybaseClass)END_MESSAGE_MAP(),56,为了使用消息映射宏:首先在类定义的结尾用DECLARE_MESSAGE_MAP()宏来声明使用消息映射,该宏表示在为各个处理函数所写的类声明之后存在消息映射条目,这些函数是该类的成员函数。然后在类的实现源文件中用BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏来定义消息映射。MFC

29、应用程序MESSAG_MAP消息映射的形式如下:,57,BEGIN_MESSAGE_MAP(MyClass,MybaseClass)/AFX_MSG_MAP(MyClass)/ClassWizard维护消息映射宏的标记 ON_./MFC预定义消息映射宏 ON_MESSAGE(message,memberFun)/用户自定义消息映射宏/AFX_MSG_MAPEND_MESSAGE_MAP()其中,MyClass是拥有消息映射的派生类名,MybaseClass 是其基类名。,58,【例2.2】利用ClassWizard为例2.1中生成的应用程序Li2_1添加鼠标右键按下消息,即WM_RBUTTON

30、DOWN消息,并为File菜单下的Open菜单项添加消息处理函数,分析ClassWizard类向导所进行的消息映射。,59,2.3.3 自定义消息处理,自定义消息处理:定义消息名、分配ID值及完成消息映射。Windows将所有的消息值分为4段:0 x00000 x03FF段:用于Windows系统消息0 x04000 x7FFF段:用于用户自定义的窗口消息0 x80000 xBFFF段:为Windows保留值0 xC0000 xFFFF段:用于应用程序的字符串消息。,60,常量WM_USER(为0 x0400)与第一个自定义消息值相对应,用户必须为自己的消息定义相对于WM_USER的偏移值,利

31、用#define语句直接定义自己的消息,如下所示:#define WM_USER WM_USER+3;/自定义消息WM_USER 也可以调用窗口消息注册函数RegisterWindowMessage()来定义一个Windows消息,由系统分配消息一个整数值。该函数原型为:UINT RegisterWindowMessage(LPCTSTR lpString);其中参数lpString是要定义的消息名,调用成功后将返回该消息的ID值。,61,【例2.3】编写一个自定义消息应用程序,并添加WM_RBUTTONDOWN消息。当程序运行时,用户在视图窗口中单击鼠标右键,则调用自定义消息处理函数,输出文

32、本“自定义消息WM_MYMESSAGE的处理函数被调用!”。,62,2.4 程序调试,程序调试分为源程序语法错误的修改和程序逻辑设计错误的修改两个阶段,编译器只能找出源程序的语法错误,程序的逻辑设计错误只能靠程序员利用调试工具来手工检查和修改。,63,2.4.1 查找源程序中的语法错误 语法错误分为:一般错误(error):出现该错误时将不会产生可执行程序;警告错误(warning):出现该错误时能够生成可执行程序,但程序运行时可能发生错误,严重的warning错误还会引起死机现象。,64,如果程序有语法错误,则在执行编译、链接命令时,Visual C+编译器将在输出窗口中给出语法错误提示信息

33、,但链接错误提示信息不能给出错误发生的具体位置。在输出窗口中双击错误提示信息或按F4键可以返回到源程序编辑窗口,并通过一个箭头符号定位到产生错误的语句。,65,2.4.2 调试器 为了查找和修改程序中的逻辑设计错误,Visual C+IDE提供了重要的调试工具Debug。单击Build|Start Debug菜单中的菜单项,可以启动Debug。,66,Start Debug子菜单中有Go、Step Into、Run To Cursor及Attach to Process菜单项,开始或继续调试程序,到某个断点、程序的结束或需要用户输入的地方停止,程序执行到当前光标处,相当于在光标处临时设置了一个

34、断点,单步执行程序的每一个指令,能进入被调用的函数内部,将调试器与一个正在运行的进程相连接,67,调试过程开始后,Debug主菜单取代Build主菜单出现在菜单栏中,同时出现一个可停靠的调试工具栏和一些调试窗口。,68,即使源程序没有语法错误,但最后生成的可执行程序也没有像程序设计要求的那样运行,这类程序设计上的错误被称为逻辑设计错误或缺陷(bug)。跟踪调试程序是查找此类逻辑设计错误方法中最常采用的动态方法。跟踪调试的基本原理就是在程序运行过程的某一阶段观测程序的状态。而在一般情况下程序是连续运行的,所以我们必须使程序在某一点停下来。,2.4.3 跟踪调试程序,69,选择Edit菜单下的Br

35、eakpoints菜单项,显示Breakpoints对话框。,1.设置断点,设置断点的3种方式:按位置按表达式的值按窗口消息,70,2.控制程序运行,Step Over:运行当前箭头指向的代码(只运行一条代码)。,Step Into:如果当前箭头所指的代码是一个函数的调用,则用Step Into进入该函数并进行单步执行。Step Out:如果当前箭头所指向的代码是在某一函数内部,用它可使程序运行至函数返回处。Run to Cursor:使程序运行至光标所指的代码处。,71,3.观察数据变化,在调试过程中,可以通过Watch和Variables窗口查看当前变量的值。,72,【例2.4】编写一个SDI单文档应用程序Li2_4,求210中偶数的和,并在视图中输出计算结果。,73,2.5 应用实例,编写一个单文档应用程序Sy2,程序运行后,首先在视图窗口中输入文本,然后通过键盘上的光标键控制该文本向左、向右、向上和向下4个方向移动。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号