您的位置:首页 > 其它

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

2015-08-09 14:34 399 查看
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) ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: