VC++动态链接库(DLL)编程(五)――DLL典型实例

简介:
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++学过,对Win32VCL比较熟悉,但是没有接触过VC++,对MFC很陌生。这段时间和一个同学合作做光学成像的计算机模拟,用到傅立叶变换,手里面有例程是VC++写的。我们的界面是用Delphi开发,需要将其傅立叶变换功能提出做一个DLLDelphi调用。苦于不懂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,如需转载请自行联系原作者

相关文章
|
22天前
|
安全 算法 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
46 3
|
24天前
|
安全 算法 编译器
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
240 3
|
24天前
|
存储 算法 编译器
【C++ TypeName用法 】掌握C++中的TypeName:模板编程的瑞士军刀
【C++ TypeName用法 】掌握C++中的TypeName:模板编程的瑞士军刀
234 0
|
24天前
|
安全 算法 C++
【C++泛型编程 进阶篇】模板返回值的优雅处理(二)
【C++泛型编程 进阶篇】模板返回值的优雅处理
31 0
|
24天前
|
安全 算法 编译器
【C++泛型编程 进阶篇】模板返回值的优雅处理(一)
【C++泛型编程 进阶篇】模板返回值的优雅处理
42 0
|
24天前
|
存储 网络协议 C语言
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
76 0
|
24天前
|
算法 编译器 数据库
【C++ 泛型编程 高级篇】使用SFINAE和if constexpr灵活处理类型进行条件编译
【C++ 泛型编程 高级篇】使用SFINAE和if constexpr灵活处理类型进行条件编译
243 0
|
24天前
|
机器学习/深度学习 算法 编译器
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
【C++ 泛型编程 中级篇】深度解析C++:类型模板参数与非类型模板参数
46 0
|
24天前
|
设计模式 程序员 C++
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
241 2
|
24天前
|
算法 安全 C++
【C++ 泛型编程 入门篇】深入探索C++的numeric_limits:全面理解数值界限(一)
【C++ 泛型编程 入门篇】深入探索C++的numeric_limits:全面理解数值界限
43 0