Windows Embedded Compact 7中的dll编程(上)

   动态链接库(Dynamic Link Library,DLL)是一些编译过的可执行程序模块,它包含代

码、数据或资源,可以在应用程序中或其他DLL中被调用。动态链接库的文件扩展名一般为.dll,也可以是.drv(设备驱动程序)、.sys(系统文件)和.fon(字体文件)。DLL的应用非常广泛,可以实现多个应用程序问的代码和资源共享,是Windows Embedded Compact 7程序设计中的一个非常重要的组成部分。

12.1 dll概述

当使用普通的函数库时,可以在程序链接时将库中的代码拷贝到可执行文件中,这是一种

静态链接。而在多个同样的程序执行时,系统将保留许多重复的代码副本,很容易造成内存资源的浪费。如果使用DLL动态链接库,那么在建立应用程序的可执行文件时,就不必将DLL链接到程序中,只需要在应用程序运行时动态地装载DLL。装载时DLL将被映射到进程的地址空间中,因此使用DLL动态链接并不就是拷贝库代码,只是在程序中记录了函数的入口点和接口,在程序执行时才将库代码装入内存。所以不管多少程序使用了DLL,内存中都将只有该DLL的一个副本,当没有程序使用它时,系统就将它移出内存,减少了对内存和磁盘的要求。由此可见,使用DLL的一个明显的好处就是节省系统资源。

    除了以上的优点外,使用DLL设计程序还有以下一些优点:

    ・  共享代码、资源和数据。DLL作为一种基于WindoWS的程序模块,不仅可以包含可

    执行代码,还可以包括数据和各种资源等,扩大了库文件的使用范围。

    DLL提供了共享资源的途径,例如位图、字体或者图表等都可以放到一个资源文件中,

并直接连接到应用程序中。如果将这些资源都放到DLL中,那么许多应用程序都可以直接使

用,而不必在内存里重复装入这些数据。

    在16位的Windows中,DLL有自己的数据段,因此所有需要调用同一个DLL的应用程

序都能够访问同一个全局变量和静态变量。但是在32位的系统中,情况就不同了。因为DLL

的映像被映射到每个进程的地址空间,该DLL的所有数据将属于映射到的进程。值得注意的

是,尽管进程间不能共享DLL的数据,但是同一个进程的所有线程则可以共享。由于线程间

相互独立,因此在访问某一DLL全局变量时,要注意保持同步,以免冲突。

    尽DLL的映像被映射到每个进程的地址空间时,该DLL的所有数据将属于映射到的进

程,这也并不意味着没有办法在进程间共享DLL的数据。利用内存映射文件就可以实现,只要将数据存储到内存映射的共享区,那么一切需要调用DLL的应用程序都可以读取这些存储在内存中的共享区域的数据。

    ・  可将系统模块化,方便升级。DLL技术对于开发大型软件系统也大有好处。如果使

    用一个执行文件完成一个大型系统,那么程序将会很庞大,而且还可能存在许多重复

    的功能。而如果将程序分成一系列的主程序和DLL,则可以减少开发的工作量并加

    快开发的速度。而且,如果开发过程中就将一些功能模块做成DLL,那么在需要对

    系统进行升级的时候,只需要升级个别DLL,然后用新的DLL文件覆盖掉旧的DLL

    文件就.-1以Y,如此一来,就不需要对整个系统进行重新编译和链接,极大地方便了

    系统的升级和维护。

    ・  隐藏实现的细节。在某些情况下,用户可能想隐藏例程实现的细节,DLL就是一个

    非常不错的实现方法。DLL的例程可以被应用程序访问,而不显示其中的代码细节。

    还有很重要的一点就是DLL与语言无关。

12.2 dll的调用

12.2.1 静态调用

静态调用指由编译系统完成对DLL的加载并且在应用程序结束时对DLL进行释放。静态

调用相对于动态调用而言,其优点是简单实用,弊端就是不够灵活。

    在Visual C++中静态调用DLL也非常简单,首先将动态连接库的.LIB文件加入到应用程

序的工程中,然后在使用DLL函数的文件里引用DLL的头文件即可。

    静态调用不需要调用LoadLibrary和FreeLibrary,这是因为开发人员在建立一个DLL文

件时,链接程序会自动生成一个与之对应的LIB导入文件。该文件包含了每一个DLL导出函

数的符号名和可选的标识号,但是并不包含实际的代码,LIB文件会作为DLL的替代文件被

编译到应用程序项目中。当开发人员通过静态链接方式编译并生成应用程序时,应用程序中的

调用函数与LIB文件中的导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。LIB

文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件

内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息查寻并加载

DLL,然后通过符号名或标识号实现对DLL函数的动态链接。当加载应用程序的EXE文件时,

所有被应用程序调用的DLL文件都将被加载在到内存中。可执行程序直接通过函数名调用

DLL的输出函数,其调用方法与调用程序内部的其他的函数相同。

12.2.2 动态调用

动态调用是由开发人员使用APl函数手工加载和卸载DLL,以达到调用DLL的目的。动

态调用较之静态调用,在使用上更为复杂,但却能更加有效地使用内存,因此是编写大型应用

程序的重要方式。

    动态调用是指在应用程序中使用LoadLibrary函数或MFC提供的AfxLoadLibrary函数显

式地调入所需的动态连接库,动态连接库的文件名即上面两个函数的参数,然后再使用

GetProeAddress获取所需引入的函数。完成以上操作后,就可以像使用本应用程序自定义的函数一样来调用引入函数了。在应用程序退出之前,应该使用FreeLibrary函数或MFC提供的

AfxFreeLibrary函数来释放动态连接库。

    动态调用DLL的第l步就是调用LoadLibrary函数来加载DLL。该函数的定义如下:

    HINSTANCE LoadLibrary(  LPCTSTR ipLibFi leName)

    参数lpLibFileName用于指定DLL的文件名,并且该文件名可以包含文件名的目录。如

果不包含文件名的目录,那么该函数将遵循下面的搜索顺序来定位DLL。

  ・  包含EXE文件的目录

  ・  进程的当前工作目录

  ・  Windows系统目录

  ・  Windows目录

  ・  列在Path环境变量中的一系列目录

  成功加载DLL后,函数将返回指向该DLL的句柄,否则将返回NULL。

  成功执行第1步(加载DLL)之后,就可以执行第2个步骤了。执行该步骤的目的就是

获取DLL里的输出函数接口,可以通过GetProcAddress函数来实现该目标。GetProcAddress

函数的定义如下:

    FARPROC GetProcAddress(

    HMODULE hModule,

    LPCWSTR lpprocName);

    ・  参数hModule用于指定DLL旬柄,即LoadLibrary函数的返回值。

    ・  参数lpProcName指定想要得到的函数名称。

    如果函数执行成功,那么将返回指定函数的地址指针,否则返回NULL。如果DLL里有

N个需要获取的输出函数,就需要执行GetProcAddress函数N次,来获取这N个函数的地址。

获取DLL里的输出函数之后,直接调用输出函数即可。

    动态调用DLL的最后一个步骤就是当不再使用DLL里的输出函数时,调用FreeLibrary

函数释放DLL,该函数的定义如下:

    BOOL  FreeLibrary(

    HMODULE hLibModule)j

    参数hLibModule用于指定DLL句柄,即LoadLibrary函数的返回值。

    如果该函数成功的释放了DLL,将返回TRUE,否则返回FALSE。

12.3 dll的创建

本节将分别介绍如下3种类型的DLL动态链接库创建方法和创建过程。

    ●  Windows Embedded Compact 7  DLL。

   Windows Embedded Compact 7是指不使用MFC创建的DLL。Windows Embedded Compact 7导出函数通常使用标

C接口,这些函数可以被MFC或非MFC应用程序调用。

    ・  MFC常规DLL(动态连接MFC)。    .

    MFC常规DLL是使用MFC创建的,其导出函数也通常使用标准C接口,它们可以被

MFC或非MFC应用程序调用。按照与MFC链接方式的不同,MFC常规DLL又可以分为动

态连接和静态连接两种,前者使用MFC的动态链接库(即共享版本),后者使用MFC的静态

链接版本。

    ・  纯资源DLL。

    纯资源DLL只包含共享的资源,如菜单、字符串、图标、位图以及声音等。

    下面就分别介绍以上3类DLL动态链接库的创建方法。

12.3.1 Windows Embedded Compact 7中 dll的创建

新建一个基于“Win32智能设备项目’’的项目,将项目名称设为MyCEDLL,实现页面

如图12-1所示。

图12-1创建动态函数链接库

选择yincheng_OS,如图12-2

图12-2选择程序开发环境yincheng_OS

选择DLL,如图12-3

图12-3选择程序类

单击“finish”按钮就完成了MyCEDLL工程的创建。

下面先来了解一下非MFC的DLL工程实现原理。

    首先,每个DLL必须有一个入口点,这就如同使用C语言编写的应用程序必须有一个

WINMAlN函数一样。DllMain是一个缺省的入口函数,它负责初始化(Initialization)和结束

(Termination)工作。当一个新的进程或者该进程的新的线程访问DLL,以及访问DLL的每一

个进程或者线程不再使用此DLL时,都会调用DllMain函数。但是有一种特殊情况,那就是如

果使用TerminateProcess或TerminateThread方法结束进程或线程,就不会调用DllMain函数。

    用户只需要打开MyCEDLL.cpp文件,就可以看到DllMain函数的实现,它的函数原型

如下:

    BOOL APIENTRY DllMain( HANDLE hModule, 

                       DWORD  ul_reason_for_call, 

                       LPVOID lpReserved

 )

{

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

    return TRUE;

}

    ・  参数Moudle是动态库被调用时所传递来的一个指向自己的句柄

    ・  参数ul reason for call是一个说明动态库被调用原因的标志。当进程或者线程装载、

    卸载动态链接库的时候,操作系统便调用入口函数,并说明动态链接库被调用的原因。

    该参数可以取如下所示的值:

    >DLL PROCESS ATTACHt进程被创建。

    >DLL THREAD ATTACH:线程被创建。

    >LL PROCESS DETACH:进程被停止。

    >LL THREAD DETACH:线程被停止。

    ・  参数lpReserved是一个被系统所保留的参数。

    在理解了DllMain函数的实现原理后,就要接着考虑输出函数的实现方法了。

    输出函数需要在函数名称前加上修饰符declspec(dllexport),表示输出。此外,还有一种

修饰符extem”C”declspec(dllexport),它也表示输出,而且该类DLL不仅可以被c++调用,

还可以被c调用。在c++下定义c函数时,需要加上extem”C”关键词,用extern”C”来指明

该函数使用C的编译方式,输出的c函数可以从c代码里调用,extern”C”使得在C++中使用C编译方式成为可能。

    本示例向导完成后,会自动导出3种类型示例符号:一个是导出了一个“C++类”、一个

是导出了一个“全局变量”、另外一个导出了一个“函数”。读者可以效仿示例进行添加自定义

的导出符号。这3个示例符号定义如下:

    //此类是从MyCEDLLdll导出的

    class MYCEDLL_API CMyCEDLL{

    public

    CMyCEDLL(void)

    //TODO:在此添加您的方法

    )

    extern MYCEDLL_API int nMyCEDLL;

    函数实现代码中的修饰符MYCEDLL_APl其实就是_declspec(dllexport),因为在

MyCEDLL.h文件中含有如下宏代码:

    ifdef MYCEDLL―EXPORTS

    #define MYCEDLL―API declspec(dllexport)

    #else

    #define MYCEDLL―API declspec(dllimport)

    #endif    

    MYCEDLL_API int fnMyCEDLL(void);

    下面就来为MyCEDLL.dll动态链接库增加一个输出函数TestDll。

    首先在MyCEDLL.h头文件中添加TestDll函数的声明,代码如下:

    extern”C”MYCEDLL_API void TestDll(void)

然后在MyCEDLL.cpp文件中添加如下所示的TestDll函数的实现代码:

extern "C" MYCEDLL_API void TestDll(void)

{

MessageBox(NULL,_T("此信息来自DLL"),_T("测试所编DLL"),MB_OK);

}

    完成以上操作后,一个简单的DLL就编写完了。按下F5编译就会生成MyCEDLL.dll文件,将此文件下载到yincheng.OS\RelDir\VirtualPC_x86_Release目录下。在下面的一个小节中,将以MyCEDll.dll为例,介绍静态调用和动态调用该DLL的方法步骤。

你可能感兴趣的:(windows,微软技术)