浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第21章 动态链接库和钩子(1)

Posted on 2015-08-09 14:34  浅墨浓香  阅读(706)  评论(0编辑  收藏  举报

21.1 关于库的基础知识

(1)两种LIB库——都是链接时才用,链接完就不再使用了。属开发期的产物。

LIB库

区别

对象库

(也叫静态链接库)

①是多个obj文件能过Lib.exe组合成一个.lib文件。包含了实际执行代码、符号表等。链接时被加到exe文件中

②只需要使用#pragma comment(lib,"XXXX.lib")或在编译器中设置相关选项。并包含头文件到进相应的文件中。

导入库

(导入动态库时使用)

①只包含函数的地址和符号表等,确保程序静态载入动态链接库。②引入方式与静态链接库一样,只需要使用#pragma comment(lib,"XXXX.lib")或在编译器中设置相关选项,并按照头文件函数接口的声明调用函数就可以了。

③如果是动态载入,则不需要该导入库了。但要通过LoadLibrary调入DLL文件,并手工调用GetProcAddress获得对应函数。

(2)静态链接库和动态链接库的特点

名称

特点

静态链接库

缺点

①多个程序使用相同库函数时,要存多份相同的代码到各个exe中,显然浪费空间。

②如果某个函数有错或更新算法,则所有用到此函数的exe要重新编译一遍,升级麻烦。

③多个exe运行时,要载入相同的代码,浪费内存。

优点

①仅在链接时使用,链接完后,可执行文件可脱离库文件单独存在

②代码的访问速度快

动态链接库

特点

①程序运行时被载入,且内存中只保留一份代码,这份代码是通过分页机制被映射到不同进程的地址空间。但数据段仍是多份的,会被映射到不同的物理内存中,有多少个程序,就会产生多少份的数据段

②动态链接库与可执行文件的不同,仅在文件头的属性位不同而己,exe文件的一些特征,动态链接库中也有。如动态链接库也可以使用各种资源

③动态链接库是被映射到其他应用程序的地址空间的,与应用程序是“一体”的。它所拥有的资源也可被应用程序使用。它的任何操作都代表应用程序进行,当在库中打开文件、分配内存和创建窗口,这些都为应用程序所拥的。

不能独立于应用程序而单独运行

(3)库的入口点和退出点

   int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)

fdwReason

含义

DLL_PROCESS_ATTACH

①当动态链接链被映射到程序进程的地址空间时,相当于初始化信号

②在进程的整个生命周期内,只会用这个参数调用一次。如果以后的线程通过调用LoadLibraryEx函数,只会递增DLL的使用计数,而不再用这个参数去调用DllMain。

③返回值TRUE,表明初始化成功。返回FALSE,系统会终止整个进程的运行。

hInstance为动态链接库的模块实例句柄(注意不是“宿主”的实例句柄),获得该句柄的唯一途径是在入口函数被调用时,保存这个参数,否则运行时没有其他方法可获取了。

DLL_PROCESS_DETACH

①表示动态链接库将被卸载,给库提供一个自清理的机会

②同样,进程的整个生命期内,只会用这个参数调用函数一次。

注意,DLL能够阻止进程的终止。例如,当收到DLL_PROCESS_DETACH时,让程序进入一个无限循环。只有当每个DLL都处理完该通知后,操作系统才会中止进程。

DLL_THREAD_ATTACH

①表示应用程序创建了一个新的线程,系统会向当前进程中的所有DLL发送该通知。只有系统处理后这个通知,系统才允许新线程开始执行它的线程函数。

如果程序频繁的创建创建和结束线程,会以该参数和DLL_THREAD_DETACH频繁地调用该函数。

系统只会新的线程调用该函数,如果系统己经存的线程则不会再次收到该通知

主线程比较特殊,他不会调用以该参数去调用DllMain

DLL_THREAD_DETACH

①线程将要结束时,会以该参数调用DllMain。注意,此时的线程还没结束,甚到还可以发送线程消息。但不应该再使用PostMessage,因为该线程可能在消息被收取取之前就消失了。

②如果调用TerminateThread终止线程时,那么不会收到该通知。

③如果DLL被撤消时,仍有线程在运行,那么就不为任何线程调用该函数。

④该通知能阻止线程的中止

(4)动态链接链的调用

①静态调用:

#include "..\\EdrLib\\EdrLib.h"  //包含动态库的头文件

#pragma comment(lib,"..\\Debug\\EdrLib.lib") //告诉编译器DLL相对应的lib文件所在路径和文件名

  ★特点:使用方便,像使用自己内部函数一样的使用DLL中的函数。但装入DLL过程中任何错误,应用程序没有任何机会完成应变的措施,因为它根本没来得及被装入执行。

②动态调用

//注意,这里可以不包含动态库的头文件

typedef int (* lpAddFun)(int ,int);//定义一个动态库里的AddNew的函数指针。

  lpAddFun  addFun; //函数指针

  hDll = LoadLibrary("EdrLib.dll");//动态加载DLL模块句柄

  addFun = (lpAddFun)GetProcAddress(hDll, "AddNew");//得到函数的地址

  ……

  addFun(2,3);

  ……

  if(hDll) FreeLibrary(hDll);//释放已经加载的DLL模块。

  ★特点:因为事先不需要知道动态库文件名和函数名,所以也就不再需要根据导入库中的dll文件名和函数名写入exe文件头的导入表中,所以就不再需要#include动态库的头文件和.lib导入库。需要使用动态库时才由应用程序通过LoadLibrary装入,如果动态库不存在会返回NULL,这时错误是可控的。

21.2 动态链接库导出函数名称问题

(1)DLL导出函数名称的关系图

 

(2)用模块定义文件导出C\C++文件中的函数——可让编译器不改动函数名

①新建模块定义文件,内容如下:(文件名如“EdrLib.def”)

;模块定义文件里的注释是用分号的
LIBRARY EdrLib     ;EdrLib为动态链接库的名称

EXPORTS

EdrCenterTextA  @1     ;@1,为函数的编序号

EdrCenterTextW  @2

②设置链接时依赖文件

  方法:“项目属性”→“配置属性”→“链接器”→“输入”→“模块定义文件”→输入“EdrLib.def”(不包含引号)

(3)VC 中默认调用是 __cdecl 方式,Windows API 使用 __stdcall 调用方式。在 DLL 导出函数中,为了跟 Windows API 保持一致,一般使用 __stdcall方式。如果调用约定仍采用C默认的方式不变,则按模块定义文件或_declspec(dllexport)导出都能使C文件(注意,不是C++文件)的函数名保持不变。但如果调用约定发生改变时,即使使用extern "C",编译后的函数名还是会发生改变。例如上面我们加入_stdcall关键字说明调用约定。

 【EdrTest程序】
  EdrLib.dll链接库

//Edrlib.h

/*--------------------------------------------------------
EDRLIB.H    header file
--------------------------------------------------------*/
#pragma once;
#include <windows.h>

#ifdef _cplusplus
    #ifdef API_EXPORT 
        #define EXPORT   extern "C" __declspec(dllexport)          //当头文件供动态库本身使用时
    #else
        #define EXPORT    extern "C" __declspec(dllimport)         //当头文件供调用库的程序使用时
    #endif
#else
    #ifdef API_EXPORT 
        #define EXPORT   __declspec(dllexport)                        //当头文件供动态库本身使用时
    #else
        #define EXPORT   __declspec(dllimport)                        //当头文件供调用库的程序使用时
    #endif
#endif

EXPORT BOOL  CALLBACK EdrCenterTextA(HDC, PRECT, PCSTR);
EXPORT BOOL  CALLBACK EdrCenterTextW(HDC, PRECT, PCWSTR);

#ifdef UNICODE
#define EdrCenterText   EdrCenterTextW
#else
#define EdrCenterText   EdrCenterTextA
#endif

//EdrLib.c

/*--------------------------------------------------------
   EDRLIB.C —— Easy Drawing Routine Library module
                (c) Charles Petzold,1998
--------------------------------------------------------*/
#include <Windows.h>

#define API_EXPORT
#include "EdrLib.h"

//入口和退出点
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    return TRUE;
}

EXPORT BOOL CALLBACK EdrCenterTextA(HDC hdc, PRECT prc, PCSTR pString)
{
    int iLength;
    SIZE size;

    iLength = lstrlenA(pString);
    GetTextExtentPoint32A(hdc, pString, iLength, &size);

    return TextOutA(hdc, (prc->right - prc->left - size.cx) / 2,
                         (prc->bottom - prc->top - size.cy) / 2,
                         pString,iLength);
}

EXPORT BOOL CALLBACK EdrCenterTextW(HDC hdc, PRECT prc, PCWSTR pString)
{
    int iLength;
    SIZE size;

    iLength = lstrlenW(pString);
    GetTextExtentPoint32W(hdc, pString, iLength, &size);

    return TextOutW(hdc, (prc->right - prc->left - size.cx) / 2,
        (prc->bottom - prc->top - size.cy) / 2,
        pString, iLength);
}

//EdrTest测试文件

/*------------------------------------------------------------
   EDRTEST.C -- Program using EDRLIB dynamic-link library
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
//-----------------------静态调用(隐式链接)-------------------
//#include "..\\EdrLib\\EdrLib.h"
//#pragma comment(lib,"..\\Debug\\EdrLib.lib") //动态链接库的导入库

//-----------------------动态调用(显式链接)--------------------
#ifdef UNICODE
  typedef BOOL (CALLBACK *PEDRCENTERTEXT)(HDC, PRECT, PCWSTR);
  #define EdrCenterText   "EdrCenterTextW"
#else
  typedef BOOL (CALLBACK *PEDRCENTERTEXT)(HDC, PRECT, PCSTR);
  #define EdrCenterText   "EdrCenterTextA"
#endif

//---------------------------------------------------------

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("StrProg") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("DLL Demonstration Program"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
    
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;
     static HANDLE   hDll;  //供动态调用DLL使用
     static PEDRCENTERTEXT pEdrCenterText; //供动态调用DLL使用

     switch (message)
     {
     case WM_CREATE:

         //动态调用(显式链接)
          hDll = LoadLibrary(TEXT("EdrLib.dll"));
          if (hDll)
              pEdrCenterText = (PEDRCENTERTEXT)GetProcAddress(hDll, EdrCenterText);
          return 0 ;
          
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          GetClientRect (hwnd, &rect) ;
          
          //静态调用(隐式链接)
          //EdrCenterText(hdc, &rect, TEXT("This string was displayed by a DLL")); 

          //动态调用(显式链接)
          pEdrCenterText(hdc, &rect, TEXT("This string was displayed by a DLL"));
          
          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
         if (hDll)
             FreeLibrary(hDll);

          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

 21.3 在DLL中共享内存

(1)动态链接库存在于不同进程的地址空间,它们代表“宿主”程序工作,其代码段会被不同进程共享,但数据段会被映射到不同的物理内存,也就是这些数据是不能共享的,但有时我们需要数据在程序间共享,怎么办?

(2)创建数据共享区的步骤

  ①声明并创建的一个数据区——表现在dll文件中会出现一个自定义的myshared数据区

   #pragma data_seg(“myshared”)

   int iTotal =0;   //一定要初始化,否则被编译到未初始化区,而不是myshared区

   WCHAR szString[MAX_STRING]={‘\0’};  //一定要初始化。

   #pragma data_seg();  //数据区定义结束

  ②将数据区设为可读、可写和可共享的属性

  #pragma comment(linker, "/SECTION:myshared,RWS")

【StrProg程序】

 效果图 

//DLL动态链接库代码

/*------------------------------------------------------
     STRLIB.H  header file
-------------------------------------------------------*/
#pragma once

#include <Windows.h>

#ifdef __cplusplus
    #ifdef API_EXPORT
        #define EXPORT  extern "C" __declspec(dllexport)
    #else
        #define EXPORT  extern "C" __declspec(dllimport)
    #endif
#else
    #ifdef API_EXPORT
        #define EXPORT   __declspec(dllexport)
    #else
        #define EXPORT   __declspec(dllimport)
    #endif
#endif

//字符串的最大行数和字符长度
#define MAX_STRINGS  256
#define MAX_LENGTH   63

//用于显示字符串的函数(回调函数,由调用者决定如何显示)
/*
   函数指针名称:GETSTRCB,其中CB表示CALLBACK
   第1个参数:为指向要显示的字符串的指针
   第2个参数:指定自定义的数据结构的指针,用来决定如何显示或处理字符串
*/
typedef BOOL(CALLBACK* GETSTRCB)(PCTSTR, PVOID);


//每个函数都有两个版本:ANSI和UNICODE
EXPORT BOOL CALLBACK  AddStringA(PCSTR);
EXPORT BOOL CALLBACK  AddStringW(PCWSTR);

EXPORT BOOL CALLBACK  DeleteStringA(PCSTR);
EXPORT BOOL CALLBACK  DeleteStringW(PCWSTR);

EXPORT int CALLBACK  GetStringA(GETSTRCB,PVOID);
EXPORT int CALLBACK  GetStringW(GETSTRCB, PVOID);

//根据UNICODE标识符的情况来使用相应版本的函数
#ifdef UNICODE
#define AddString     AddStringW
#define DeleteString  DeleteStringW
#define GetStrings    GetStringW
#else 
#define AddString     AddStringA
#define DeleteString  DeleteStringA
#define GetStrings    GetStringA
#endif

//StrLib.c

/*-----------------------------------------------------------
    STRLIB.C —— Library module for STRPPROG program
                 (c)Charles Petzold,1998
-----------------------------------------------------------*/
#define API_EXPORT

#include <Windows.h>
#include "StrLib.h"

//创建DLL共享内存,会在DLL文件中创新一个新的数据区,名为"myshared"区,就像
//一般的文件有代码区、常量区等。
//(需要链接选项: /SECTION:myshared,RWS)
#pragma data_seg(".myshared")  //共享数据区定义开始
int iTotal = 0;    //必须初始化,否则分配在未初始化数据区中,该变量用于存在当前的字符串(串数)(不是每个字符串的字符个数)
WCHAR szStrings[MAX_STRINGS][MAX_LENGTH + 1] = { '\0' }; //必须初始化,MAX_STRINGS行,MAXLENGTH+1列
#pragma data_seg()            //共享数据区定义结束

#pragma comment (linker,"/SECTION:.myshared,RWS")  //设为可读写、可共享属性

//#pragma comment(linker,"/DEF:strLib.def" )

int WINAPI  DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    return TRUE;
}


EXPORT BOOL CALLBACK  AddStringA(PCSTR pStringIn)
{
    BOOL bReturn=FALSE;
    int iLength;
    PWSTR pWideStr;

    //将字符串转为UNICODE编码,并调用AddStringW函数
    iLength = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); //返回转化后的字符个数(含\0)
    pWideStr = malloc(iLength*sizeof(WCHAR)); //课本这里有误,因为返回的是字符个数,而不是缓冲区大小
    MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iLength); //iLength缓冲最多能存储的字符数
    bReturn = AddStringW(pWideStr);

    free(pWideStr);
    return bReturn;
}

EXPORT BOOL CALLBACK  AddStringW(PCWSTR pStringIn)
{
    PWSTR pString;
    int i, iLength;

    if (iTotal == MAX_STRINGS - 1)  //如果当前字符串的个数己经达到最大,则不再增加
        return TRUE;
        
    //为字符串分配内存,复制字符串并转为大写
    if ((iLength = wcslen(pStringIn)) == 0)  //长度不含\0
        return FALSE;

    pString = (PWSTR)malloc(sizeof(WCHAR)*(1 + iLength)); 
    wcscpy_s(pString, iLength + 1, pStringIn);
    _wcsupr_s(pString, iLength+1);  //将字符串转为大写

    //比较输入的字符串与DLL共享区中各字符串的大写,并按字母表的顺序进行排序
    for  ( i=iTotal; i >0; i--)
    {
        if (wcscmp(pString, szStrings[i - 1]) > 0) //从最后面往前比较,如果pString较大,就跳出
            break;

        //如果pString较小,则szStrings[i-1]后移,腾出位置出来。
        wcscpy_s(szStrings[i], wcslen(szStrings[i-1])+1, szStrings[i-1]);  
    }
    wcscpy_s(szStrings[i], iLength + 1,pString); //将pStringIn加入到数组中合适的位置
    iTotal++;

    free(pString);
    pString = NULL;
    return TRUE;
}

EXPORT BOOL CALLBACK  DeleteStringA(PCSTR pStringIn)
{
    BOOL bReturn;
    int iLength;
    PWSTR pWideStr;

    //将字符串转为UNICODE编码,并调用DeleteStringW函数
    iLength = MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, NULL, 0); //返回转化后的字节总数(含\0)
    pWideStr = malloc(iLength*sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, pStringIn, -1, pWideStr, iLength);

    bReturn = DeleteStringW(pWideStr);
    free(pWideStr);
    return bReturn;
}

EXPORT BOOL CALLBACK  DeleteStringW(PCWSTR pStringIn)
{
    int i, j;

    if (0 == wcslen(pStringIn))
        return FALSE;

    for (i = 0; i < iTotal; i++)
    {
        //查找pStringIn字符串,在数组中的位置(_wcsicmp不区分大小写)
        if (_wcsicmp(szStrings[i], pStringIn) == 0) 
            break;
    }
    
    //i返回索引位置
    if (i == iTotal)  //如果找不到,则返回
        return FALSE;

    //将i后面的元素往前移
    for (j =i; j< iTotal;j++)
    {
        wcscpy_s(szStrings[j],wcslen(szStrings[j+1])+1,szStrings[j+1]);
    }

    szStrings[iTotal--][0] = '\0';
    return TRUE;
}

//其中pfnGetStrCallBack为回调函数,用于用户自己决定如果显示或处理字符串,pParam为附加参数
EXPORT int CALLBACK  GetStringA(GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
    BOOL bReturn;
    int i, iLength;
    PSTR pAnsiStr;

    //枚举各串字符串
    for (i = 0; i < iTotal; i++)
    {
        //转化为UNICODE
        iLength = WideCharToMultiByte(CP_ACP, 0, szStrings[i], -1, NULL, 0, NULL, NULL);
        pAnsiStr = malloc(iLength);
        WideCharToMultiByte(CP_ACP, 0, szStrings[i], -1, pAnsiStr, iLength, NULL, NULL);

        //调用回调函数,并传入附回参数
        bReturn = pfnGetStrCallBack((PCTSTR)pAnsiStr, pParam);

        free(pAnsiStr);
        if (bReturn == FALSE) //如果错误,返回错误的行号
            return i + 1;
    }
    return iTotal;
}

//其中pfnGetStrCallBack为回调函数,用于用户自己决定如果显示或处理字符串,pParam为附加参数
EXPORT int CALLBACK  GetStringW(GETSTRCB pfnGetStrCallBack, PVOID pParam)
{
    BOOL bReturn;
    int i;

    for (i = 0; i < iTotal; i++)  //枚举各串字符串,并在回调函数中进行处理。
    {
        bReturn = pfnGetStrCallBack(szStrings[i], pParam);
        if (bReturn == FALSE) //如果错误,返回错误的行号
            return i + 1;  
    }

    return bReturn;
}

//StrProg测试程序

/*------------------------------------------------------------
   STRPROG.C -- Program using STRLIB dynamic-link library
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include "resource.h"
#include "..\\StrLib\\StrLib.h"

#pragma comment(lib,"..\\debug\\Strlib.lib")

//回调函数的参数(结构体)
//将DLL共享区中若干行和列来显示,其中每行高度yIncr(cyChar),
//每列宽度xIncr(cxChar*MAXLENGTH)
typedef struct
{
    HDC hdc;
    int xText; //当前的输出位置(x坐标)
    int yText; //当前的输出位置(y坐标)
    int xStart;//字符串输出的起始x坐标
    int yStart;//字符串输出的起始y坐标
    int xIncr; //cxChar * MAX_LENGTH,每列的宽度
    int yIncr; //每次输出时,每行的高度(cyChar)
    int xMax; //xIncr*(1+cxClient/xIncr);   最大的列数
    int yMax; //cyChar*(cyClient /cyChar-1);最大的行数
}CBPARAM;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT("StrProg");
TCHAR szString[MAX_LENGTH + 1];

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("DLL Demonstration Program"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }

     return msg.wParam ;
}

//显示字符串的回调函数,GetString函数来回调使用
BOOL  CALLBACK GetStrCallBack(PCTSTR pString, CBPARAM* pcbp)
{
    //每成若干行和列来显示DLL共享区中的每一条字符串。
    TextOut(pcbp->hdc, pcbp->xText, pcbp->yText, pString, lstrlen(pString));

    //下一行的位置
    pcbp->yText += pcbp->yIncr;
    if (pcbp->yText >pcbp->yMax)  //如果该列己经显示满了,计算新的行和列
    {
        pcbp->yText = pcbp->yStart;  //新的一行的位置,回到起始行
        pcbp->xText += pcbp->xIncr;  //新的一列的位置

        if (pcbp->xText > pcbp->xMax) //如果列数太多(超过了显示区域范围了),
                                      //则不显示忽略该字符串
            return FALSE;
    }
    return TRUE;
}

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            GetDlgItemText(hDlg, IDC_STRING, szString, MAX_LENGTH);
            EndDialog(hDlg, TRUE);
            return TRUE;

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return TRUE;
        }
    }
    return FALSE;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HINSTANCE hInst;
     HDC         hdc ;
     PAINTSTRUCT ps ;
     static int cxClient, cyClient,cxChar,cyChar;
     CBPARAM cbparam;
     static UINT iDataChangeMsg;

     switch (message)
     {
     case WM_CREATE:
          hInst = ((LPCREATESTRUCT)lParam)->hInstance;
          cxChar = LOWORD(GetDialogBaseUnits());
          cyChar = HIWORD(GetDialogBaseUnits());

          //注册消息,但数据发生变化时,进行广播通知所有实例。
          iDataChangeMsg = RegisterWindowMessage(TEXT("StrProgDataChange"));
          return 0 ;

     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);
         return 0;

     case WM_COMMAND:
         switch (LOWORD(wParam))
         {
         case IDM_ENTER:
             if (DialogBox(hInst, TEXT("EnterDlg"), hwnd, &DlgProc))
             {
                 if (AddString(szString))
                     PostMessage(HWND_BROADCAST, iDataChangeMsg, 0, 0);
                 else
                     MessageBeep(0);
             }
             break;

         case IDM_DELETE:
             if (DialogBox(hInst, TEXT("DeleteDlg"), hwnd, &DlgProc))
             {
                 if (DeleteString(szString))
                     PostMessage(HWND_BROADCAST, iDataChangeMsg, 0, 0); //广播通知各个实例
                 else
                     MessageBeep(0);
             }
             break;
         }
         break;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          cbparam.hdc = hdc;
          cbparam.xText = cbparam.xStart = cxChar;
          cbparam.yText = cbparam.yStart = cyChar;
          cbparam.xIncr = cxChar*MAX_LENGTH; //每列的宽度
          cbparam.yIncr = cyChar;            //每行的高度

          //cxClient/cbparam.xIncr表示总列数(取整数部分),加1能保
          //证当总列数不满客户区宽度时,剩余的客户区宽度里也要充分利用,断续
          //输出(当然,这将导致部分文字被剪裁,也就是最后一列字符串不完整了。
          cbparam.xMax  = cbparam.xIncr*(1 + cxClient / cbparam.xIncr);
          cbparam.yMax  = cyChar*(cyClient / cyChar -1 );
          
          GetStrings((GETSTRCB)GetStrCallBack, (PVOID)&cbparam);
         
          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
          
     default:
         if (message == iDataChangeMsg)
             InvalidateRect(hwnd, NULL, TRUE);
         break;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 StrProg.rc 使用
//
#define IDC_STRING                      1001
#define IDM_ENTER                       40001
#define IDM_DELETE                      40002
#define IDC_STATIC                      -1

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40005
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//strProg.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

STRPROG MENU
BEGIN
    MENUITEM "&Enter!",                     IDM_ENTER
    MENUITEM "&Delete!",                    IDM_DELETE
END


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

ENTERDLG DIALOGEX 0, 0, 227, 78
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Enter"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,46,46,50,14
    PUSHBUTTON      "取消",IDCANCEL,130,46,50,14
    LTEXT           "&Enter:",IDC_STATIC,27,20,21,8
    EDITTEXT        IDC_STRING,61,18,144,14,ES_AUTOHSCROLL
END

DELETEDLG DIALOGEX 0, 0, 221, 60
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Delete"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,46,39,50,14
    PUSHBUTTON      "取消",IDCANCEL,130,39,50,14
    LTEXT           "&Enter:",IDC_STATIC,22,12,21,8
    EDITTEXT        IDC_STRING,56,10,144,14,ES_AUTOHSCROLL
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    "ENTERDLG", DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 220
        TOPMARGIN, 7
        BOTTOMMARGIN, 71
    END

    "DELETEDLG", DIALOG
    BEGIN
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

 

21.4 关于动态链接库的其他问题

(1)动态库不接收消息,但可以调用GetMessage或PeekMessage从消息队列中获取消息,这些消息是来自“宿主”程序的消息。

(2)动态链接库的静态载入方法——以调用Rectangle为例

默认情况下,程序自动加入了GDI32.LIB(导入库)这一选项(可以在项目属性中看到GDI32.LIB、USER32.LIB、Kernel32.LIB等附加依赖项)。当正常调用Rectangle时,程序会从GDI32.LIB中找到该函数地址,并提供给调用程序,即采用静态载入(静态载入或叫隐式载入动态链接库的方法)。

(3)动态链接库的动态载入方法——适用于没用导入库的DLL。(Rectangle另类调用方法)

//回调函数
typedef BOOL(WINAPI* PFNRECTANGLE)(HDC, int, int, int, int);

HANDLE hLibrary;
PFNRECTANGLE pfnRectangle;

hLibrary = LoadLibrary(TEXT("GDI32.DLL")); //动态载入DLL

//获取Rectangle函数的地址
pfnRectangle = (PFNRECTANGLE)GetProcAddress(hLibrary, TEXT("Rectangle"));
 
pfnRectangle(hdc, xLeft, yTop, xRight, yBottom); //绘图
 
FreeLibrary(hLibrary); //释放动态链接库

(4)资源库

【ShowBit程序】

 效果图

//BitLib.Dll

/*-------------------------------------------------------------------------------
    BITLIB.C —— Code entry point for BITLIB dynamic-link library
                  (c)Charles Petzold,1998
-------------------------------------------------------------------------------*/

#include <Windows.h>

int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    return TRUE;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 BitLib.rc 使用
//

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        110
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//BitLib.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

1             BITMAP                  "BITMAP1.BMP"
2             BITMAP                  "BITMAP2.BMP"
3             BITMAP                  "BITMAP3.BMP"
4             BITMAP                  "BITMAP4.BMP"
5             BITMAP                  "BITMAP5.BMP"
6             BITMAP                  "BITMAP6.BMP"
7             BITMAP                  "BITMAP7.BMP"
8             BITMAP                  "BITMAP8.BMP"
9             BITMAP                  "BITMAP9.BMP"
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

 //ShowBit测试程序

/*------------------------------------------------------------
   SHOWBIT.C -- Shows bitmaps in BITLIB dynamic-link library
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
 
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

TCHAR szAppName[] = TEXT("ShowBit");
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("Show Bitmaps from BITLIB(Press Key)"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

void DrawBitmap(HDC hdc, int xStart, int yStart, HBITMAP hBitmap)
{
    BITMAP bm;
    HDC hMemDC;

    GetObject(hBitmap, sizeof(BITMAP), &bm);

    hMemDC = CreateCompatibleDC(hdc);
    SelectObject(hMemDC, hBitmap);

    BitBlt(hdc,xStart, yStart,bm.bmWidth,bm.bmHeight ,hMemDC,0,0,SRCCOPY);

    DeleteDC(hMemDC);
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc ;
     PAINTSTRUCT ps ;

     static HINSTANCE hLibrary;
     static int iCurrentIndex;
     HBITMAP   hBitmap;

     switch (message)
     {
     case WM_CREATE:
          hLibrary = LoadLibrary(TEXT("BitLib.dll"));
          if (NULL==hLibrary)
          {
              MessageBox(hwnd, TEXT("Can't load BitLib.dll"), szAppName, 0);
              return -1;
          }
          return 0 ;
          
     case WM_CHAR:
         if (hLibrary)
         {
             iCurrentIndex++;
             iCurrentIndex %= 9;
             InvalidateRect(hwnd, NULL, TRUE);
         }
         return 0;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          if (hLibrary)
          {
              //第1个参数为hLibrary,表示从动态库中加载资源
              hBitmap = LoadBitmap(hLibrary, MAKEINTRESOURCE(iCurrentIndex+1));

              if (hBitmap)
              {
                  DrawBitmap(hdc, 0, 0, hBitmap);
                  DeleteObject(hBitmap);
              }
          }

          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          if (hLibrary)
               FreeLibrary(hLibrary);
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}