博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
libCEF总结01下载、编译、入门
阅读量:5157 次
发布时间:2019-06-13

本文共 13852 字,大约阅读时间需要 46 分钟。

 

 

 

1 下载

1.1 下载

下载网址为:http://www.magpcss.net/cef_downloads/,显示如下图所示:

1.1

下载后的文件名如下所示:

cef_binary_3.2785.1466.g80e473e_windows32.tar.tar

cef_binary_3.2785.1466.g80e473e_windows64.tar.tar

1.2 合并

解压上一节的两个压缩包,并使用 Beyond Compare 进行二进制比较,如下图所示:

1.2

可见:只有 DebugReleaseResources 三个文件夹中的某些文件存在差异。所以,可将32位的DebugReleaseResources重命名为Debug32Release32Resources3264位的DebugReleaseResources重命名为Debug64Release64Resources64。然后,将两个文件夹合并。

笔者将两个文件夹合并到了W:\libCEF\v3.2785.1466\unzip,其目录结构如下图所示:

1.3

 

 

2 cmake

2.1 编译简介

这里的编译主要指的是 libcef_dll_wrapper 的编译。它是libcef.dll的包装工程,客户端程序通过它可以间接的访问libcef.dll

libCEF较早的版本(如3.2171.1979),包含有VC++项目文件(*.sln),可直接打开进行编译。目前的版本不再包含这些*.sln文件,使用VC++编译前可使用cmake程序生成*.sln文件。

2.2 下载cmake

cmake的下载网址为:https://cmake.org/download/。显示如下图所示:

2.1

说明如下:

cmake-3.6.1.zip                cmake的源代码

cmake-3.6.1-win32-x86.msi        安装包(32 )

cmake-3.6.1-win32-x86.zip        直接运行(32 )

cmake-3.6.1-win64-x64.msi        安装包(64 )

cmake-3.6.1-win64-x64.zip        直接运行(64 )

这里,下载 cmake-3.6.1-win32-x86.zip 即可。

2.3 运行cmake

解压cmake-3.6.1-win32-x86.zip,运行cmake-gui.exe,将显示如下界面:

2.2

"Where is the source code:"右边的文本框内请输入图1.3CMakeLists.txt所在的目录,即W:\libCEF\v3.2785.1466\unzip

"Where to build the binaries:"右边的文本框内请输入一个目录,cmake将在这个目录下生成VC++项目文件(*.sln;*.vcproj;*.vcxproj)。这个目录可以和"Where is the source code:"的设置相同。

单击上图的"Configure"按钮,将弹出上图右下角的界面。在这个界面里选择VC++编译器,如:使用vc2010编译64位程序,请选择"Visual Studio 10 2010 Win64"。这里选择Visual Studio 10 2010,然后单击"Finish"按钮。

单击上图的"Generate"按钮,cmake将在W:/libCEF/v3.2785.1466/make/vc2010里生成vc2010的项目文件cef.slnALL_BUILD.vcxproj……如下图所示:

2.3

使用Visual Studio 2010打开上图的cef.sln,可以发现有五个项目。如下图所示:

2.4

libcef_dll_wrapper        libcef.dll的包装,是编译的重点

cefclient                一个调用libcef的示例程序,较复杂

cefsimple                一个调用libcef的示例程序,较简单

ZERO_CHECK            再次运行cmake程序,检查哪些文件被修改了

ALL_BUILD            编译它,则ZERO_CHECKlibcef_dll_wrapper

                        cefclientcefsimple将依次被编译

 

3 编译进阶

本章将说明如何在不使用cmake的情况下编译libcef_dll_wrapper

3.1 创建项目

创建VC++静态库项目libcef_dll_wrapper。以VC++2010为例,创建项目时首先选择"Win32 Project",如下图所示:

3.1

接下来的设置如下图所示:即创建一个静态库,不使用预编译头文件。

3.2

3.2 添加源文件

将图1.3文件夹libcef_dll及其子文件夹下的所有源文件(*.cc)添加到上一节创建的VC++项目libcef_dll_wrapper里。

3.2.1 下载vcHelper

手动添加所有的源文件比较繁琐,可借助工具vcHelper来完成此项工作。其下载方法为:

1、进入百度网盘 http://pan.baidu.com/s/1gd7XDkf

2、进入目录 public\Tools\vcHelper

3、下载vcHelper的最新版本。

3.2.2 使用vcHelper

运行vcHelper,进入"源文件树"页面,如下图所示:

3.3

接下来的操作步骤为:

1、拖放文件夹libcef_dll至"生成文件树"内的文本框内,文件夹libcef_dll的全路径名(上图中的W:\libCEF\v3.2785.1466\unzip\libcef_dll)即被自动添加到该文本框内。当然,也可手工输入或粘贴这个全路径名;

2、单击"生成文件树"按钮。根据上图可知:vcHelper找到了263个文件;

3、拖放VC++项目libcef_dll_wrapper所在文件夹(W:\libCEF\v3.2785.1466\libcef_dll_wrapper)至"替换文件树"内的文本框内。vcHelper将在该文件夹内查找所有的VC++工程文件(*.vcproj*.vcxproj),并将其全路径名添加到相应的文本框内;

4、单击"替换文件树"按钮,文件树(263个文件)将被添加到VC++工程文件里。

打开项目libcef_dll_wrapper,可以看到源文件树。如下图所示:

3.4

3.3 配置VC++项目

3.3.1 不使用预编译头文件

具体的配置如下图所示:

3.5

3.3.2 增加宏定义

增加宏定义:USING_CEF_SHARED=1NOMINMAX,如下图所示:

3.6

USING_CEF_SHARED 有两个作用:

1、调用libcef.dll里的函数时,会使用__declspec(dllimport),详见include\internal\cef_export.h文件;

2、编译时防止有效代码被屏蔽,如文件libcef_dll\cpptoc\views\browser_view_delegate_cpptoc.h里有如下代码:

#ifndef USING_CEF_SHARED

#pragma message("Warning: "__FILE__" may be accessed wrapper-side only")

#else // USING_CEF_SHARED

class CefBrowserViewDelegateCppToC

: public CefCppToC<CefBrowserViewDelegateCppToC, CefBrowserViewDelegate,

cef_browser_view_delegate_t> {

public:

CefBrowserViewDelegateCppToC();

};

#endif

不定义宏USING_CEF_SHARED,则有效代码(蓝色部分)将被屏蔽掉。

NOMINMAX的作用:取消宏minmax的定义,防止它们与std::minstd::max冲突。以下内容节选自windef.h

#ifndef NOMINMAX

 

#ifndef max

#define max(a,b) (((a) > (b)) ? (a) : (b))

#endif

 

#ifndef min

#define min(a,b) (((a) < (b)) ? (a) : (b))

#endif

 

#endif /* NOMINMAX */

亦即:定义宏NOMINMAX之后,宏minmax将不会被定义。

3.3.3 设置头文件查找目录

编译libcef_dll_wrapper时,需要用到W:\libCEF\v3.2785.1466\unzip\include目录下的头文件。为此,需要设置W:\libCEF\v3.2785.1466\unzip为头文件查找目录。具体设置如下图所示:

3.7

上图中的../../../unzipW:\libCEF\v3.2785.1466\unzip相对于文件W:\libCEF\v3.2785.1466\libcef_dll_wrapper\make-libT\vc2010\libcef_dll_wrapperT.vcxproj的相对路径。可用vcHelper获得这个相对路径,如下图所示:

3.8

输入"基准目录/文件"和"绝对路径",单击按钮">>"即可获得相对路径。

3.4 其它

3.4.1 使用VC++2008编译

libcef_dll_wrapper默认情况下至少需要VC++2010才能编译通过。使用VC++2008编译时,会提示无法找到stdint.h,为此特做如下修改:

include目录下新建文件stdint.h,其内容如下:

#pragma once

#if _MSC_VER >= 1600 //VC++2010 ~ VC++2015

#include <stdint.h>

#else

typedef signed char int8_t;

typedef short int16_t;

typedef int int32_t;

typedef unsigned char uint8_t;

typedef unsigned short uint16_t;

typedef unsigned int uint32_t;

typedef signed char int_least8_t;

typedef short int_least16_t;

typedef int int_least32_t;

typedef unsigned char uint_least8_t;

typedef unsigned short uint_least16_t;

typedef unsigned int uint_least32_t;

typedef char int_fast8_t;

typedef int int_fast16_t;

typedef int int_fast32_t;

typedef unsigned char uint_fast8_t;

typedef unsigned int uint_fast16_t;

typedef unsigned int uint_fast32_t;

#ifdef _WIN64

typedef __int64 intptr_t;

#else /* _WIN64 */

typedef int intptr_t;

#endif /* _WIN64 */

#endif

亦即:VC++2010及其以上版本直接包含自带的stdint.h头文件,否则就自定义int8_tint16_t……

接下来,修改源代码中的#include <stdint.h>#include "../../include/stdint.h"。注意双引号内的相对路径不要弄错。

3.4.2 修改#include语句

如图3.7所示,使用libCEF的客户端项目,必须设置libCEF的头文件查找目录,否则将无法正常编译。

W:\libCEF\v3.2785.1466\unzip\libcef_dll\wrapper\libcef_dll_wrapper.cc为例进行说明。该文件包含如下代码:

#include "include/cef_app.h"

它其实想包含的是W:\libCEF\v3.2785.1466\unzip\include\cef_app.h。为此,需要设置W:\libCEF\v3.2785.1466\unzip为头文件查找目录,这样编译器在编译时就能顺利找到文件include/cef_app.h了。

现在,如果把#include语句修改一下,如下所示:

#include "../../include/cef_app.h"

编译W:\libCEF\v3.2785.1466\unzip\libcef_dll\wrapper\libcef_dll_wrapper.cc时,编译器会使用头文件W:\libCEF\v3.2785.1466\unzip\libcef_dll\wrapper\..\..\include\cef_app.h,即W:\libCEF\v3.2785.1466\unzip\include\cef_app.h。这样,不用设置头文件查找目录,编译器也能找到头文件cef_app.h了。

也就是说:#include语句中的包含路径全部更改为相对路径,就不再需要设置头文件查找目录了

#include语句那么多,手工逐条修改将是一项浩大的工程。可借助vcHelper完成此项工作。如下图所示:

3.9

具体操作步骤如下:

1、设置"源文件目录"和"引用目录"为W:\libCEF\v3.2785.1466\unzip

2、勾中"修改#include""语句"复选框,不要勾中"修改#include<>语句"复选框,不要勾中"修改#include里的扩展名"

3、单击"扫描"按钮;

4、单击"自动修改"按钮,vcHelper将自动修改#include""语句,并将无法修改的语句显示到右侧的列表中;

5、右侧列表中,单击"数量"列标题,整个列表将按数量进行排序。数量2表示包含文件(ef_callback.hresource.h)有两个。此时鼠标左键双击该行,将显示如下界面:

3.10

单击按钮"UltraEdit",vcHelper将调用UltraEdit,并打开文件urlrequest_test.cc,然后跳转至第10行。如下图所示:

3.11

根据上图可知,urlrequest_test.cc需要的是base目录下的cef_callback.h。因此应该选择图3.10中的第一条修改建议。鼠标左键双击此修改建议,然后粘贴到上图的第10行,即可完成此行语句的修改。

注意:如果vcHelper无法正常调用UltraEdit,请在vcHelperConfig.txt里配置UltraEdit的全路径名,如下图所示:

3.12

 

 

4 入门示例

4.1 简介

本章参考了开源项目CEF3SimpleSample,其下载网址为:

https://github.com/acristoffers/CEF3SimpleSample

CEF3SimpleSample是一个SDK程序,本章对其进行精简,并使用了MFC。最终实现了如下图所示的效果——运行后将显示新浪网的首页。

4.1

4.2 创建项目

创建一个MFC的对话框程序mfcCEF。创建项目时的配置请见下图:

4.2

4.3 编码

4.3.1 修改stdafx.h

stdafx.h的最后增加如下代码

#define STR(x) #x

#define STR2(x) STR(x)

#define INCLUDE(f) STR2(PATH(f))

//包含 libCEF 的头文件

//下面的宏定义,##之前是 libCEF include 目录相对于本文件的相对路径

#define PATH(f) ../../libCEF/v3.2785.1466/unzip/include/##f

#define USING_CEF_SHARED 1

#include INCLUDE(cef_app.h)

#include INCLUDE(cef_client.h)

#undef PATH

 

#include "cefSimple.h"

 

//下面的宏定义是 libCEF 库文件相对于 vc 项目文件(*.vcproj/*.vcxproj)的相对路径

#define PATH "../../libCEF/v3.2785.1466/"

//连接库文件 libcef.lib

#ifdef _WIN64

#ifdef _DEBUG

#define PATH1 "unzip/Debug64/"

#else

#define PATH1 "unzip/Release64/"

#endif

#else

#ifdef _DEBUG

#define PATH1 "unzip/Debug32/"

#else

#define PATH1 "unzip/Release32/"

#endif

#endif

#pragma comment(lib,PATH PATH1 "libcef.lib")

#undef PATH1

//连接库文件 libcef_dll_wrapper.lib

#define PATH1 "libcef_dll_wrapper/bin/"

#if _MSC_VER==1500 //VC++9.0(VC2008)

#define PATH2 "vc2008"

#elif _MSC_VER==1600 //VC++10.0(VC2010)

#define PATH2 "vc2010"

#elif _MSC_VER==1700 //VC++11.0(VC2012)

#define PATH2 "vc2012"

#elif _MSC_VER==1800 //VC++12.0(VC2013)

#define PATH2 "vc2013"

#elif _MSC_VER==1900 //VC++14.0(VC2015)

#define PATH2 "vc2015"

#else

#error 未知的 VC++ 编译器

#endif

#ifdef _WIN64

#define PATH3 "-x64"

#else

#define PATH3 "-Win32"

#endif

#ifdef _DEBUG

#ifdef _UNICODE

#define PATH4 "-DU/"

#else

#define PATH4 "-DA/"

#endif

#else

#ifdef _UNICODE

#define PATH4 "-RU/"

#else

#define PATH4 "-RA/"

#endif

#endif

#ifdef _MT

#ifdef _DLL //使用多线程 DLL 版的 C 函数库

#define PATH5 "libcef_dll_wrapperD.lib"

#else //使用多线程版的 C 函数库

#define PATH5 "libcef_dll_wrapperT.lib"

#endif

#else //使用单线程版的 C 函数库

#define PATH5 "libcef_dll_wrapperS.lib"

#endif

#pragma comment(lib,PATH PATH1 PATH2 PATH3 PATH4 PATH5)

#undef PATH1

#undef PATH2

#undef PATH3

#undef PATH4

#undef PATH5

#undef PATH

 

#undef STR

#undef STR2

#undef INCLUDE

说明:

1#define USING_CEF_SHARED 1 表示导入libcef.dll的导出函数;

2VC++编译预处理器对#include INCLUDE(cef_app.h)的预处理:

首先变为 #include STR2(PATH(cef_app.h))

然后变为 #include STR(../../libCEF/v3.2785.1466/unzip/include/cef_app.h)

最后变为 #include "../../libCEF/v3.2785.1466/unzip/include/cef_app.h"

"#define PATH(f) ../../libCEF/v3.2785.1466/unzip/include/##f"中的../../libCEF/v3.2785.1466/unzip/include/libCEF include 目录(W:\libCEF\v3.2785.1466\unzip\include)相对于本文件(W:\VC\mfcCEF\stdafx.h)的相对目录。请根据实际情况修改这个相对路径。

注意:若要使用相对路径包含libCEF头文件,请按照3.4.2节的说明把libCEF源代码里的#include语句都修改掉。

3、如果使用vc2010编译器,编译Debug版,则#pragma comment(lib,PATH PATH1 "libcef.lib")将被展开为:#pragma comment(lib,"../../libCEF/v3.2785.1466/" "unzip/Debug32/" "libcef.lib")也就是#pragma comment(lib,"../../libCEF/v3.2785.1466/unzip/Debug32/libcef.lib")

../../libCEF/v3.2785.1466/unzip/Debug32/libcef.lib是库文件(W:\libCEF\v3.2785.1466\unzip\Debug32\libcef.lib)相对于vc项目文件(W:\VC\mfcCEF\mfcCEF.vcxproj)的相对路径。请根据实际情况修改"#define PATH "../../libCEF/v3.2785.1466/""中的相对路径。

4libcef_dll_wrapper库文件

笔者的电脑上libcef_dll_wrapper库文件的路径为:

W:\libCEF\v3.2785.1466\libcef_dll_wrapper\bin\vc2010-Win32-RU

vc2010还有可能是vc2012/vc2013/vc2015

Win32还有可能是x64

RURelease Unicode)还有可能是RARelease Ansi)、DADebug Ansi)、DUDebug Unicode

库文件名有两种:

libcef_dll_wrapperT.lib        使用的C函数库是多线程版

libcef_dll_wrapperD.lib        使用的C函数库是多线程DLL

如果客户端程序Use MFC in a Static Library,就会自动连接libcef_dll_wrapperT.lib;如果客户端程序Use MFC in a Shared DLL,就会自动连接libcef_dll_wrapperD.lib

4.3.2 增加cefSimple.h

给项目增加头文件cefSimple.h,其内容如下所示:

#pragma once

 

class SimpleApp : public CefApp

{

private:

IMPLEMENT_REFCOUNTING(SimpleApp);

};

 

class SimpleHandler : public CefClient

, public CefLifeSpanHandler

{

public:

SimpleHandler()

{

m_hWndBrowser = NULL;

}

public://CefClient methods:

virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler()

OVERRIDE { return this; }

public://CefLifeSpanHandler methods:

virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser)

OVERRIDE {

m_hWndBrowser = browser->GetHost()->GetWindowHandle();

}

public:

HWND m_hWndBrowser;

IMPLEMENT_REFCOUNTING(SimpleHandler);

};

4.3.3 修改mfcCEF.h

修改后的内容如下

#pragma once

 

#ifndef __AFXWIN_H__

    #error "在包含此文件之前包含"stdafx.h"以生成 PCH 文件"

#endif

 

#include "resource.h"

 

class CmfcCEFApp : public CWinApp

{

public:

    CmfcCEFApp();

public:

    virtual BOOL InitInstance();

    DECLARE_MESSAGE_MAP()

};

 

extern CmfcCEFApp theApp;

4.3.4 修改mfcCEF.cpp

修改后的内容如下

#include "stdafx.h"

#include "mfcCEF.h"

#include "mfcCEFDlg.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

 

BEGIN_MESSAGE_MAP(CmfcCEFApp, CWinApp)

END_MESSAGE_MAP()

 

CmfcCEFApp::CmfcCEFApp()

{

}

 

CmfcCEFApp theApp;

 

BOOL CmfcCEFApp::InitInstance()

{

CWinApp::InitInstance();

_tsetlocale(LC_ALL,_T(""));

{

//CEF 初始化

CefMainArgs ma(m_hInstance);

CefRefPtr<SimpleApp> app(new SimpleApp());

int nCEP = CefExecuteProcess(ma,app.get(),NULL);

if(nCEP >= 0) { exit(nCEP); }

CefSettings settings;

CefString(&settings.locale) = L"zh-CN";

settings.no_sandbox = 1;

settings.multi_threaded_message_loop = 1;

CefInitialize(ma, settings, app.get(),NULL);

}

{

CmfcCEFDlg dlg;

m_pMainWnd = &dlg;

dlg.DoModal();

}

CefShutdown(); //退出 CEF

return FALSE;

}

4.3.5 修改mfcCEFDlg.h

修改后的内容如下

#pragma once

 

class CmfcCEFDlg : public CDialog

{

public:

    CmfcCEFDlg(CWnd* pParent = NULL);

    enum { IDD = IDD_MFCCEF_DIALOG };

protected:

    virtual void DoDataExchange(CDataExchange* pDX);

protected:

    virtual BOOL OnInitDialog();

afx_msg void OnSize(UINT nType, int cx, int cy);

    DECLARE_MESSAGE_MAP()

protected:

CefRefPtr<SimpleHandler> m_Handler;

};

4.3.6 修改mfcCEFDlg.cpp

修改后的内容如下

#include "stdafx.h"

#include "mfcCEF.h"

#include "mfcCEFDlg.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

 

CmfcCEFDlg::CmfcCEFDlg(CWnd* pParent /*=NULL*/)

: CDialog(CmfcCEFDlg::IDD, pParent)

,m_Handler(new SimpleHandler())

{

}

 

void CmfcCEFDlg::DoDataExchange(CDataExchange* pDX)

{

    CDialog::DoDataExchange(pDX);

}

 

BEGIN_MESSAGE_MAP(CmfcCEFDlg, CDialog)

ON_WM_SIZE()

END_MESSAGE_MAP()

 

BOOL CmfcCEFDlg::OnInitDialog()

{

    CDialog::OnInitDialog();

CefWindowInfo wi;

RECT rc;

GetClientRect(&rc);

wi.SetAsChild(m_hWnd,rc);

CefBrowserSettings bs;

CefBrowserHost::CreateBrowser(wi,m_Handler.get()

,L"www.sina.com",bs,NULL);

return TRUE;

}

 

void CmfcCEFDlg::OnSize(UINT nType, int cx, int cy)

{

CDialog::OnSize(nType, cx, cy);

HWND hWnd = m_Handler->m_hWndBrowser;

if(hWnd)

{

RECT rc;

GetClientRect(&rc);

::MoveWindow(hWnd,0,0,rc.right,rc.bottom,FALSE);

Invalidate();

}

}

4.4 运行

编译mfcCEF成功后,先不要急着运行程序。因为mfcCEF.exe运行时需要一些文件,需要把它们复制到mfcCEF.exe所在目录。

假如mfcCEF.exe32位的Release版,则

1、复制图1.3Release32文件夹内的所有文件到mfcCEF.exe所在目录。注意:*.lib259M,运行时不需要这些文件,所以就不用复制了。

2、复制图1.3Resources32文件夹内的所有文件到mfcCEF.exe所在目录。

文件复制完成后,mfcCEF.exe所在目录如下图所示:

4.3

现在,就可以运行mfcCEF.exe了。

4.5 程序运行逻辑

运行mfcCEF,代码的执行顺序如下:

1、执行 CmfcCEFApp::InitInstance,调用 CefInitialize 初始化 CEF

2CmfcCEFApp::InitInstance 里的 dlg.DoModal() 显示程序主界面;

3、程序主界面被创建时,执行 CmfcCEFDlg::OnInitDialog,调用 CefBrowserHost::CreateBrowser 创建浏览器窗口;

4、浏览器窗口创建完毕后,执行 SimpleHandler::OnAfterCreated,获得浏览器窗口的句柄 m_hWndBrowser

5、浏览器窗口是程序主界面窗口的子窗口。调整主界面大小时,将执行 CmfcCEFDlg::OnSizeOnSize 函数里移动浏览器窗口(句柄为 m_hWndBrowser)使其占满主界面窗口的客户区;

6、用户退出主界面,CmfcCEFApp::InitInstance 里的 dlg.DoModal() 将返回。同时对象 dlg 将被析构,其成员变量m_Handler 也将被析构。CefRefPtr<SimpleHandler> 使用了计数功能,m_Handler 析构时计数值减一后等于零,new SimpleHandler() 创建的对象将被自动 delete

7CmfcCEFApp::InitInstance 里的 CefShutdown() 被执行,退出 CEF

8CmfcCEFApp::InitInstance 返回 FALSE,整个程序不进入消息循环,而是结束。

下面说明一下对象new SimpleApp()的生命周期:

1CefRefPtr<SimpleApp> app(new SimpleApp()) 后对象的计数值为 1

2CefInitialize(ma, settings, app.get(),NULL) 后对象的计数值为2

3app 析构后对象的计数值为 1

4CefShutdown() 后,app 的计数值为 0,对象自动被 delete

转载于:https://www.cnblogs.com/hanford/p/6168743.html

你可能感兴趣的文章
LeetCode 159. Longest Substring with At Most Two Distinct Characters
查看>>
LeetCode Ones and Zeroes
查看>>
基本算法概论
查看>>
jquery动态移除/增加onclick属性详解
查看>>
JavaScript---Promise
查看>>
暖暖的感动
查看>>
Java中的日期和时间
查看>>
Django基于admin的stark组件创建(一)
查看>>
PAT L2-016 愿天下有情人都是失散多年的兄妹
查看>>
抛弃IIS,利用FastCGI让Asp.net与Nginx在一起
查看>>
C. Tanya and Toys_模拟
查看>>
springboot jar包运行中获取资源文件
查看>>
基于FPGA实现的高速串行交换模块实现方法研究
查看>>
Java Scala获取所有注解的类信息
查看>>
delphi ,安装插件
查看>>
case when then的用法-leetcode交换工资
查看>>
11.28.cookie
查看>>
BeanShell简介
查看>>
python字符串操作
查看>>
MySQL学习之备份
查看>>