用一个实例来说明动态链接库的使用,该实例是运行在Windows系统上的一个简单截屏功能
一、创建动态链接库
1、VS新建Win32控制台应用程序,配置向导的应用程序设置页应用程序类型选择“DLL”,附加选项选择“空项目”
2、添加源文件和头文件,如CaptureScreen.cpp和CaptureScreen.h
3、该动态库实现一个RunCapture函数,该函数读取屏幕颜色数据,然后将颜色数据通过回调函数OnCaptureComplete给外部调用程序使用,头文件和源文件代码如下:
//CaptureScreen.h
#ifndef __CAPTURE_SCREEN_H_
#define __CAPTURE_SCREEN_H_
#ifdef CAPTURE_SCREEN_EXPORT
#define CS_API __declspec(dllexport)
#else
#define CS_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
typedef bool(*OnCaptureComplete)(const char* /*data*/, int /*width*/,int /*height*/,int/*bytesPerColor*/);
/*
x,y:截取屏幕的起始位置
width,height:截取屏幕的大小
InCompeleteFunction:截取完成后的回调函数
*/
CS_API bool RunCapture(int x,int y,int width,int height,OnCaptureComplete InCompeleteFunction);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif //__CAPTURE_SCREEN_H_
//CaptureScreen.cpp
#define CAPTURE_SCREEN_EXPORT
#include"CaptureScreen.h"
#include<Windows.h>
bool RunCapture(int x, int y, int width, int height, OnCaptureComplete InCompeleteFunction)
{
//获取屏幕大小,并调整截取图像的参数
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
if (x < 0 || x >= screenWidth)
{
x = 0;
}
if (y < 0 || y >= screenHeight)
{
y = 0;
}
if (width <= 0 || width+x > screenWidth)
{
width = screenWidth-x;
}
if (height <= 0 || height+y > screenHeight)
{
height = screenHeight-y;
}
int bytesPerColor = 3;
//获取屏幕DC句柄以及创建兼容的内存DC
HDC hdcScreen = GetDC(NULL);
HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
HBITMAP hbmScreen = CreateCompatibleBitmap(hdcScreen, width, height);
//位图信息
BITMAPINFO bi;
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bytesPerColor*8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biXPelsPerMeter = 0;
bi.bmiHeader.biYPelsPerMeter = 0;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
int bmpSize = ((width * bi.bmiHeader.biBitCount + 31) / 32) * 4*height;
char* lpBitmap = new char[bmpSize];
memset(lpBitmap, 0, bmpSize);
//获取屏幕颜色信息
SelectObject(hdcMemDC, hbmScreen);
if (BitBlt(hdcMemDC, 0, 0, width, height, hdcScreen, x, y, SRCCOPY))
{
GetDIBits(hdcMemDC, hbmScreen, 0, height, lpBitmap, &bi, DIB_RGB_COLORS);
if (InCompeleteFunction)
{
InCompeleteFunction(lpBitmap, width, height, bytesPerColor);//回调,由外部调用程序处理数据
}
}
//释放资源
delete[] lpBitmap;
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
return true;
}
4、编译链接后会生成CaptureScreen.lib和CaptureScreen.dll两个文件,lib文件含有导出函数(RunCapture)在dll文件中的信息,调用该动态库的时候会用到
注1:在头文件中使用CAPTURE_SCREEN_EXPORT宏来决定函数是导出还是导入,在动态库工程中需定义CAPTURE_SCREEN_EXPORT宏,这时CS_API宏定义为__declspec(dllexport),RunCapture函数为导出,在引用该动态链接库的工程中则不需要定义CAPTURE_SCREEN_EXPORT,表示RunCapture函数为导入。
注2:extern "C"声明告诉编译器以C语言的方式编译链接其中的函数,这样就不会改变函数名称。如果未加此声明,在dll文件中的函数名就不是RunCapture了,动态调用中通过GetProcAddress获得的函数指针将为空,静态调用时可不加此声明
二、调用动态链接库
调用动态链接库的方法有静态调用和动态调用
1、静态调用,静态调用用到CaptureScreen工程中的CaptureScreen.h、CaptureScreen.lib和CaptureScreen.dll
(1)新建一个控制台应用程序空项目,命名为Test,并添加一个源文件main.cpp
(2)将CaptureScreen.h和CaptureScreen.lib以及CaptureScreen.dll分别复制到工程Test的相应位置,CaptureScreen.dll文件一般与最终生成的可执行文件同一目录。
(3)在工程Test的“属性->链接器->输入->附加依赖项”添加CaptureScreen.lib文件的路径(这里也可以直接在代码中引用#pragma comment(lib,"CaptureScreen.lib"))
(4)在main.cpp文件中包含CaptureScreen.h文件,之后就可以直接调用RunCapture函数,完整代码如下:
#include"CaptureScreen.h"
#include<stdio.h>
#include<windows.h>
//#pragma comment(lib,"CaptureScreen.lib")
bool OnComplete(const char* data,int width,int height,int bytesPerColor)
{
FILE* pFile = nullptr;
fopen_s(&pFile, "test.bmp", "wb");
if (pFile)
{
BITMAPINFOHEADER bmpInfoHeader;//bmp信息头
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biWidth = width;
bmpInfoHeader.biHeight = height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = bytesPerColor*8;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biSizeImage = 0;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
DWORD bmpDataSize = ((width * bmpInfoHeader.biBitCount + 31) / 32) * 4*height;//数据字节数
BITMAPFILEHEADER bmpFileHeader;//bmp文件头
bmpFileHeader.bfType = 0x4D42; //BM bmp文件标识符
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + sizeof(bmpInfoHeader);//bmp颜色数据偏移
bmpFileHeader.bfSize = bmpDataSize + sizeof(bmpFileHeader) + sizeof(bmpInfoHeader);//BMP文件大小
//依次写入文件头,信息头和数据
fwrite(&bmpFileHeader,sizeof(bmpFileHeader),1,pFile);
fwrite(&bmpInfoHeader,sizeof(bmpInfoHeader),1,pFile);
fwrite(data,1, bmpDataSize,pFile);
fclose(pFile);
}
return true;
}
int main()
{
RunCapture(0,0,0,0,OnComplete);
return 0;
}
2、动态调用,只需要CaptureScreen.dll文件,但是要知道其中函数的名称(在这里是RunCapture)
(1)新建一个控制台应用程序,命名为Test1,并添加一个源文件main.cpp
(2)将CaptureScreen.dll文件放到工程Test1相应位置
(3)使用LoadLibrary/FreeLibrary加载/释放动态库,使用GetProcAddress获取函数地址(函数指针),代码如下:
#include<stdio.h>
#include<windows.h>
bool OnComplete(const char* data, int width, int height, int bytesPerColor)
{
FILE* pFile = nullptr;
fopen_s(&pFile, "test.bmp", "wb");
if (pFile)
{
BITMAPINFOHEADER bmpInfoHeader;//bmp信息头
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biWidth = width;
bmpInfoHeader.biHeight = height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = bytesPerColor * 8;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biSizeImage = 0;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
DWORD bmpDataSize = ((width * bmpInfoHeader.biBitCount + 31) / 32) * 4*height;
BITMAPFILEHEADER bmpFileHeader;//bmp文件头
bmpFileHeader.bfType = 0x4D42; //BM bmp文件标识符
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + sizeof(bmpInfoHeader);//bmp颜色数据偏移
bmpFileHeader.bfSize = bmpDataSize + sizeof(bmpFileHeader) + sizeof(bmpInfoHeader);//BMP文件大小
//依次写入文件头,信息头和数据
fwrite(&bmpFileHeader, sizeof(bmpFileHeader), 1, pFile);
fwrite(&bmpInfoHeader, sizeof(bmpInfoHeader), 1, pFile);
fwrite(data, 1, bmpDataSize, pFile);
fclose(pFile);
}
return true;
}
typedef bool(*OnCaptureComplete)(const char*, int, int, int);
typedef bool(*RunCaptureProc)(int x, int y, int width, int height, OnCaptureComplete InCompeleteFunction);
int main()
{
HMODULE hLib = ::LoadLibrary(TEXT("CaptureScreen.dll"));
if (hLib)
{
RunCaptureProc proc = (RunCaptureProc)::GetProcAddress(hLib, "RunCapture");
if (proc)
{
proc(0, 0, 0, 0, OnComplete);
}
::FreeLibrary(hLib);
}
return 0;
}
静态链接库与动态链接库的静态链接用法差不多,只是没有dll文件,所有信息都被编译到lib文件中,因此静态链接库的lib文件比动态链接库的lib文件大很多,外部程序引用静态链接库时会将整个lib文件链接到最终可执行文件,之后就不需要依赖该静态库的任何文件。创建静态链接库工程可以在创建想到中选择静态库,或者将一个已有工程的工程属性的配置类型改为静态库。此外静态库不需要extern "C"声明,也不需要__declspec(dllexport)或者__declspec(dllimport)声明。
将CaptureScreen改为静态库后代码如下
//CaptureScreen.h
#ifndef __CAPTURE_SCREEN_H_
#define __CAPTURE_SCREEN_H_
typedef bool(*OnCaptureComplete)(const char* /*data*/, int /*width*/, int /*height*/, int/*bytesPerColor*/);
/*
x,y:截取屏幕的起始位置
width,height:截取屏幕的大小
InCompeleteFunction:截取完成后的回调函数
*/
bool RunCapture(int x, int y, int width, int height, OnCaptureComplete InCompeleteFunction);
#endif //__CAPTURE_SCREEN_H_
//CaptureScreen.cpp
#include"CaptureScreen.h"
#include<Windows.h>
bool RunCapture(int x, int y, int width, int height, OnCaptureComplete InCompeleteFunction)
{
//获取屏幕大小,并调整截取图像的参数
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
if (x < 0 || x >= screenWidth)
{
x = 0;
}
if (y < 0 || y >= screenHeight)
{
y = 0;
}
if (width <= 0 || width+x > screenWidth)
{
width = screenWidth-x;
}
if (height <= 0 || height+y > screenHeight)
{
height = screenHeight-y;
}
int bytesPerColor = 3;
//获取屏幕DC句柄以及创建兼容的内存DC
HDC hdcScreen = GetDC(NULL);
HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
HBITMAP hbmScreen = CreateCompatibleBitmap(hdcScreen, width, height);
//位图信息
BITMAPINFO bi;
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bytesPerColor*8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biXPelsPerMeter = 0;
bi.bmiHeader.biYPelsPerMeter = 0;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
int bmpSize = ((width * bi.bmiHeader.biBitCount + 31) / 32) * 4*height;
char* lpBitmap = new char[bmpSize];
memset(lpBitmap, 0, bmpSize);
//获取屏幕颜色信息
SelectObject(hdcMemDC, hbmScreen);
if (BitBlt(hdcMemDC, 0, 0, width, height, hdcScreen, x, y, SRCCOPY))
{
GetDIBits(hdcMemDC, hbmScreen, 0, height, lpBitmap, &bi, DIB_RGB_COLORS);
if (InCompeleteFunction)
{
InCompeleteFunction(lpBitmap, width, height, bytesPerColor);//回调,由外部调用程序处理数据
}
}
//释放资源
delete[] lpBitmap;
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
return true;
}