??xml version="1.0" encoding="utf-8" standalone="yes"?>
一、在应用E序自定义消息方法:
一般自定义消息有一定的范围Q虽然说自定义消息从WM_USER开始,但是׃我们的工E里面一般还有很多其他的控gQ他们也要占用一部分WM_USER消息范围Q所以我们必Mؓ他们留出一部分范围Q这里,我们保留100个消息,一般情况下Q这可以满我们的要求?/p>
(1) 定义消息的倹{在我们要发生消息的地方(例如CMyView.cpp的开始部?或者stdafx..h文gQ进行如下定义:
#define WM_MSG (WM_USER+101) |
接下来的工作其实很简单,我们在前面说了,消息正常工作?个部分必d调:消息声明、消息映、消息体。我们就一ơ进行手工加入?/p>
(2)首先在AFX_MSG块中加入消息声明Q在CMyView.h中,扑ֈ如下部分Qƈ加入消息声明Q?/p>
protected: // {{AFX_MSGQCMyView) ...... afx_msg LRESULT OnMyMsg(WPARAM wParam,LPARAM lParam); |
(3)在MESSAGE_MAP块中dON_MESSAGE宏指令:
BEGIN_MESSAGE_MAP(CMyView, CView) ..... ON_MESSAGE(WM_MSG, OnMyMsg) END_MESSAGE_MAP() |
(4)d消息函数体:
LPESULT CMyView::OnMyMsg(WPARAM wParam, LPARAM lParam)
{ |
AfxMessageBox("消息已经收到Q?); return 0; } |
消息x已l定义完毕,接下来我们就可以ȀzL息了Q就可以用我们前面所说的PostMessage/SendMessage来发送消息了?/p>
如:::PostMessage(hwnd,WM_MSG,0,1);
PostMessage:不用{消息返回?/p>
SendMessage:需要等消息q回?/p>
二、从DLL中传递消息到EXE
在DLL中定义消息和上面的方法很怼Q有两点不同的地方:
1?nbsp; 在DLL和应用程序中两个地方定义相同的消息?/p>
2?nbsp; 应用E序调用DLLE序之后Q需要将应用E序的窗口句柄传递给DLLQ以便DLL中的消息q回?/p>
在DLL工程中:
Q?Q?nbsp; 在stdafx.h头文件中d消息定义Q?/p>
#define WM_MSG WM_USER + 102 |
Q?Q?nbsp; d启动消息的输出函敎ͼ
CMessageDLLApp theApp; //发送消?/p> extern "C" _declspec(dllexport) void StartSendMessage(HWND hwnd) { theApp.SendMessage(hwnd); } |
其中hwnd是接收消息的H口句柄?/p>
Q?Q?nbsp; d启动消息的实现函敎ͼ
在头文g中添加函数声明:
void SendMessage(HWND hwnd); |
在CPP文g中添加函数实?/p>
//启动发送消?/p>
void CMessageDLLApp::SendMessage(HWND hwnd) { ::PostMessage(hwnd,WM_MSG,0,1); } 在应 |
用程序中Q?/p>
Q?Q?nbsp; 在stdafx.h头文件中d消息定义Q?/p>
#define WM_MSG WM_USER + 102 |
Q?Q?nbsp; 首先在AFX_MSG块中加入消息声明Q在CTestMessageDLLDlg.h中,扑ֈ如下部分Qƈ加入消息声明Q?/p>
。。。。。?/p>
afx_msg LRESULT OnMyMsg(WPARAM wParam,LPARAM lParam); DECLARE_MESSAGE_MAP() |
Q?Q?nbsp; 在MESSAGE_MAP块中dON_MESSAGE宏指令:
BEGIN_MESSAGE_MAP(CTestMessageDLLDlg, CDialog) 。。。。。?/p> ON_MESSAGE(WM_MSG, OnMyMsg) //}}AFX_MSG_MAP END_MESSAGE_MAP() |
Q?Q?nbsp; d消息函数体:
LRESULT CTestMessageDLLDlg::OnMyMsg(WPARAM wParam, LPARAM lParam)
{ AfxMessageBox("消息已经收到Q?); return 0; } |
Q?Q?nbsp; 在对话框上添加一个按钮,在按钮事件中Q先调用DLL文gQ然后发送一个测试消息响应的命oQ?/p>
void CTestMessageDLLDlg::OnBnClickedButton1() { // TODO: 在此d控g通知处理E序代码 //定义函数 typedef void (_cdecl*STARTSENDMESSAGE)(HWND hwnd); HMODULE hmessage = NULL; STARTSENDMESSAGE StartSendMessage = NULL; //导入DLL库文?/p> hmessage = LoadLibrary("MessageDLL.dll"); if(hmessage==NULL) { FreeLibrary(hmessage); exit(0); } //导入DLL中测试消息函?/p> StartSendMessage = (STARTSENDMESSAGE)GetProcAddress(hmessage,"StartSendMessage"); if(StartSendMessage==NULL) { FreeLibrary(hmessage); exit(1); } //获取对话框的H口句柄 HWND hwnd = this-QGetSafeHwnd(); //发送测试消息函?/p> (*StartSendMessage)(hwnd); } |
q行应用E序Q就可以看到试l果了?br>
?2 MFC规则DLL的调用例?/P>
下面是“调用DLL”按钮单M件的消息处理函数Q?BR>
void CRegularDllCallDlg::OnCalldllButton()
{
typedef void (*lpFun)(void);
HINSTANCE hDll; //DLL句柄
hDll = LoadLibrary("RegularDll.dll");
if (NULL==hDll)
{
MessageBox("DLL加蝲p|");
}
lpFun addFun; //函数指针
lpFun pShowDlg = (lpFun)GetProcAddress(hDll,"ShowDlg");
if (NULL==pShowDlg)
{
MessageBox("DLL中函数寻扑֤?);
}
pShowDlg();
}
#pragma comment(lib,"RegularDll.lib")
void ShowDlg(void);
void CRegularDllCallDlg::OnCalldllButton()
{
ShowDlg();
}
?1 MFC规则DLL例子
在DLL中添加对话框的方式与在MFC应用E序中是一L?BR>
在图11所CDLL中的对话框的Hello按钮上点LMessageBox一个“Hello,pconline的网友”对话框Q下面是相关的文件及源代码,其中删除了MFC向导自动生成的绝大多数注释?BR>W一l文ӞCWinAppl承cȝ声明与实?BR>
// RegularDll.h : main header file for the REGULARDLL DLL
#if !defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)
#define AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // main symbols
class CRegularDllApp : public CWinApp
{
public:
CRegularDllApp();
DECLARE_MESSAGE_MAP()
};
#endif
// RegularDll.cpp : Defines the initialization routines for the DLL.
#include "stdafx.h"
#include "RegularDll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
BEGIN_MESSAGE_MAP(CRegularDllApp, CWinApp)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRegularDllApp construction
CRegularDllApp::CRegularDllApp()
{
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CRegularDllApp object
CRegularDllApp theApp;
分析Q?BR>
在这一l文件中定义了一个承自CWinApp的类CRegularDllAppQƈ同时定义了其的一个实例theApp。乍一看,您会以ؓ它是一个MFC应用E序Q因为MFC应用E序也包含这L在工E名后添加“App”组成类名的c(q承自CWinAppc)Q也定义了这个类的一个全局实例theApp?BR>我们知道Q在MFC应用E序中CWinApp取代了SDKE序中WinMain的地位,SDKE序WinMain所完成的工作由CWinApp的三个函数完成:
virtual BOOL InitApplication( );
virtual BOOL InitInstance( );
virtual BOOL Run( ); //传说中MFCE序的“活水源头?/P>
但是MFC规则DLLq不是MFC应用E序Q它所l承自CWinApp的类不包含消息@环。这是因为,MFC规则DLL不包含CWinApp::Run机制Q主消息泵仍然由应用E序拥有。如果DLL 生成无模式对话框或有自己的主框架H口Q则应用E序的主消息泵必调用从DLL 导出的函数来调用PreTranslateMessage成员函数?BR>
另外QMFC规则DLL与MFC 应用E序中一P需要将所?DLL中元素的初始化放到InitInstance 成员函数中?BR>
W二l文?自定义对话框cd明及实现(点击查看附g)
分析Q?BR>
q一部分的编E与一般的应用E序Ҏ没有什么不同,我们照样可以利用MFCcd导来自动为对话框上的控gd事g。MFCcd导照样会生成cMON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映宏?BR>
W三l文?DLL中的资源文g
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by RegularDll.rc
//
#define IDD_DLL_DIALOG 1000
#define IDC_HELLO_BUTTON 1000
分析Q?BR>
在MFC规则DLL中用资源也与在MFC应用E序中用资源没有什么不同,我们照样可以用Visual C++的资源编辑工兯行资源的d、删除和属性的更改?BR>
W四l文?MFC规则DLL接口函数
#include "StdAfx.h"
#include "DllDialog.h"
extern "C" __declspec(dllexport) void ShowDlg(void)
{
CDllDialog dllDialog;
dllDialog.DoModal();
}
分析Q?BR>
q个接口q不使用MFCQ但是在其中却可以调用MFC扩展cCdllDialog的函敎ͼq体C“规则”的概类?BR>
与非MFC DLL完全相同Q我们可以用__declspec(dllexport)声明或在.def中引出的方式导出MFC规则DLL中的接口?BR>
? MFC DLL工程的创?/P>
?0所C对话框中的1区选择MFC DLL的类别?BR>
2区选择是否支持automationQ自动化Q技术, automation 允许用户在一个应用程序中操纵另外一个应用程序或lg。例如,我们可以在应用程序中利用 Microsoft Word 或Microsoft Excel的工P而这U用对用户而言是透明的。自动化技术可以大大简化和加快应用E序的开发?BR>
3区选择是否支持Windows SocketsQ当选择此项目时Q应用程序能?TCP/IP |络上进行通信?CWinAppzcȝInitInstance成员函数会初始化通讯端的支持Q同时工E中的StdAfx.h文g会自动include <AfxSock.h>头文件?BR>dsocket通讯支持后的InitInstance成员函数如下Q?BR>
BOOL CRegularDllSocketApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}
return TRUE;
}
4区选择是否由MFC向导自动在源代码中添加注释,一般我们选择“Yes,please”?BR>
?0 MFC DLL的创建选项
//文g名:point.hQpointcȝ声明
#ifndef POINT_H
#define POINT_H
#ifdef DLL_FILE
class _declspec(dllexport) point //导出cpoint
#else
class _declspec(dllimport) point //导入cpoint
#endif
{
public:
float y;
float x;
point();
point(float x_coordinate, float y_coordinate);
};
#endif
//文g名:point.cppQpointcȝ实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "point.h"
//cpoint的缺省构造函?BR>
point::point()
{
x = 0.0;
y = 0.0;
}
//cpoint的构造函?BR>
point::point(float x_coordinate, float y_coordinate)
{
x = x_coordinate;
y = y_coordinate;
}
//文g名:circle.hQcirclecȝ声明
#ifndef CIRCLE_H
#define CIRCLE_H
#include "point.h"
#ifdef DLL_FILE
class _declspec(dllexport)circle //导出ccircle
#else
class _declspec(dllimport)circle //导入ccircle
#endif
{
public:
void SetCentre(const point rePoint);
void SetRadius(float r);
float GetGirth();
float GetArea();
circle();
private:
float radius;
point centre;
};
#endif
//文g名:circle.cppQcirclecȝ实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "circle.h"
#define PI 3.1415926
//circlecȝ构造函?BR>
circle::circle()
{
centre = point(0, 0);
radius = 0;
}
//得到圆的面积
float circle::GetArea()
{
return PI *radius * radius;
}
//得到圆的周长
float circle::GetGirth()
{
return 2 *PI * radius;
}
//讄圆心坐标
void circle::SetCentre(const point rePoint)
{
centre = centrePoint;
}
//讄圆的半径
void circle::SetRadius(float r)
{
radius = r;
}
cȝ引用Q?BR>
#include "..\circle.h" //包含cd明头文g
#pragma comment(lib,"dllTest.lib");
int main(int argc, char *argv[])
{
circle c;
point p(2.0, 2.0);
c.SetCentre(p);
c.SetRadius(1.0);
printf("area:%f girth:%f", c.GetArea(), c.GetGirth());
return 0;
}
从上q源代码可以看出Q由于在DLL的类实现代码中定义了宏DLL_FILEQ故在DLL的实C所包含的类声明实际上ؓQ?BR>
class _declspec(dllexport) point //导出cpoint
{
?BR>
}
?BR>
class _declspec(dllexport) circle //导出ccircle
{
?BR>
}
而在应用工程中没有定义DLL_FILEQ故其包含point.h和circle.h后引入的cd明ؓQ?BR>
class _declspec(dllimport) point //导入cpoint
{
?BR>
}
?BR>
class _declspec(dllimport) circle //导入ccircle
{
?BR>
}
/* 文g名:lib.h */
#ifndef LIB_H
#define LIB_H
extern int dllGlobalVar;
#endif
/* 文g名:lib.cpp */
#include "lib.h"
#include <windows.h>
int dllGlobalVar;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
dllGlobalVar = 100; //在dll被加载时Q赋全局变量?00
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
;文g名:lib.def
;在DLL中导出变?BR>
LIBRARY "dllTest"
EXPORTS
dllGlobalVar CONSTANT
;或dllGlobalVar DATA
GetGlobalVar
变量名 CONSTANT //q时的方?/P>
?BR>
变量名 DATA //VC++提示的新Ҏ
在主函数中引用DLL中定义的全局变量Q?BR>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern int dllGlobalVar;
int main(int argc, char *argv[])
{
printf("%d ", *(int*)dllGlobalVar);
*(int*)dllGlobalVar = 1;
printf("%d ", *(int*)dllGlobalVar);
return 0;
}
特别要注意的是用extern int dllGlobalVar声明所导入的ƈ不是DLL中全局变量本nQ而是其地址Q应用程序必通过强制指针转换来用DLL中的全局变量。这一点,?(int*)dllGlobalVar可以看出。因此在采用q种方式引用DLL全局变量Ӟ千万不要q行q样的赋值操作:
dllGlobalVar = 1;
其结果是dllGlobalVar指针的内容发生变化,E序中以后再也引用不到DLL中的全局变量了?BR>
在应用工E中引用DLL中全局变量的一个更好方法是Q?BR>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
int main(int argc, char *argv[])
{
printf("%d ", dllGlobalVar);
dllGlobalVar = 1; //q里可以直接? 无须q行强制指针转换
printf("%d ", dllGlobalVar);
return 0;
}
通过_declspec(dllimport)方式导入的就是DLL中全局变量本n而不再是其地址了,W者徏议在一切可能的情况下都使用q种方式?BR>
#define CALLBACK __stdcall //q就是传说中的回调函?BR>
#define WINAPI __stdcall //q就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这?BR>
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
int __stdcall add(int x, int y);
typedef int(__stdcall *lpAddFun)(int, int);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("\nprocess attach of dll");
break;
case DLL_THREAD_ATTACH:
printf("\nthread attach of dll");
break;
case DLL_THREAD_DETACH:
printf("\nthread detach of dll");
break;
case DLL_PROCESS_DETACH:
printf("\nprocess detach of dll");
break;
}
return TRUE;
}
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
//MAKEINTRESOURCE直接使用导出文g中的序号
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("\ncall add in dll:%d", result);
}
FreeLibrary(hDll);
}
#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#pragma comment(lib,"dllTest.lib")
//.lib文g中仅仅是关于其对应DLL文g中函数的重定位信?BR>
extern "C" __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[])
{
int result = add(2,3);
printf("%d",result);
return 0;
}
; lib.def : 导出DLL函数
LIBRARY dllTest
EXPORTS
add @ 1
/* 文g名:lib.h */
#ifndef LIB_H
#define LIB_H
extern "C" int __declspec(dllexport)add(int x, int y);
#endif
/* 文g名:lib.cpp */
#include "lib.h"
int add(int x, int y)
{
return x + y;
}
分析上述代码QdllTest工程中的lib.cpp文g与第2节静态链接库版本完全相同Q不同在于lib.h对函数add的声明前面添加了__declspec(dllexport)语句。这个语句的含义是声明函数add为DLL的导出函数。DLL内的函数分ؓ两种Q?BR>
(1)DLL导出函数Q可供应用程序调用;
(2) DLL内部函数Q只能在DLLE序使用Q应用程序无法调用它们?BR>
与第2节对静态链接库的调用相|我们也徏立一个与DLL工程处于同一工作区的应用工程dllCallQ它调用DLL中的函数addQ其源代码如下:
#include <stdio.h>
#include <windows.h>
typedef int(*lpAddFun)(int, int); //宏定义函数指针类?BR>
int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("%d", result);
}
FreeLibrary(hDll);
}
return 0;
}
而应用程序对本DLL的调用和对第2节静态链接库的调用却有较大差异,下面我们来逐一分析?BR>
首先Q语句typedef int ( * lpAddFun)(int,int)定义了一个与add函数接受参数cd和返回值均相同的函数指针类型。随后,在main函数中定义了lpAddFun的实例addFunQ?BR>
其次Q在函数main中定义了一个DLL HINSTANCE句柄实例hDllQ通过Win32 Api函数LoadLibrary动态加载了DLL模块q将DLL模块句柄赋给了hDllQ?BR>
再次Q在函数main中通过Win32 Api函数GetProcAddress得到了所加蝲DLL模块中函数add的地址q赋l了addFun。经由函数指针addFunq行了对DLL中add函数的调用;
最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已l加载的DLL模块?BR>
通过q个单的例子Q我们获知DLL定义和调用的一般概念:
(1)DLL中需以某U特定的方式声明导出函数Q或变量、类Q;
(2)应用工程需以某U特定的方式调用DLL的导出函敎ͼ或变量、类Q?BR>
//文gQlib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y); //声明为C~译、连接方式的外部函数
#endif
//文gQlib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}
~译q个工程得C一?lib文gQ这个文件就是一个函数库Q它提供了add的功能。将头文件和.lib文g提交l用户后Q用户就可以直接使用其中的add函数了?BR>
标准Turbo C2.0中的C库函敎ͼ我们用来的scanf、printf、memcpy、strcpy{)来自这U静态库?BR>
下面来看看怎么使用q个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文gQ它演示了静态链接库的调用方法,其源代码如下Q?BR>
#include <stdio.h>
#include "..\lib.h"
#pragma comment( lib, "..\\debug\\libTest.lib" ) //指定与静态库一赯?BR>int main(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ) );
}
静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白q个概念。代码中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文g生成?obj文g应与libTest.lib一赯接?BR>