动态链接库和静态链接库的使用

用一个实例来说明动态链接库的使用,该实例是运行在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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值