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,如需转载请自行联系原作者

相关文章
|
2月前
|
监控 Linux C++
4步实现C++插件化编程,轻松实现功能定制与扩展(2)
本文是《4步实现C++插件化编程》的延伸,重点介绍了新增的插件“热拔插”功能。通过`inotify`接口监控指定路径下的文件变动,结合`epoll`实现非阻塞监听,动态加载或卸载插件。核心设计包括`SprDirWatch`工具类封装`inotify`,以及`PluginManager`管理插件生命周期。验证部分展示了插件加载与卸载的日志及模块状态,确保功能稳定可靠。优化过程中解决了动态链接库句柄泄露问题,强调了采纳用户建议的重要性。
93 27
4步实现C++插件化编程,轻松实现功能定制与扩展(2)
|
3月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
3月前
|
存储 机器学习/深度学习 编译器
【C++终极篇】C++11:编程新纪元的神秘力量揭秘
【C++终极篇】C++11:编程新纪元的神秘力量揭秘
|
7月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
889 90
|
3月前
|
存储 算法 C++
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
|
3月前
|
存储 安全 算法
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
|
7月前
|
安全 程序员 编译器
【实战经验】17个C++编程常见错误及其解决方案
想必不少程序员都有类似的经历:辛苦敲完项目代码,内心满是对作品品质的自信,然而当静态扫描工具登场时,却揭示出诸多隐藏的警告问题。为了让自己的编程之路更加顺畅,也为了持续精进技艺,我想借此机会汇总分享那些常被我们无意间忽视却又导致警告的编程小细节,以此作为对未来的自我警示和提升。
1007 34
|
6月前
|
消息中间件 存储 安全
|
7月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
146 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
8月前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
123 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用

热门文章

最新文章