MFC学习

内容分享2周前发布
0 0 0

MFC六大核心机制

https://blog.csdn.net/ligand/article/details/49848161

[[MFC 六大核心机制]]

MFC的六大核心机制概述

1、MFC程序的初始化工作

在MFC中所有的类都来源于一个基类:CObject。MFC程序初始化过程中,其实就是虚函数的调用的过程,分清调用执行的到底是哪一个具体的虚函数,是父类的虚函数,还是基类的虚函数,都是至关重要的。

2、RTTI运行时类型识别

MFC程序运行过程中需要对类的类型进行动态的判断。在实现这一个机制的方法是,在MFC中的每一个类中都有一个CRuntimeClass类(《深入浅出MFC》中是这么命名,具体MFC中具有这个功能的类的真实名称,我还不知道),它用来记录类的基本信息,里面包含了必要的一些数据信息(比如类的名称,父类指针,对象大小,以及维护一个链表的必须指针),最后通过将所有类的CRuntimeClass以串行的方式串联起来,构成一个庞大的“类记录表”。这样在实际的程序运行时,当要判断当前类的信息时,就只要逐个比较类记录表中各个类的信息就可以确定到底是哪个类了。

3、Dynamic Creation(动态创建)

所谓的动态创建,我的理解是:在程序运行时,根据类名称来创建类对象,而这种动态创建的实现还是需要依赖于前面的RTTI中维护的“类记录表”。

4、Persistence机制

这个机制简单的理解,就是一种能够将所有类型的类记录到文件或者从文件读取到类的机制(可能理解的还不够透彻,欢迎指正)。

5、Message Mapping消息映射机制

MFC程序的执行是依赖于消息传递的。所谓的Message Map(消息映射表)其实就是在MFC整个类继承关系图中消息传递攀爬的一个图,这个图中有的消息是从子类攀爬到父类这样向上走的,也有的消息会产生旁流的情况。从形式上来看,是通过类似于RTTI、Dynamic Creation中定义宏的方式,简化消息映射定义的方式的,如:DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、ON_COMMAND、END_MESSAGE_MAP。

6、Command Routing命令绕行

MFC 对于消息绕行的规定是: 如果是一般的Windows 消息(WM_xxx),一定是由衍生类别流向基础类别,没有旁流的可能。

如果是命令消息WM_COMMAND,就有奇特的路线了:

MFC学习

MFC 桌面应用程序

MFC 类

层次结构图

![[MFC_Hierarchy_Chart1of3.pdf]]
![[MFC_Hierarchy_Chart2of3.pdf]]
![[MFC_Hierarchy_Chart3of3.pdf]]

MFC中的主要类

1.通过数据处理类

字符串类CString,集合类CByteArray,CDwoedArray,CRtrArray,CStringArray,映射类CMapPtrToTpr,CMapStringToOb,链表类CObList,CPtrList,CStringList

2.Windows API封装类

将API函数按其功能分别封装到不同的类中,可以通过类成员的方式访问API函数

CWnd,CFrameWnd,CMIDIFrameWnd,CMainFrameWndCDialog,CFileDialogCDC,CPaintDC,CClientDC,CWindowDC等

3.应用程序框架类

将Windows程序的基本机构封装在不同的类中,程序员可以通过这些类生成Windwos程序的雏形。应用程序框架类包括应用程序类CWinAPP,线程类CWindThread,文档模版类CDocTemplate,CSingleDocTEmplate,CMultiDocTemplate

文档类CDocUment等

4.工具类

工具条CToolBar,菜单CMenu,状态栏CStatusBar,拆分CSplitterWnd,滚动窗口CScrollBar

5.OLE类

提供了对OLE API 的访问支持,允许用户创建和编辑复合文档,在这样的文档中可以包含文本,图形,声音流媒体等类型的数据

6.数据库类

提供对数据库的存取,建立,连接等操作,数据源类CDatabase,记录集类CRecordSet,记录集视图类CRecordView,DAO接口类CDaoDatabase,DAO数据集类CDaoRectordSet,DAO数据表定义类CDaoTebleDef

7.网络类

允许用户通过ISAPI或WindowsSockets实现计算机网络互连,CSocket,ISAPI,CHttpFiter,CHttpServer,CSocketFile

MFC创建选项

复合文档支持的各选项功能如下:


◆ 无复合文档支持:即无OLE(Object Linking and Embedding,对象连接与嵌入)支持。 
◆ 容器支持:将对象嵌入或链接至文档,以便用户编辑。
◆ 袖珍服务器:表示应用程序可创建和管理复合文档对象。不能独立运行并且仅支持嵌入项。 
◆ 完全服务器:表示应用程序可创建和管理复合文档对象。能够独立运行,并且既支持链接项也支持嵌入项。 
◆ 容器/完全服务器:表示应用程序可以既是容器又是服务器。容器是可将嵌入项或者链接项并入自己的文档中的应用程序。服务器是可创建供容器应用程序使用的自动化项的应用程序。 其他支持包括: 
◆ 自动化:允许应用程序向脚本工具和其他应用程序公开对象。 
◆ ActiveX控件:应用程序能够将ActiveX控件包含在内。

MAPI支持:可以从应用程序直接调用MAPI函数。

MAPI”表示“消息传递应用程序接口”。它是电子邮件等应用程序的消息传递结构和客户接口组件。如果双方应用程序都启用“MAPI”,就可以相互共享邮件信息,为多个应用程序提供一致接口。


◆ Windows套接字:启用适当的头文件、库和MFC Windows Socket规范,使应用程序支持TCP/IP网络协议。

说你对Windows消息机制的理解

Windows系统是一个消息驱动的操作系统。什么是消息呢?下面从不同的几个方面进行讲解:


1)消息的组成:一个消息由一个消息名称(UINT)和两个参数(WPARAM,LPARAM)组成。当用户进行了输入或是窗口的状态发生改变时系统都会发送消息到某一个窗口。例如,当菜单选择之后会有WM_COMMAND消息发送,WPARAM的高字节中(HIWORD(wParam))是命令的ID号,对菜单来讲就是菜单ID。当然,用户也可以定义自己的消息名称,也可以利用自定义消息来发送通知和传送数据。 
2)谁将收到消息:一个消息必须有一个窗口接收。在窗口过程(WNDPROC)中可以对消息进行分析,对自己感兴趣的消息进行处理。例如,希望对菜单选择进行处理,那么可以定义对WM_COMMAND进行处理的代码,如果希望在窗口中进行图形输出,则就必须对WM_PAINT进行处理。 
3)未处理的消息到哪里去了:Microsoft为窗口编写了默认的窗口过程,这个窗口过程将负责处理那些没有被处理的消息。正因为有了这个默认窗口过程,我们才可以利用Windows的窗口进行开发而不必过多关注窗口各种消息的处理。例如,窗口在被拖动时会有很多消息发送,而我们都可以不予理睬而让系统自己去处理。 
4)窗口句柄:说到消息就不能不说窗口句柄,系统通过窗口句柄在整个系统中唯一标识一个窗口,发送一个消息时必须指定一个窗口句柄表明该消息由哪个窗口接收。而每个窗口都会有自己的窗口句柄,所以用户的输入就会被正确地处理。例如,有两个窗口共用一个窗口过程代码,在窗口1上按下鼠标时消息就会通过窗口1的句柄被发送到窗口1而不是窗口2。 

系统将会维护一个或多个消息队列,所有产生的消息都会被放入或插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄而将该消息发送给拥有该窗口程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统,所以Windows可以同时进行多个任务。

基于消息的事件驱动机制(Message Based, Event Driven)

消息泵(Windows应用程序)

消息泵负责从应用程序的消息队列中读取消息、转换消息、派发消息。


MSG msg;

 // 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
     {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }
}

以上出现的函数都是Windows API 函数

GetMessage 从消息队列中读取消息TranslateMessage 消息翻译、转换。DispatchMessage 派发消息、找到消息对应的窗口、调用响应函数

消息队列

(1)系统消息队列:这是系统唯一队列,设备驱动把用户的操作输入转化成消息存放于系统队列中,然后系统会把此消息放到目标窗口所在的线程消息队列中等待处理。
(2)线程消息队列:每一个GUI线程都会维护一个线程消息队列,然后线程消息队列中的消息会被送到相应的窗口过程处理。

消息队列并不可以直接访问,但是我们可以通过指定接口去访问消息队列。

PostMessage函数,用于向消息队列中追加消息,并立即返回;GetMessage函数,用于从消息队列中读取消息;

模态对话框和非模态对话框的区别

模态对话框:在子界面活动期间,父窗口是无法进行消息响应。独占用户输入非模态对话框:各窗口之间不影响。

模态对话框通过在消息循环内再造消息循环。如果当前窗口内的消息循环不退出,父窗口的消息循环将无法运转,也即无法响应。从而产生模态对话框独占响应的效果。

操作系统层支持

1)Windows IOCP

2)CentOS Epoll

3)xxxBSD kqueue

4.3.2 语言层面的框架支持

1)C/C++ libevent/Muduo/Asio/…

2)Java Netty

3)DotNet DotNetty

4.3.3 Epoll机制说明

1)创建Epoll实例句柄:可以理解为管理其他socket的领头羊;

2)事件注册:为每个SOCKET要关注的事件进行注册,服务端监听SOCKET

主要关注有没有新的连接进来;一般性SOCKET关注是否有数据进来,需要读取;超时,事件处理;…

3)进入等待状态,有事件进来时,操作系统会进行通知;

4)事件处理,根据操作系统的通知,应用程序进行反馈,调用对应事件的处理函数进行响应。

数据库编程基础

ODBC数据库链接:可以学到在VC 6.0中使用ODBC进行数据库链接。ADO数据库链接:可以学到在VC 6.0中使用ADO进行数据库链接。

OLE DB

OLE DB模板是活动模板库(ATL)的一部分,它们通过提供实现许多常用OLE DB接口的类来使得高性能OLE DB数据库技术使用起来很简单。模板库中附带有创建OLE DB起始应用程序的向导支持。 模板库包含两部分:

OLE DB使用者模板:用于实现OLE DB客户(使用者)应用程序。OLE DB提供程序模板:用于实现OLE DB服务器(提供程序)应用程序。

OLE DB数据使用者模板是由一些模板组成的。下面对一些常用类做一些介绍。

(1)CDataSource类 CDataSource类与OLE DB的数据源对象相对应。这个类代表了OLE DB数据提供程序和数据源之间的连接,即主要负责数据源对象。只有当数据源的连接被建立之后,才能产生会话对象,可以调用Open()函数来打开数据源的连接。(2)CSession类 CSession所创建的对象代表了一个单独的数据库访问的会话,该类负责管理数据源和应用程序进程的会话。一个用CDataSource类产生的数据源对象可以创建一个或者多个会话,要在数据源对象上产生一个会话对象,需要调用Open()函数来打开。同时,会话对象还可用于创建事务的操作。(3)CEnumeratorAccessor类CEnumeratorAccessor类是用来访问枚举器查询后所产生的行集中可用数据提供程序的信息的访问器,可提供当前可用的数据提供程序和可见的访问器。

访问器类用于管理与访问相关的操作。常用的访问器类如下。

(1)CAcessor类 CAccessor类代表与访问器的类型。当用户知道数据库的类型和结构时,可以使用此类。它支持对一个行集采用多个访问器,并且存放数据的缓冲区是由用户分配的。(2)CDynamicAccessor类 CDynamicAccessor类用来在程序运行时动态地创建访问器。当系统运行时,可以动态地从行集中获得列的信息,可根据此信息动态地创建访问器。(3)CManualAccessor类 CManualAccessor类用来在程序运行时将列与变量绑定或者将参数与变量绑定。

行集类用于管理以行为单位的数据集。常用的行集类如下。

(1)CRowSet类 CRowSet类封装了行集对象和相应的接口,并且提供了一些方法用于查询、设置数据等。可以用Move()等函数进行记录移动,用GetData()函数读取数据,用Insert()、Delete()、SetData()来更新数据。(2)CBulkRowset类 CBulkRowset类用于在一次调用中取回多个行句柄或者对多个行进行操作。(3)CArrayRowset类 CArrayRowset类提供用数组下标进行数据访问。

命令类包括以下两类。

(1)CTable类 CTable类用于对数据库的简单访问,用数据源的名称得到行集,从而得到数据。(2)CCommand类 CCommand类用于支持命令的数据源。

可以用Open()函数来执行SQL命令,也可以用Prepare()函数先对命令进行准备,对于支持命令的数据源,可以提高程序的灵活性和健壮性。 注意 如果使用OLE DB进行数据库程序设计,则应该使用OLE DB类,在使用OLE DB类的过程中,也可以和其他数据库连接方式的类兼容。

HTML交互

CDHtmlDialog

IWebBrowser2

IHTMLDocument2

工具栏与停靠窗口 自定义

MFC控件积累——CMFCToolBar工具栏控件

MFC自动生成时有如下定义:


const int  iMaxUserToolbars = 10;
const UINT uiFirstUserToolBarId = AFX_IDW_CONTROLBAR_FIRST + 40;
const UINT uiLastUserToolBarId = uiFirstUserToolBarId + iMaxUserToolbars - 1;

这个ID值按MSDN上解释:The ID of the child window of the toolbar.应该是工具栏的窗口的ID,并不是工具栏资源ID。

这里的这些资源ID应该属于BCG组件的内置给ToolBar使用的ID,因为
AFX_IDW_CONTROLBAR_FIRST

AFX_IDW_TOOLBAR+40

多个工具栏的话就顺序使用uiFirstUserToolBarId、uiFirstUserToolBarId+1…。


InitUserToolbars(NULL, uiFirstUserToolBarId, uiLastUserToolBarId);应该放在所有工具栏创建之前

2.对多个工具栏,启用自定义按钮的方法一样:


CString strCustomize;
bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
ASSERT(bNameValid);

// 允许用户定义的工具栏操作:
m_fileToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);
m_clientConnectToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);

类:


CMFCToolBarsCustomizeDialog

MFC基础:应用类和窗口类

MFC为Windows应用程序提供了一套非常标准的框架结构。每一个MFC程序,不管是EXE可执行程序还是DLL动态连接库程序,都包含一个应用类(CWinApp的派生类)实例,此应用类代表了整个应用程序,利用它可以进行各种全局操作,比如对注册表的操作、命令行参数处理、文档管理、光标和图标的操作等等。应用类实例在MFC程序全局均可访问,可以通过MFC提供的辅助函数 AfxGetApp得到此实例对象。CWinApp类有几个虚函数值得提一下,首先是CWinApp::Initlnstance 和 CWinApp::Exitlnstance,它们分别完成应 用的初始化以及终结处理,与前面介绍SDK程序框架结构中的初始化和结束处理相对应;另一个虚函数是CWinApp::Onldle,消息循环在程序接收消息的间隙调用 Onldle函数;虚函数CWinApp::Run实现了消息循环。从这几个函数也可以看出,除了程序主窗口外,CWinApp类完成了应用程序的所有基本操作。

对于有用户界面的MFC程序,在应用类CWinApp中还包含一个窗口类(CWnd)数据成员m.pMainWnd,它代表了应用程序的主窗口,对于不同类型的MFC程序,此窗口类也有所不同。由于应用类实例是个全局对象,所以实际上主窗口对象也是个全程生存周期的对象,可以通过多种途径获取,比如 AfxGetApp()->GetMainWnd()、AfxGetApp()-> m_pMainWnd或者直接调用 AfxGetMainWnd辅助函数。

MFC应用结构

(1) MDI应用。SD和MDI应用是Windows程序最标准、最通用的方式,我们熟悉的word字处理程序就是典型的MDI程序,这种程序包含了主框架窗口和子框架窗口,主框架窗口为应用的主窗口,通常包含菜单、工具条和状态条等界面元素,MFC中代表主框架的C++类为CMDIFrameWnd;子框架窗口位于主框架窗口的客户区,当子框架最大化时充满整个主框架客户区,一般地,一个子框架窗口与一个文档对象相联系,MFC中代表子框架窗口的C+ +类为CMDIChildwnd,子框架窗口也可以有自己的控制条。一般子框架窗口不再有自己的菜单,当子框架窗口所代表的文档对象被激活时,主窗口会使用与此文档对象相对应的菜单,供用户对此文档进行各种操作。

MDI应用中,每一个文档不仅有一个子框架窗口用于指定文档窗口在主窗口中的位置,而且还有一个文档对象和视对象,也就是我们通常所说的文档—视结构。文档对象的MFC类为CDocument,它负责文档的各种操作,包括从文件装载数据和保存数据到文件,以及在多视情况下对视的管理。视对象的MFC类为CView或者它的派生类(如CScrol—IView.CEditView等),它负责文档的各种界面特性,包括与用户的交互处理,视本身也是个窗口对象,它充满子框架的客户区,通常文档的显示以及用户的编辑操作(如鼠标键盘操作和剪贴板操作)由视对象处理。

MDI应用包含一个应用类对象和一个主框架窗口类对象,在运行过程中可以产生若干个文档对象,每个文档对象可对应多个视对象,每个视对象与一个子框架窗口对象相对应,这就是MDI程序的基本对象结构。

(2)SDI应用。SDI应用比MDI应用的对象结构要简单一些,因为SDI应用是单文档结构,所以SDI应用只有一个主框架窗口对象而没有子框架对象,主框架对象的MFC类为CFrameWnd类。文档类和视类也分别为CDocument和CView或其派生类,通常文档类与一个视类相对应。

所以,SDI应用包含一个应用类和一个主框架窗口类对象,以及一个文档类和一个视类,在程序运行过程中基本保持这样的对象结构。

(3)基于对话框程序。基于对话框的程序结构最简单,除了应用类,程序从一个对话框开始,当对话框结束后,程序也就结束了。MFC生成的对话框应用程序只是在Initln—stance函数中创建对话框并启动对话框,当对话框退出后, Inithnstance函数返回,程序不再进入消息循环而直接退出。当然对话框本身可以很复杂,甚至包含其他多层对话框,但因为对话框是有模式窗口,所以应用本身可以不管对话框的实现逻辑。

(4) DLL应用。MFC支持三种DLL.应用:静态连接MFC库的正规DLL、动态连接MF库的正规DLL和MFC扩展DLL, COM进程内组件程序应该使用前两种DL应用。这两种应用都包含一个CWinApp派生的应用类实例,在应用类的InitlInstance函数中进行DLL被装载时的初始化操作,在Exilnstance函数中进行终结操作。因为DL,程序被其他的EXE程序或者DLL程序调用,所以它所有的应用逻辑必须通过引出函数体现出来。

正规DLL.是一种标准的DLL,程序,可被各种语言编写的程序调用,而MFC扩展DLL.只能被MFC程序调用,但这种DLL可以直接引出整个C+ +类,使用MFC的C++程序员可以用这种类型的程序提供对MFC的扩展库。

(5)其他应用。MFC除了支持以上几种应用外,还支持其他一些特性,尤其是COM特性或者基于COM标准的特性,因此,从COM特性上来讲,我们也可以把MFC应用分为以下几种:

·支持OLE服务或者包容器的SDI应用,更进一步,可以支持ActiveX文档服务或者包容器。

支持OLE服务或者包容器的MDI应用,更进一步,可以支持ActiveX文档服务或者包容器。

支持自动化( Automation)服务的SDI或者MDI程序。Activex控制应用,生成OCX应用程序。

MFC一些类

CObject是MFC库中最基本的类,它提供了一些基本的功能,包括序列化( serialization)支持、运行时刻类信息、对象诊断输出、与集合类型相兼容。运行时刻类型信息是MFC对象动态创建的基础,更进一步序列化支持使得对象可以在序列化过程中被动态创建,而且,利用运行时刻类型信息可以在程序运行过程中判断C++对象是否与某个类(通过lskindM成员函数)相关联。对象诊断输出特性使得应用程序在调试版本中可以获得对象的状态信息,用于检查内存泄漏、运行过程中转储对象内容等操作。如果我们自己使用的类需要这些特性,则可以直接从CObject派生新的类,然后根据它所提供的机制使用这些特性。

CCmdlarget类或其派生类的实例对象拥有自己的消息处理机制,它可以接收消息,并按照它的消息映射表调用相关的消息控制成员函数。而且CCndTarget类提供了对COM接口的支持,我们将在下一节中详细讨论这种支持机制。

CWnd类是所有窗口对象的基类,CWnd是一个规模较大的类,包括几百个成员函数,基本上覆盖了窗口处理的各个方面,包括各种消息处理、窗口状态控制、对话框子项的处理、剪贴板处理、拖—放处理等等, MFC库中所有与Windows商口对象关联的类都从CWnd类派生,包括框架窗口类、控制条类、对话框类、属性页表(property sheet)类,视类和各种控制(如按钮、编辑框、标签等)类。

MFC库的图形设备环境基类CDC类封装了Win32 SDK的设备环境对象,用于各种图形操作,包括绘制各种图形、文字输出、颜色处理、坐标系统设置、剪裁功能、位图处理、图元文件处理以及打印处理等等,灵活使用CDC及其派生类是我们进行可视化输出的有效手段。

在MFC库的类中,集合类型也属基本类型之一,常用的集合类型包括两种:数组类型和列表类型,用CObject对象作为元素的集合Coblist和CObArray具有一些很好的特性,如序列化、列表诊断信息转储、对象数目自动增长等。MFC库很多地方都用到了集合对象,比如在MDI程序结构中,一个文档可对应多个视,文档对象就维护了一个视对象的列表。在列表中,MFC使用了POSTTON类型的变量作为其位置指针,相当于列表的书签,利用此书签,可以在集合中来回搜索,如果理解了POSTTION变量的使用方法,则使用列表记录集合非常方便。

MFC对COM应用的支持

前面曾经提到过MFC对COM的支持从CCmdTarget类开始,CCmdTarget使用了一种与消息映射表非常类似的机制来实现COM接口,我们把这种机制称为接口映射表。接口映射表的基本思路就是前面介绍的嵌套类,但它通过一组宏把这些细节隐藏起来了。

实现COM接口关键是引用计数和QueryInterface函数的实现, MFC的引用计数实现方法很简单,在CCntTarget类中使用m.dwRef数据成员作为计数器,然后按照COM规范维护计数器的增1和减1操作,所有的接口共享同一个引用计数器。

CCmdTarget类实现1Unknown

IUnknown是所有接口的基础,CCndlarget类提供了一种标准实现,并且支持对象被聚合的情形,所以CCmdTarget类实现了两个版本的 Unknowno

在CCndTarget实现的两个IUnknown中,称为内部Unknown和外部Unknown,分别对应聚合模型中的非委托Unknown和委托1Unknown,

COM引出函数和类厂实现

ColeobjectFactory是一个通用的类厂,我们知道类厂本身也是一个COM对象,所以ColeObjectFactory从CCndTarget派生,并且它实现了IClassfactory2接口,IClass Factory2接口是对IClassFactory接口的扩展,除了包含Createlnstance和LockServer成员函数,它还提供了COM对象许可证支持。

客户要创建一个COM对象必须分两步进行,第一步,客户调用引出函数DIGetClas—sobject得到类厂对象;第二步,类厂对象再创建COM对象。

MFC对COM支持小结

MFC对COM的支持可以从两个方面进行讨论:单个COM对象的实现和类厂的支持。CCmndTarget类提供了cOM对象实现的所有支持,它用接口映射表机制可实现任意多个接口,并且CCmdTarget实现的TUnknown很好地支持了对象被聚合的情形。COleObjec—Factory类实现了通用的类厂,它从CCndlarget类派生,并且又与CCmndTarget类协作,利用COM对象提供的CISD和运行时刻类型信息完成对象的创建工作。

进程内组件程序的几个标准引出函数通过程序状态结构中的类厂表与类厂对象联系起来,而类厂对象又与CCmdTarget派生类联系起来,因此客户程序和COM库可通过这些函数实现对组件程序的各种操作。

功能区

https://docs.microsoft.com/zh-cn/cpp/mfc/ribbon-designer-mfc?view=vs-2019

https://docs.microsoft.com/en-us/windows/win32/api/_windowsribbon/

https://docs.microsoft.com/zh-cn/windows/win32/windowsribbon/-uiplat-windowsribbon-entry

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...