C++中DLL函数的导出和导入

合集下载

如何做才能从dll中导出类

如何做才能从dll中导出类

如何做才能从dll中导出类能不能在一个dll中定义一个类,然后在程序中动态链接这个dll,然后导出这个类呢?要用 MFC 的扩展DLL写倒出类定义//定义一个导出类class AFX_EXT_CLASS CMouseHook : public CObject{public:CMouseHook();virtual ~CMouseHook();BOOL StopMouseHook();BOOL StartMouseHook( HWND hOwner );};OK 了,其它和DLL编写是一样的用API也可以:class extern "C" __dllexport(import) 类名{定义;};不好意思写错了,应该是__declspec(dllimport)顶一、DLL的不同类型使用MFC可以生成两种类型的DLL:MFC扩展DLL和常规DLL。

常规DLL有可以分为动态连接和静态连接。

Visual C++还可以生成WIN32 DLL,但不是这里讨论的主要对象。

1、MFC扩展DLL每个DLL都有某种类型的接口:变量、指针、函数、客户程序访问的类。

它们的作用是让客户程序使用DLL,MFC扩展DLL可以有C++的接口。

也就是它可以导出C++类给客户端。

导出的函数可以使用C++/MFC数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。

同时,在DLL中也可以使用DLL和MFC。

Visual C++使用的MFC类库也是保存在一个DLL中,MFC扩展DLL动态连接到MFC代码库的DLL,客户程序也必须要动态连接到MFC代码库的DLL。

(这里谈到的两个DLL,一个是我们自己编写的DLL,一个装MFC类库的DLL)现在MFC代码库的DLL也存在多个版本,客户程序和扩展DLL都必须使用相同版本的MFC代码DLL。

所以为了让MFC扩展DLL能很好的工作,扩展DLL和客户程序都必须动态连接到MFC代码库DLL。

MFC动态链接库的创建和调用(类和函数的dll导出和调用)

MFC动态链接库的创建和调用(类和函数的dll导出和调用)

1.新建MFC DLL工程,取名为:DLL0410
动态链接库的创建和调用(类,函数的DLL导出和调用)
2.在工程中编辑好DLL0410.h,DLL0410.cpp,DLL0410.def三个文件后编译生成对应的dll和lib文件
2.1 DLL0410.h
2.2 DLL0410.cpp
2.3 DLL0410.def
2.4 编辑好上面的3个文件编译后,用dumpbin命令查看是否有函数导出。

(如图所示,sub全局函数和add类的成员函数已经导出)
3.新建一个工程DLL0410test将生成的DLL0410.dll,DLL0410.lib以及DLL0410.h文件拷贝到 DLL0410test工程目录下
4.静态调用:在工程的DLL0410test.cpp文件中导入头文件DLL0410.h,并编写对应的静态调用代码。

同时也要在工程属性链接中加入DLL0410.lib文件。

(如果编译出错有可能是,工程属性中的常规>>字符集>>修改为 使用多字节字符集)
运行成功
5.4.动态调用:只需将生成的DLL0410.dll文件拷贝到新建工程目录下,直接在工程的
DLL0410test.cpp中编写动态调用代码即可。

不用做其他任何连接和导入头文件的操作。

运行成功。

C中DLL函数的导出和导入

C中DLL函数的导出和导入

1.使用DEF 文件从DLL 导出模块定义(.def) 文件是包含一个或多个描述DLL 各种属性的Module 语句的文本文件。

如果不使用__declspec(dllexport)关键字导出DLL 的函数,则DLL 需要 .def 文件。

.def 文件必须至少包含下列模块定义语句:文件中的第一个语句必须是LIBRARY 语句。

此语句将 .def 文件标识为属于DLL。

LIBRARY 语句的后面是DLL 的名称。

链接器将此名称放到DLL 的导入库中。

EXPORTS 语句列出名称,可能的话还会列出DLL 导出函数的序号值。

通过在函数名的后面加上@ 符和一个数字,给函数分配序号值。

当指定序号值时,序号值的范围必须是从1 到N,其中N 是DLL 导出函数的个数。

如果希望按序号导出函数,请参见按序号而不是按名称从DLL 导出函数以及本主题。

例如,包含实现二进制搜索树的代码的DLL 看上去可能像下面这样:LIBRARY BTREEEXPORTSInsert @1Delete @2Member @3Min @4如果使用MFC DLL 向导创建MFC DLL,则向导将为您创建主干 .def 文件并将其自动添加到项目中。

添加要导出到此文件的函数名。

对于非MFC DLL,必须亲自创建 .def 文件并将其添加到项目中。

如果导出C++ 文件中的函数,必须将修饰名放到 .def 文件中,或者通过使用外部“C”定义具有标准C 链接的导出函数。

如果需要将修饰名放到 .def文件中,则可以通过使用DUMPBIN 工具或/MAP 链接器选项来获取修饰名。

请注意,编译器产生的修饰名是编译器特定的。

如果将Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到DLL 的应用程序必须也是用相同版本的Visual C++ 生成的,这样调用应用程序中的修饰名才能与DLL 的 .def 文件中的导出名相匹配。

如果生成扩展DLL 并使用 .def 文件导出,则将下列代码放在包含导出类的头文件的开头和结尾:#undef AFX_DATA#define AFX_DATA AFX_EXT_DATAef 文件创建导出(.exp) 文件和导入库(.lib) 文件。

c语言导出函数

c语言导出函数

c语言导出函数C语言是一种广泛应用于系统编程、嵌入式开发和科学计算等领域的编程语言。

在C语言中,导出函数是一种非常重要的概念,它允许我们将函数声明为可在其他源文件中使用的公共接口。

本文将详细介绍C语言中导出函数的概念、用法和注意事项。

一、什么是导出函数在C语言中,导出函数即将函数声明为可供其他源文件调用的公共接口。

通过导出函数,我们可以将某个函数的实现代码封装在一个源文件中,并在其他源文件中通过函数声明来调用该函数。

导出函数的作用类似于其他编程语言中的类的公共方法或接口,它提供了一种模块化的编程方式,使得代码更易于维护和重用。

二、导出函数的用法要将一个函数声明为导出函数,我们需要在函数声明前加上关键字"extern"。

例如,下面是一个将函数add声明为导出函数的例子:extern int add(int a, int b);在上面的例子中,函数add被声明为一个返回类型为int、接受两个int类型参数的导出函数。

通过将函数声明为导出函数,我们可以在其他源文件中使用add函数的功能,而无需关心它的具体实现。

在使用导出函数时,我们需要注意以下几点:1. 在调用导出函数之前,我们需要包含包含该函数声明的头文件。

通常,我们会将函数声明放在一个单独的头文件中,并在需要使用该函数的源文件中通过#include指令引入该头文件。

2. 导出函数的实现代码应该放在一个独立的源文件中。

在编译时,我们需要将该源文件与调用该函数的源文件一起编译。

3. 导出函数的实现代码通常位于一个独立的源文件中,这样可以提高代码的可维护性和重用性。

通过将函数的实现代码与函数的声明分离,我们可以实现模块化的编程,使得代码更易于理解和修改。

4. 导出函数的命名应具有一定的规范性,以便其他开发人员能够清楚地理解函数的功能和用途。

三、导出函数的注意事项在使用导出函数时,我们需要注意以下几点:1. 导出函数的参数和返回值类型应与函数声明一致。

C#调用动态库,C_调用C++DLL

C#调用动态库,C_调用C++DLL

同上面一样,我们也举一个例子: int __stdcall FunctionName(unsigned char &param1, unsigned char *param2) 在 C#中对其进行调用的方法是: [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(ref byte param1, ref byte param2) 看到这,可能有人会问,&是取地址,*是传送指针,为何都只用 ref 就可以了呢?一种可能的解释是 ref 是一个具有重载特性的修饰符,会自动识别是取地址还是传送指针。 在实际的情况中,我们利用参数传递地址更多还是用在传送数组首地址上。 如:byte[] param1 = new param1(6); 在这里我们声明了一个数组,现在要将其的首地址传送过去,只要将 param1 数组的第一个元素用 ref 修 饰。具体如下: [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(ref byte param1[1], ref byte param2)
第四步,修改动态链接库实现,实现整数参数的输出: LIBEXPORT_API int mySum(int a,int b,int *c) { *c=a+b; return *c;
(此文详细解说了,C#调用 C++DLL 的参数传入,与传出,sting 类型的传出等) C#调用 C++编写的 COM DLL 封装库时会出现两个问题: 1. 数据类型转换问题 2. 指针或地址参数传送问题 首先是数据类型转换问题。因为 C#是.NET 语言,利用的是.NET 的基本数据类型,所以实际上是 将 C++的数据类型与.NET 的基本数据类型进行对应。 例如 C++的原有函数是: int __stdcall FunctionName(unsigned char param1, unsigned short param2) 其中的参数数据类型在 C#中,必须转为对应的数据类型。如: [DllImport(“ COM DLL path/file ”)] extern static int FunctionName(byte param1, ushort param2) 因为调用的是__stdcall 函数,所以使用了 P/Invoke 的调用方法。其中的方法 FunctionName 必须声明为 静态外部函数, 即加上 extern static 声明头。 我们可以看到,在调用的过程中, unsigned char 变为了 byte, unsigned short 变为了 ushort。变换后,参数的数据类型不变,只是声明方式必须改为.NET 语言的规范。 我们可以通过下表来进行这种转换: Win32 Types char, INT8, SBYTE, CHAR short, short int, INT16, SHORT int, long, long int, INT32, LONG32, BOOL , INT __int64, INT64, LONGLONG unsigned char, UINT8, UCHAR , BYTE unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT unsigned __int64, UINT64, DWORDLONG, ULONGLONG float, FLOAT double, long double, DOUBLE CLR Type System.SByte System.Int16 System.Int32 System.Int64 System.Byte Sys.UInt64 System.Single System.Double

VC DLL加载方法

VC DLL加载方法

调用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同。

Windows提供了两种将DLL映像到进程地址空间的方法:1. 隐式的加载时链接这种方法需要DLL工程经编译产生的LIB文件,此文件中包含了DLL允许应用程序调用的所有函数的列表,当链接器发现应用程序调用了LIB文件列出的某个函数,就会在应用程序的可执行文件的文件映像中加入一些信息,这些信息指出了包含这个函数的DLL文件的名字。

当这个应用程序运行时,也就是它的可执行文件被操作系统产生映像文件时,系统会查看这个映像文件中关于DLL的信息,然后将这个DLL文件映像到进程的地址空间。

系统通过DLL文件的名称,试图加载这个文件到进程地址空间时,它寻找DLL 文件的路径按照先后顺序如下:·程序运行时的目录,即可执行文件所在的目录;·当前程序工作目录·系统目录:对于Windows95/98来说,可以调用GetSystemDirectory函数来得到,对于WindowsNT/2000 来说,指的是32位Windows的系统目录,也可以调用GetSystemDirectory函数来得到,得到的值为SYSTEM32。

·Windows目录·列在PATH环境变量中的所有目录VC中加载DLL的LIB文件的方法有以下三种:①LIB文件直接加入到工程文件列表中在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中“Add Files to Project”菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。

②设置工程的 Project Settings来加载DLL的LIB文件打开工程的 Project Settings菜单,选中Link,然后在Object/library modules 下的文本框中输入DLL的LIB文件。

③通过程序代码的方式加入预编译指令#pragma comment (lib,”*.lib”),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件。

使用VS2015创建纯C的DLL及其调用

使用VS2015创建纯C的DLL及其调用

使用VS2015创建纯C的DLL及其调用
展开全文
1. 创建一个win32项目,如下图所示
2.确定后选择下一步,应用程序类型下勾选DLL(D)选项,附加选项去掉预编译头选项(如果忘记也可以在项目属性下去掉),如下图所示:
3.确定后默认创建如下项目:
右键项目---》生成
可以正常生成
4.删除所有默认生成的文件,如下:
右键项目---》生成,这时候是可以正常生成的。

如果提示缺少预编译头之类的错误,就是创建文件的时候加上的预编译头了,看第二步。

右键项目-->属性,如下设置即可解决该问题
5.添加自己的C源文件及头文件,注意,导出的函数可通过def文件导出,也可通过
在导出函数前添加_declspec(dllexport) 声明,如下:
#define API_DECL _declspec(dllexport)
extern API_DECL void add(int a, int b);
注意:在导出函数所以对应的源文件,需包含自身头文件,否则编译时只生成Dll,不会生成lib文件(头文件函数的声明需添加_declspec(dllexport))
编译成功后,我们需要生成的Dll及对应的lib、头文件这个三个。

6.创建Demo对生成的Dll的调用
对所创建的Demo工程属性设置如下:
a.链接器—> 常规—> 附加库目录添加lib文件所在路径
b.链接器—> 输入—> 附加依赖项添加对应Dll的lib文件名
正确设置之后,Demo中包含对应的头文件即可正常调用Dll中的函数。

DLL文件的生成和引用方法

DLL文件的生成和引用方法

一、动态链接库什么是动态链接库?DLL三个字母对于你来说一定很熟悉吧,它是Dynamic Link Library 的缩写形式,动态链接库(DLL) 是作为共享函数库的可执行文件。

动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。

函数的可执行代码位于一个DLL 中,该DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。

DLL 还有助于共享数据和资源。

多个应用程序可同时访问内存中单个DLL 副本的内容。

和大多数程序员一样,你一定很使用过DLL吧。

也曾感受到它的带给你程序设计和编码上的好错吧今天我想和大家探讨一个主题:如何在C#创建和调用DLL(动态链接库), 其实在很大意义上而讲,DLL让我更灵活的组织编写我们的应用程序,作为软件设计者,可一个根据它来达到很高的代码重用效果。

下面我来介绍一下在C#中如何创建和调用DLL。

二、准备工作我们需要对我们接下来要做的事情做个简单的介绍,在本文我们将利用C#语言创建一个名为MyDLL.DLL的动态链接库,在这个动态链接库文件中我们将提供两个功能一个是对两个参数交换他们的值,另一个功能是求两个参数的最大公约数。

然后创建一个应用程序使用这个DLL。

运行并输出结果。

1、MySwap.csnamespace MyMethods{public class SwapClass{public static bool Swap(ref long i,ref long j){i = i+j;j = i-j;i = i-j;return true;}}}2、MyMaxCD.csnamespace MyMethods{public class MaxCDClass{public static long MaxCD(long i, long j){long a,b,temp;if(i>j){a = i;b = j;}else{b = i;a = j;}temp = a % b;while(temp!=0){a = b;b = temp;temp = a % b;}return b;}}}需要注意的是:我们在制作这两个文件的时候可以用Visual 或者其他的文本编辑器,就算是记事本也可以。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.使用DEF 文件从DLL 导出模块定义(.def) 文件是包含一个或多个描述DLL 各种属性的Module 语句的文本文件。

如果不使用__declspec(dllexport)关键字导出DLL 的函数,则DLL 需要 .def 文件。

.def 文件必须至少包含下列模块定义语句:文件中的第一个语句必须是LIBRARY 语句。

此语句将 .def 文件标识为属于DLL。

LIBRARY 语句的后面是DLL 的名称。

链接器将此名称放到DLL 的导入库中。

EXPORTS 语句列出名称,可能的话还会列出DLL 导出函数的序号值。

通过在函数名的后面加上@ 符和一个数字,给函数分配序号值。

当指定序号值时,序号值的范围必须是从1 到N,其中N 是DLL 导出函数的个数。

如果希望按序号导出函数,请参见按序号而不是按名称从DLL 导出函数以及本主题。

例如,包含实现二进制搜索树的代码的DLL 看上去可能像下面这样:LIBRARY BTREEEXPORTSInsert @1Delete @2Member @3Min @4如果使用MFC DLL 向导创建MFC DLL,则向导将为您创建主干 .def 文件并将其自动添加到项目中。

添加要导出到此文件的函数名。

对于非MFC DLL,必须亲自创建 .def 文件并将其添加到项目中。

如果导出C++ 文件中的函数,必须将修饰名放到 .def 文件中,或者通过使用外部“C”定义具有标准C 链接的导出函数。

如果需要将修饰名放到 .def文件中,则可以通过使用DUMPBIN 工具或/MAP 链接器选项来获取修饰名。

请注意,编译器产生的修饰名是编译器特定的。

如果将Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到DLL 的应用程序必须也是用相同版本的Visual C++ 生成的,这样调用应用程序中的修饰名才能与DLL 的 .def 文件中的导出名相匹配。

如果生成扩展DLL 并使用 .def 文件导出,则将下列代码放在包含导出类的头文件的开头和结尾:#undef AFX_DATA#define AFX_DATA AFX_EXT_DATA// <body of your header file>#undef AFX_DATA#define AFX_DATA这些代码行确保内部使用的MFC 变量或添加到类的变量是从扩展DLL 导出(或导入)的。

例如,当使用DECLARE_DYNAMIC派生类时,该宏扩展以将CRuntimeClass成员变量添加到类。

省去这四行代码可能会导致不能正确编译或链接DLL,或在客户端应用程序链接到DLL 时导致错误。

当生成DLL 时,链接器使用 .def 文件创建导出(.exp) 文件和导入库(.lib) 文件。

然后,链接器使用导出文件生成DLL 文件。

隐式链接到DLL 的可执行文件在生成时链接到导入库。

请注意,MFC 本身使用 .def 文件从MFCx0.dll 导出函数和类。

2.使用_declspec(dllexport) 从DLL 导出Microsoft 在Visual C++ 的16 位编译器版本中引入了_export,使编译器得以自动生成导出名并将它们放到一个 .lib 文件中。

然后,此 .lib 文件就可以像静态 .lib 那样用于与DLL 链接。

在更新的编译器版本中,可以使用_declspec(dllexport)关键字从DLL 导出数据、函数、类或类成员函数。

_declspec(dllexport)会将导出指令添加到对象文件中,因此您不需要使用 .def 文件。

当试图导出C++ 修饰函数名时,这种便利最明显。

由于对名称修饰没有标准规范,因此导出函数的名称在不同的编译器版本中可能有所变化。

如果使用_declspec(dllexport),仅当解决任何命名约定更改时才必须重新编译DLL 和依赖 .exe 文件。

许多导出指令(如序号、NONAME 和PRIVATE)只能在 .def 文件中创建,并且必须使用 .def 文件来指定这些属性。

不过,在 .def 文件的基础上另外使用_declspec(dllexport)不会导致生成错误。

若要导出函数,_declspec(dllexport)关键字必须出现在调用约定关键字的左边(如果指定了关键字)。

例如:_declspec(dllexport) void _cdecl Function1(void);若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,如下所示:class _declspec(dllexport) CExampleExport : public CObject{ ... class definition ... };生成DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将_declspec(dllexport)添加到头文件中的声明中。

若要提高代码的可读性,请为_declspec(dllexport)定义一个宏并对正在导出的每个符号使用该宏:#define DllExport _declspec( dllexport )_declspec(dllexport)将函数名存储在DLL 的导出表中。

如果希望优化表的大小,请参见按序号而不是按名称从DLL 导出函数。

注意:将DLL 源代码从Win16 移植到Win32 时,请用_declspec(dllexport)替换_export的每个实例。

作为参考,请在Win32 Winbase.h 头文件中搜索。

它包含_declspec(dllimport)的用法示例。

3.使用AFX_EXT_CLASS 导出和导入扩展DLL 使用AFX_EXT_CLASS宏导出类;链接到扩展DLL 的可执行文件使用该宏导入类。

用于生成扩展DLL 的相同头文件可通过AFX_EXT_CLASS宏与链接到DLL 的可执行文件一起使用。

在DLL 的头文件中,将AFX_EXT_CLASS关键字添加到类的声明中,如下所示:class AFX_EXT_CLASS CMyClass : public CDocument{// <body of class>};当定义了预处理器符号_AFXDLL和_AFXEXT时,该宏被MFC 定义为__declspec(dllexport)。

但当定义了_AFXDLL而未定义_AFXEXT时,该宏被定义为__declspec(dllimport)。

定义后,预处理器符号_AFXDLL指示共享MFC 版本正在由目标可执行文件(DLL 或应用程序)使用。

当_AFXDLL 和_AFXEXT都定义了时,这指示目标可执行文件是扩展DLL。

由于从扩展DLL 导出时,AFX_EXT_CLASS被定义为__declspec(dllexport),因此可以导出整个类,而不必将该类的所有符号的修饰名放到 .def 文件中。

此方法由MFC 示例DLLHUSK使用。

虽然使用此方法可以避免创建 .def 文件和类的所有修饰名,但由于名称可以按序号导出,因此创建 .def 文件的效率更高。

若要使用 .def 文件导出方法,请将下列代码放在头文件的开头和结尾处:#undef AFX_DATA#define AFX_DATA AFX_EXT_DATA// <body of your header file>#undef AFX_DATA#define AFX_DATA警告:导出内联函数时要小心,因为它们有可能导致版本冲突。

内联函数扩展到应用程序代码中;因此,如果以后重写内联函数,除非重新编译应用程序本身,否则内联函数不会被更新。

通常,不用重新生成使用DLL 函数的应用程序就可以更新DLL 函数。

4.导出类中的个别成员有时,您可能希望导出类中的个别成员。

例如,如果导出CDialog派生类,可能只需要导出构造函数和DoModal调用。

可以对需要导出的个别成员使用AFX_EXT_CLASS。

例如:class CExampleDialog : public CDialog{public:AFX_EXT_CLASS CExampleDialog();AFX_EXT_CLASS int DoModal();...// rest of class definition...};您不再导出类的所有成员,但由于MFC 宏的工作方式,您可能会遇到其他问题。

几个MFC 的Helper 宏实际声明或定义数据成员。

因此,还必须从DLL 导出这些数据成员。

例如,当生成扩展DLL 时,DECLARE_DYNAMIC宏的定义如下:#define DECLARE_DYNAMIC(class_name)protected:static CRuntimeClass* PASCAL _GetBaseClass();public:static AFX_DATA CRuntimeClass class##class_name;virtual CRuntimeClass* GetRuntimeClass() const;以static AFX_DATA打头的行声明类的内部静态对象。

若要正确导出该类并从客户端可执行文件访问运行时信息,必须导出此静态对象。

由于静态对象是用AFX_DATA修饰符声明的,因此只需在生成DLL 时将AFX_DATA定义为__declspec(dllexport),并在生成客户端可执行文件时将AFX_DATA 定义为__declspec(dllimport)。

由于已经以此方式定义了AFX_EXT_CLASS,因此只需参考类定义,将AFX_DATA重定义为与AFX_EXT_CLASS相同。

例如:#undef AFX_DATA#define AFX_DATA AFX_EXT_CLASSclass CExampleView : public CView{DECLARE_DYNAMIC()// ... class definition ...};#undef AFX_DATA#define AFX_DATAMFC 总是在其宏的内部定义的数据项上使用AFX_DATA符号,因此此技术适用于所有这类情况。

例如,它适用于DECLARE_MESSAGE_MAP。

注意:如果导出整个类而非选定的类成员,静态数据成员将自动导出。

5.使用 .DEF 文件的优缺点在 .def 文件中导出函数使您得以控制导出序号。

当将附加的导出函数添加到DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。

当您进行此操作时,使用隐式链接的应用程序不必与包含新函数的新导入库重新链接。

这非常重要,例如,在设计将由许多应用程序使用的第三方DLL 时。

相关文档
最新文档