VC++
动态链接库
(DLL)
编程(五)
――
DLL
典型实例
作者:宋宝华 e-mail:21cnbao@21cn.com
动态链接库
DLL
实现了库的共享,体现了代码重用的思想。我们可以把广泛的、具有共性的、能够多次被利用的函数和类定义在库中。这样,在再次使用这些函数和类的时候,就不再需要重新添加与这些函数和类相关的代码。具有共性的问题大致有哪些呢?笔者归纳如下:
(
1
)通用的算法
图像处理、视频音频解码、压缩与解压缩、加密与解密通常采用某些特定的算法,这些算法较固定且在这类程序中往往经常被使用。
(
2
)纯资源
DLL
我们可以从
DLL
中获取资源,对于一个支持多种语言的应用程序而言,我们可以判断操作系统的语言,并自动为应用程序加载与
OS
对应的语言。这是多语言支持应用程序的一般做法。
(
3
)通信控制
DLL
串口、网口的通信控制函数如果由
DLL
提供则可以使应用程序轻松不少。在工业控制、
modem
程序甚至
socket
通信中,经常使用通信控制
DLL
。
本节将给出
DLL
的三个典型应用实例。
7.1
算法
DLL
我们直接用读者的一个提问作为例子。
宋宝华先生,您好!
我在pconline上看到你连载的《VC++动态链接库(DLL)编程深入浅出》,觉得非常好。我以前主要是用Delphi的,C/C++学过,对Win32和VCL比较熟悉,但是没有接触过VC++,对MFC很陌生。这段时间和一个同学合作做光学成像的计算机模拟,用到傅立叶变换,手里面有例程是VC++写的。我们的界面是用Delphi开发,需要将其傅立叶变换功能提出做一个DLL供Delphi调用。苦于不懂MFC,试了很多方法,都不成功,最后只得采用折衷方案,简单修改一下程序,传一个参数进去,当作exe来调用,才没有耽搁后续进程。
……
谢谢!
致
礼!
某某
学习过较高级别数学(概率统计与随机过程)、信号与线性系统及数字信号处理的读者应该知道,傅立叶变换是一种在信号分析中常用的算法,用于时域和频域的相互转换。
FFT
变换算法通用而有共性,我们适宜把它集成在一个
DLL
中。
随后,这位读者提供了这样的一个函数:
/*
函数名称:
FFT()
*
参数
:
* complex<double> * TD -
指向时域数组的指针
* complex<double> * FD -
指向频域数组的指针
* r
-
2
的幂数,即迭代次数
*
返回值
:
无。
*
说明
:
该函数用来实现快速傅立叶变换
*/
void FFT(complex<double> * TD, complex<double> * FD, int r)
{
LONG count; //
傅立叶变换点数
int i,j,k; //
循环变量
int bfsize,p; //
中间变量
double angle; //
角度
complex<double> *W,*X1,*X2,*X;
count = 1 << r; //
傅立叶变换点数
//
分配运算所需存储器
W = new complex<double>[count / 2];
X1 = new complex<double>[count];
X2 = new complex<double>[count];
//
计算加权系数
for(i = 0; i < count / 2; i++)
{
angle = -i * PI * 2 / count;
W[i] = complex<double> (cos(angle), sin(angle));
}
//
将时域点写入
X1
memcpy(X1, TD, sizeof(complex<double>) * count);
//
采用蝶形算法进行快速傅立叶变换
for(k = 0; k < r; k++)
{
for(j = 0; j < 1 << k; j++)
{
bfsize = 1 << (r-k);
for(i = 0; i < bfsize / 2; i++)
{
p = j * bfsize;
X2[i + p] = X1[i + p] + X1[i + p + bfsize / 2];
X2[i + p + bfsize / 2] = (X1[i + p] - X1[i + p + bfsize / 2]) * W[i * (1<<k)];
}
}
X = X1;
X1 = X2;
X2 = X;
}
//
重新排序
for(j = 0; j < count; j++)
{
p = 0;
for(i = 0; i < r; i++)
{
if (j&(1<<i))
{
p+=1<<(r-i-1);
}
}
FD[j]=X1[p];
}
//
释放内存
delete W;
delete X1;
delete X2;
}
既然有了
FFT
这个函数,我们要把它做在
DLL
中,作为
DLL
的一个接口将是十分简单的,其步骤如下:
(
1
)利用
MFC
向导建立一个非
MFC DLL
;
(
2
)在工程中添加
fft.h
和
fft.cpp
两个文件;
fft.h
的源代码为:
#ifndef FFT_H
#define FFT_H
#include <complex>
using namespace std;
extern "C" void __declspec(dllexport) __stdcall FFT(complex<double> * TD, complex<double> * FD, int r);
#define PI 3.1415926
#endif
fft.cpp
的源代码为:
/*
文件名:
fft.cpp
*/
#include "fft.h"
void __stdcall FFT(complex<double> * TD, complex<double> * FD, int r)
{
…//
读者提供的函数代码
}
在任何编程语言中使用
Win32 API LoadLibrary
都可以加载这个
DLL
,而使用
GetProcAddress(hDll, "FFT")
则可以获得函数
FFT
的地址,读者所提到的
Delphi
当然也不例外。
这个
DLL
中有两点需要注意:
(
1
)使用
extern "C"
修饰函数声明,否则,生成的
DLL
只能供
C++
调用;
(
2
)使用
__stdcall
修饰函数声明及定义,
__stdcall
是
Windows API
的函数调用方式。
7.2
纯资源
DLL
我们在应用程序中产生如图
18
所示的资源(对话框)。
图
18
中文对话框
在与这个应用程序相同的工作区里利用
MFC
向导建立两个简单的
DLL
,把应用工程中的资源全选后分别拷贝到
ChineseDll
和
EngLishDll
,在
EnglishDll
工程的资源文件中搜索下面的语句:
/////////////////////////////////////////////////////////////////////////////
// Chinese (P.R.C.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
#ifdef _WIN32
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)
#endif //_WIN32
将其改为:
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
并将其中所有的中文翻译为英文。这个
DLL
为我们提供了如图
19
所示的对话框资源。
图
19
英文对话框
修改应用工程的
InitInstance()
函数,在
CResourceDllCallDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
之前(即对话框显示之前)添加如下代码:
//
获取操作系统的语言
WORD wLangPID = PRIMARYLANGID( GetSystemDefaultLangID() );
if( LANG_CHINESE == wLangPID )
{
hLanguageDll = LoadLibrary( "ChineseDll.dll" ); //
加载中文资源
}
else
{
hLanguageDll = LoadLibrary( "EnglishDll.dll" ); //
加载英文资源
}
if( NULL == hLanguageDll )
{
AfxMessageBox( "Load DLL failure" );
return FALSE;
}
AfxSetResourceHandle( hLanguageDll ); //
设置当前的资源句柄
这样的应用程序将具有自适应性质,在中文
OS
中显示中文资源,在英文
OS
中则显示英文资源。
7.3
通信控制
DLL
我们在这里举一个串口通信类的例子。
也许您需要了解一点串口通信的背景知识,其实串口到处都看得到,譬如
PC
机的
COM
口即为串行通讯口(简称串口)。如图
20
,打开
Windows
的设备管理器,我们看到了
COM
口。
在
Windows
系统,需通过
DCB(Device Control Block)
对串口进行配置。利用
Windows API GetCommState
函数可以获取串口当前配置;利用
SetCommState
函数则可以设置串口通讯的参数。
串行通信通常按以下四步进行:
(1)
打开串口;
(2)
配置串口;
(3)
数据传送;
(4)
关闭串口。
图
20 PC
的串口
由此可见,我们需要给串口控制
DLL
提供如下四个接口函数:
//
打开指定的串口,其参数
port
为端口号
BOOL ComOpen(int port); //
在这个函数里使用默认的参数设置串口
//
将打开的串口关闭
void ComClose(int port);
//
将串口接收缓冲区中的数据放到
buffer
中
int GetComData(char *buf, int buf_len);
//
将指定长度的数据发送到串口
int SendDataToCom(LPBYTE buf,int buf_Len);
下面给出了
DLL
接口的主要源代码框架:
//com.h
:
com
类通信接口
class AFX_EXT_CLASS com
{
public:
ComOpen(int port)
{
…
}
int SendDataToCom(LPBYTE buf,int buf_Len)
{
…
}
int GetComData(char *buf, int buf_len)
{
…
}
void ComClose()
{
…
}
}
我们编写一控制台程序来演示
DLL
的调用:
#include <iostream>
#include <exception>
using namespace std;
#include <windows.h>
#include "com.h" //
包含
DLL
中导出类的头文件
int main(int argc, char *argv[])
{
try
{
char str[] = "com_class test";
com com1;
com1.ComOpen (1);
for(int i=0; i<100; i++) //
以同步方式写
com
的
buffer
{
Sleep(500);
com1.SendDataToCom (str,strlen(str));
}
com1.ComClose ();
}
catch(exception &e)
{
cout << e.what() << endl;
}
return 0;
}
DLL
的编写与调用方法及主要应用皆已讲完,在下一节里,我们将看到比较“高深”的主题――
DLL
木马。曾几何时,
DLL
木马成为了病毒的一种十分重要的形式,是
DLL
的什么特性使得它能够成为一种病毒?下一节我们将揭晓谜底。
本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120765,如需转载请自行联系原作者