添加 so 共享库、Windows 应用程序入口、Windows 动态链接库

This commit is contained in:
huihut 2018-08-14 19:47:53 +08:00
parent d8eae21635
commit ce874b3123

384
README.md
View File

@ -2594,41 +2594,377 @@ Linux 下的共享库就是普通的 ELF 共享对象。
* `LD_PRELOAD`:指定预先装载的一些共享库甚至是目标文件
* `LD_DEBUG`:打开动态链接器的调试功能
### Windows 的动态链接库Dynamic-Link Library
#### so 共享库的编写
<details><summary>Windows 动态链接库例子</summary>
<details><summary>使用 CLion 编写共享库</summary>
创建一个名为 MySharedLib 的共享库
CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.10)
project(MySharedLib)
set(CMAKE_CXX_STANDARD 11)
add_library(MySharedLib SHARED library.cpp library.h)
```
library.h
DLL 头文件
```cpp
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MYSHAREDLIB_LIBRARY_H
#define MYSHAREDLIB_LIBRARY_H
#ifdef _WIN32
# ifdef MODULE_API_EXPORTS
# define MODULE_API __declspec(dllexport)
# else
# define MODULE_API __declspec(dllimport)
# endif
#else
# define MODULE_API
#endif
// 打印 Hello World!
void hello();
MODULE_API int module_init();
#ifdef __cplusplus
// 使用可变模版参数求和
template <typename T>
T sum(T t)
{
return t;
}
template <typename T, typename ...Types>
T sum(T first, Types ... rest)
{
return first + sum<T>(rest...);
}
#endif
```
DLL 源文件
```cpp
#define MODULE_API_EXPORTS
#include "module.h"
library.cpp
MODULE_API int module_init()
```cpp
#include <iostream>
#include "library.h"
void hello() {
std::cout << "Hello, World!" << std::endl;
}
```
</details>
#### so 共享库的使用(被可执行项目调用)
<details><summary>使用 CLion 调用共享库</summary>
创建一个名为 TestSharedLib 的可执行项目
CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.10)
project(TestSharedLib)
# C++11 编译
set(CMAKE_CXX_STANDARD 11)
# 头文件路径
set(INC_DIR /home/xx/code/clion/MySharedLib)
# 库文件路径
set(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug)
include_directories(${INC_DIR})
link_directories(${LIB_DIR})
link_libraries(MySharedLib)
add_executable(TestSharedLib main.cpp)
# 链接 MySharedLib 库
target_link_libraries(TestSharedLib MySharedLib)
```
main.cpp
```cpp
#include <iostream>
#include "library.h"
using std::cout;
using std::endl;
int main() {
hello();
cout << "1 + 2 = " << sum(1,2) << endl;
cout << "1 + 2 + 3 = " << sum(1,2,3) << endl;
return 0;
}
```
执行结果
```
Hello, World!
1 + 2 = 3
1 + 2 + 3 = 6
```
</details>
### Windows 应用程序入口函数
* GUIGraphical User Interface应用链接器选项`/SUBSYSTEM:WINDOWS`
* CUIConsole User Interface应用链接器选项`/SUBSYSTEM:CONSOLE`
<details><summary>_tWinMain 与 _tmain 函数声明</summary>
```cpp
Int WINAPI _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE,
PTSTR pszCmdLine,
int nCmdShow);
int _tmain(
int argc,
TCHAR *argv[],
TCHAR *envp[]);
```
</details>
应用程序类型|入口点函数|嵌入可执行文件的启动函数
---|---|---
处理ANSI字符的GUI应用程序|_tWinMain(WinMain)|WinMainCRTSartup
处理Unicode字符的GUI应用程序|_tWinMain(wWinMain)|wWinMainCRTSartup
处理ANSI字符的CUI应用程序|_tmain(Main)|mainCRTSartup
处理Unicode字符的CUI应用程序|_tmain(wMain)|wmainCRTSartup
动态链接库Dynamic-Link Library|DllMain|_DllMainCRTStartup
### Windows 的动态链接库Dynamic-Link Library
#### 用处
* 扩展了应用程序的特性
* 简化了项目管理
* 有助于节省内存
* 促进了资源的共享
* 促进了本地化
* 有助于解决平台间的差异
* 可以用于特殊目的
#### 注意
* 创建 DLL事实上是在创建可供一个可执行模块调用的函数
* 当一个模块提供一个内存分配函数malloc、new的时候它必须同时提供另一个内存释放函数free、delete
* 在使用 C 和 C++ 混编的时候,要使用 extern "C" 修饰符
* 一个 DLL 可以导出函数、变量避免导出、C++ 类(导出导入需要同编译器,否则避免导出)
* DLL 模块cpp 文件中的 __declspec(dllexport) 写在 include 头文件之前
* 调用 DLL 的可执行模块cpp 文件的 __declspec(dllimport) 之前不应该定义 MYLIBAPI
#### 加载 Windows 程序的搜索顺序
1. 包含可执行文件的目录
2. Windows 的系统目录,可以通过 GetSystemDirectory 得到
3. 16 位的系统目录,即 Windows 目录中的 System 子目录
4. Windows 目录,可以通过 GetWindowsDirectory 得到
5. 进程的当前目录
6. PATH 环境变量中所列出的目录
#### DLL 入口函数
<details><summary>DllMain 函数</summary>
```cpp
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
/* do something useful */
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
// 第一次将一个DLL映射到进程地址空间时调用
// The DLL is being mapped into the process' address space.
break;
case DLL_THREAD_ATTACH:
// 当进程创建一个线程的时候用于告诉DLL执行与线程相关的初始化非主线程执行
// A thread is bing created.
break;
case DLL_THREAD_DETACH:
// 系统调用 ExitThread 线程退出前即将终止的线程通过告诉DLL执行与线程相关的清理
// A thread is exiting cleanly.
break;
case DLL_PROCESS_DETACH:
// 将一个DLL从进程的地址空间时调用
// The DLL is being unmapped from the process' address space.
break;
}
return (TRUE); // Used only for DLL_PROCESS_ATTACH
}
```
</details>
#### 载入卸载库
<details><summary>LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread 函数声明</summary>
```cpp
// 载入库
HMODULE WINAPI LoadLibrary(
_In_ LPCTSTR lpFileName
);
HMODULE LoadLibraryExA(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
// 若要在通用 Windows 平台UWP应用中加载 Win32 DLL需要调用 LoadPackagedLibrary而不是 LoadLibrary 或 LoadLibraryEx
HMODULE LoadPackagedLibrary(
LPCWSTR lpwLibFileName,
DWORD Reserved
);
// 卸载库
BOOL WINAPI FreeLibrary(
_In_ HMODULE hModule
);
// 卸载库和退出线程
VOID WINAPI FreeLibraryAndExitThread(
_In_ HMODULE hModule,
_In_ DWORD dwExitCode
);
```
</details>
#### 显示地链接到导出符号
<details><summary>GetProcAddress 函数声明</summary>
```cpp
FARPROC GetProcAddress(
HMODULE hInstDll,
PCSTR pszSymbolName // 只能接受 ANSI 字符串,不能是 Unicode
);
```
</details>
#### DumpBin.exe 查看 DLL 信息
`VS 的开发人员命令提示符` 使用 `DumpBin.exe` 可查看 DLL 库的导出段导出的变量、函数、类名的符号、相对虚拟地址RVArelative virtual address。如
```
DUMPBIN -exports D:\mydll.dll
```
#### LoadLibrary 与 FreeLibrary 流程图
<details><summary>LoadLibrary 与 FreeLibrary 流程图</summary>
##### LoadLibrary
![WindowsLoadLibrary](http://ojlsgreog.bkt.clouddn.com/WindowsLoadLibrary.png)
##### FreeLibrary
![WindowsFreeLibrary](http://ojlsgreog.bkt.clouddn.com/WindowsFreeLibrary.png)
</details>
#### DLL 库的编写(导出一个 DLL 模块)
<details><summary>DLL 库的编写(导出一个 DLL 模块)</summary>
DLL 头文件
```cpp
// MyLib.h
#ifdef MYLIBAPI
// MYLIBAPI 应该在全部 DLL 源文件的 include "Mylib.h" 之前被定义
// 全部函数/变量正在被导出
#else
// 这个头文件被一个exe源代码模块包含意味着全部函数/变量被导入
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
// 这里定义任何的数据结构和符号
// 定义导出的变量(避免导出变量)
MYLIBAPI int g_nResult;
// 定义导出函数原型
MYLIBAPI int Add(int nLeft, int nRight);
```
DLL 源文件
```cpp
// MyLibFile1.cpp
// 包含标准Windows和C运行时头文件
#include <windows.h>
// DLL源码文件导出的函数和变量
#define MYLIBAPI extern "C" __declspec(dllexport)
// 包含导出的数据结构、符号、函数、变量
#include "MyLib.h"
// 将此DLL源代码文件的代码放在此处
int g_nResult;
int Add(int nLeft, int nRight)
{
g_nResult = nLeft + nRight;
return g_nResult;
}
```
</details>
#### DLL 库的使用(运行时动态链接 DLL
<details><summary>DLL 库的使用(运行时动态链接 DLL</summary>
```cpp
// A simple program that uses LoadLibrary and
// GetProcAddress to access myPuts from Myputs.dll.
#include <windows.h>
#include <stdio.h>
typedef int (__cdecl *MYPROC)(LPWSTR);
int main( void )
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(TEXT("MyPuts.dll"));
// If the handle is valid, try to get the function address.
if (hinstLib != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts");
// If the function address is valid, call the function.
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
(ProcAdd) (L"Message sent to the DLL function\n");
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
// If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
printf("Message printed from executable\n");
return 0;
}
```