VC++动态链接库(DLL)编程(五)――DLL典型实例-阿里云开发者社区

开发者社区> 技术小胖子> 正文

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.hfft.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修饰函数声明及定义,__stdcallWindows API的函数调用方式。
 
7.2纯资源DLL
 
我们在应用程序中产生如图18所示的资源(对话框)。
18 中文对话框
       在与这个应用程序相同的工作区里利用MFC向导建立两个简单的DLL,把应用工程中的资源全选后分别拷贝到ChineseDllEngLishDll,在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.hcom类通信接口
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++)   //以同步方式写combuffer
              {
                     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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【趣话编程】谁动了你的HTTPS流量?
已经用上了https,为什么还是被劫持?
741 0
Python编程:利用上下文管理器管理MySQL的链接对象
Python编程:利用上下文管理器管理MySQL的链接对象
17 0
C/C++程序编译流程(预处理-&gt;编译-&gt;汇编-&gt;链接)
程序的基本流程如图: 1. 预处理 预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。
759 0
Spring MVC 函数式编程进阶
1. 前言 上一篇对 Spring MVC 的函数式接口编程进行了简单入门,让很多不知道的同学见识了这种新操作。也有反应这种看起来没有传统写法顺眼,其实大家都一样。但是我们还是要敢于尝试新事物。Java Lambada 刚出来也是被人各种吐槽,现在我在很多项目都见到了它的身影。好了转回正题,本文是对上一篇的延伸,我们继续对 Functional Endpoint 进行一些了解和运用。范式转换其实上一篇已经介绍差不多了,但是一旦你初次接触这种方式往往会面临新的问题。 2. 新的问题 在使用这种风格时我们也会遇到一些新的问题。接下来我们将通过举例来一步步解决这些问题。 2.1 如何异常处理 接
33 0
21119
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载