zoukankan      html  css  js  c++  java
  • 第21章 动态链接库和钩子(1)

    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)用模块定义文件导出CC++文件中的函数——可让编译器不改动函数名

    ①新建模块定义文件,内容如下:(文件名如“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] = { '' }; //必须初始化,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); //返回转化后的字符个数(含)
        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)  //长度不含
            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); //返回转化后的字节总数(含)
        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] = '';
        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"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    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"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    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) ;
    }
  • 相关阅读:
    Well, that was fun! An adventure in WCF, SSL, and Host Headers (转)
    算法还重要吗?
    Silverlight测试——利用Ranorex实现数据驱动测试
    MSSQl分布式查询
    Silverlight 4 CHM中文离线文档已提供下载
    快速打包你的应用程序——Inno Setup
    IE 9 Beta 测试Bug
    IE 9 Bate 泄露版
    Windows PowerShell初体验——.NET对象支持
    多样化实现Windows Phone 7本地数据库访问<2>
  • 原文地址:https://www.cnblogs.com/5iedu/p/4715131.html
Copyright © 2011-2022 走看看