• VC++动态链接库编程之DLL典型实例


      动态链接库DLL实现了库的共享,体现了代码重用的思想。我们可以把广泛的、具有共性的、能够多次被利用的函数和类定义在库中。这样,在再次使用这些函数和类的时候,就不再需要重新添加与这些函数和类相关的代码。具有共性的问题大致有哪些呢?笔者归纳如下: 

      (1)通用的算法

      图像处理、视频音频解码、压缩与解压缩、加密与解密通常采用某些特定的算法,这些算法较固定且在这类程序中往往经常被使用。

      (2)纯资源DLL

      我们可以从DLL中获取资源,对于一个支持多种语言的应用程序而言,我们可以判断操作系统的语言,并自动为应用程序加载与OS对应的语言。这是多语言支持应用程序的一般做法。

      (3)通信控制DLL

      串口、网口的通信控制函数如果由DLL提供则可以使应用程序轻松不少。在工业控制、modem程序甚至socket通信中,经常使用通信控制DLL。

      本节将给出DLL的三个典型应用实例。

      7.1 算法DLL

      我们直接用读者的一个提问作为例子。

      宋宝华先生,您好!

      我在dev.yesky.com上看到你连载的《VC++动态链接库编程》,觉得非常好。我以前主要是用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的什么特性使得它能够成为一种病毒?下一节我们将揭晓谜底。

  • 相关阅读:
    mormot2封装tcp
    mormot.net.client.pas
    mormot.net.server.pas
    delphi working with big data databases in Cassandra, Couchbase and MongoDB
    Grijjy.ProtocolBuffers.pas
    Grijjy.Bson.Serialization.pas
    GrijjyFoundation开源框架
    Unidac内存表使用
    传统关系型数据库正在完成华丽的蜕变
    使用arthas 分析java 应用的一个流程总结
  • 原文地址:https://www.cnblogs.com/jinsedemaitian/p/5589132.html
Copyright © 2020-2023  润新知