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

相关文章
|
4月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
76 2
|
2月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
281 63
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
83 0
|
2月前
|
安全 程序员 编译器
【实战经验】17个C++编程常见错误及其解决方案
想必不少程序员都有类似的经历:辛苦敲完项目代码,内心满是对作品品质的自信,然而当静态扫描工具登场时,却揭示出诸多隐藏的警告问题。为了让自己的编程之路更加顺畅,也为了持续精进技艺,我想借此机会汇总分享那些常被我们无意间忽视却又导致警告的编程小细节,以此作为对未来的自我警示和提升。
161 7
|
2月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
53 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
3月前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
53 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
86 11
|
2月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
55 5
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
47 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
83 2