vc++调用动态扩展DLL 求救!!!!

starl1985 2008-12-19 04:00:38
各位大虾好

我自己创建了一个键盘钩子的扩展MFC的DLL,宏定义了导出类。

我写了一个VC6的应用程序调用这个DLL,,宏定义了导入类,把.DLL和.LIB文件都复制到应用程序的目录下面,在应用程序的.h文件里面也include了DLL的.h文件,可是编译的时候总是报错!!! 不知道是不是我的导出导出类弄错了还是怎么的? DLL里面的函数一引用就报错,还有一个错误就是应用程序建立的这个begin_massage_begin宏定义会报错!!! 本人刚刚接触VC和dll,了解的知识太少,不知道这两个错误是什么原因,希望有达人能帮忙解答下!!!谢谢!!!

PS:这个刚刚申请的号,好像还没有分给,不好意思!!希望好心人分享
...全文
106 点赞 收藏 5
写回复
5 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
starl1985 2008-12-24
谢谢 xhfch 的辛勤分享

我发现我可能是建立的工程有点问题

我建立的DLL是 MFC扩展DLL

不过我的应用程序好像不是MFC,所以调用的时候总是报错!!!

好像MFC扩展DLL 只能由 MFC类程序调用!!



回复
xhfch 2008-12-20
动态链接库 (DLL):

动态链接库 (DLL) 是作为共享函数库的可执行文件。

动态链接提供了一种方法,使进程可以调用不属于自己的可执行代码的函数。

函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。

多个应用程序可同时访问内存中单个 DLL 副本的内容。



动态链接与静态链接的不同之处在于:

动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。

在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。



使用动态链接代替静态链接有若干优点:

DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。



应用程序和 DLL 之间的区别:

DLL 和应用程序都是可执行的程序模块,它们之间有若干不同之处:

对于最终用户来说,最明显的差异在于 DLL 不是可直接执行的程序。

从系统角度讲,应用程序和 DLL 之间有两个基本差异:

应用程序可有多个同时在系统上运行的实例,而 DLL 只能有一个实例。

应用程序可以拥有堆栈、共用内存、文件句柄、消息队列这样的事物,而 DLL 不能。



将可执行文件链接到 DLL:

可执行文件以下列两种方式之一链接到(或加载)DLL:

· 隐式链接

为隐式链接到 DLL,可执行文件必须从 DLL 的提供程序获取下列各项:

· 包含导出函数和/或 C++ 类的声明的(1)头文件(.h 文件)。类、函数和数据均应具有 __declspec(dllimport),

(include [dllHeadFile].h)

· 要链接的导入库((2).LIB files)。(生成 DLL 时链接器创建导入库。)

(Link [dllLibFile].Lib)

· 实际的 DLL((3).dll 文件)。

OS:will load dll File code

使用 DLL 的可执行文件必须包括头文件,此头文件包含每个源文件中的导出函数(或 C++ 类),

而这些源文件包含对导出函数的调用。从编码的角度讲,导出函数的函数调用与任何其他函数调用一样。



隐式链接:(include+link(osLoad)+call)



若要生成调用可执行文件,必须与导入库链接。

操作系统在加载调用可执行文件时,必须能够定位 DLL 文件。

在隐式链接下,使用 DLL 的可执行文件链接到该 DLL 的创建者所提供的导入库(.lib 文件)。

使用 DLL 的可执行文件加载时,操作系统加载此 DLL。

客户端可执行文件调用 DLL 的导出函数,就好像这些函数包含在可执行文件内一样。



· 显式链接

调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。
调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。
由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。
使用完 DLL 后调用 FreeLibrary
显式加载和卸载:(LoadLibrary-> GetProcAddress-> FreeLibrary)

隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。



在显式链接下,使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载该 DLL,并访问该 DLL 的导出函数。

客户端可执行文件必须通过函数指针调用导出函数。



显式链接到 DLL 的进程 调用 GetProcAddress 来获取 DLL 导出函数的地址。

使用返回的函数指针调用 DLL 函数。GetProcAddress 将(由 LoadLibrary、AfxLoadLibrary 或 GetModuleHandle 返回的)DLL 模块句柄和要调用的函数名或函数的导出序号用作参数。

由于是通过指针调用 DLL 函数并且没有编译时类型检查,需确保函数的参数是正确的,

以便不会超出在堆栈上分配的内存和不会导致访问冲突。



LoadLibrary 和 AfxLoadLibrary:



进程调用 LoadLibrary(或 AfxLoadLibrary)以显式链接到 DLL。

如果成功,函数将指定的 DLL 映射到调用进程的地址空间中并返回此 DLL 的句柄,

该句柄可与用于显式链接的其他函数(如 GetProcAddress 和 FreeLibrary)一起使用。

LoadLibrary 尝试使用用于隐式链接的同一搜索序列来定位 DLL。

如果系统无法找到 DLL 或者入口点函数返回 FALSE,LoadLibrary 将返回 NULL。

如果对 LoadLibrary 的调用所指定的 DLL 模块已映射到调用进程的地址空间中,则函数仅返回 DLL 的句柄并递增模块的引用数。

如果 DLL 有入口点函数,则操作系统在调用 LoadLibrary 的进程上下文中调用此函数。

如果由于以前调用了 LoadLibrary 但没有相应地调用 FreeLibrary 函数而导致 DLL 已经附加到进程,则不会调用此入口点函数。

加载扩展 DLL 的 MFC 应用程序应使用 AfxLoadLibrary,而不应使用 LoadLibrary。

AfxLoadLibrary 在调用 LoadLibrary 之前将处理线程同步。

AfxLoadLibrary 的接口(函数原型)与 LoadLibrary 相同。

如果出于某种原因 Windows 无法加载 DLL,进程可以尝试从错误恢复。

例如,进程可通知用户所发生的错误,并让用户指定 DLL 的其他路径。



FreeLibrary 和 AfxFreeLibrary

不再需要 DLL 模块时,显式链接到 DLL 的进程调用 FreeLibrary 函数。

此函数递减模块的引用数,如果引用数为零,此函数便从进程的地址空间中取消模块的映射。

MFC 应用程序应使用 AfxFreeLibrary 而非 FreeLibrary 卸载扩展 DLL。

AfxFreeLibrary 的接口(函数原型)与 FreeLibrary 相同。



导入和导出:

可以使用两种方法将公共符号导入到应用程序中或从 DLL 导出函数:

· 生成 DLL 时使用模块定义 (.def) 文件

· 在主应用程序的函数定义中使用关键字 __declspec(dllimport) 或 __declspec(dllexport)

使用 .def 文件

模块定义 (.def) 文件是包含一个或多个描述 DLL 各种属性的 Module 语句的文本文件。

如果不使用 __declspec(dllimport) 或 __declspec(dllexport) 导出 DLL 函数,则 DLL 需要 .def 文件。

可以使用 .def 文件导入到应用程序中或从 DLL 导出。



def 文件必须至少包含下列模块定义语句:

· 文件中的第一个语句必须是 LIBRARY 语句。

此语句将 .def 文件标识为属于 DLL。LIBRARY 语句的后面是 DLL 的名称。

链接器将此名称放到 DLL 的导入库中。

· EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。

通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。
当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
LIBRARY BTREE

EXPORTS

Insert @1

Delete @2

Member @3

Min @4

使用 __declspec

Visual C++ 用 __declspec(dllimport) 和 __declspec(dllexport) 取代以前在 16 位版的 Visual C++ 中使用的 __export 关键字。

不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。

编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,

而这些代码通常会出现在跨 DLL 边界的函数调用中。

必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

如果有正确的 .def 文件 EXPORTS 节,则不需要 __declspec(dllexport)。

添加 __declspec(dllexport) 是为了提供不使用 .def 文件从 .exe 或 .dll 文件导出函数的简单方法。

Win32 可移植可执行文件格式旨在最小化为修改导入而必须访问的页数。

为此,它将所有程序的所有导入地址都放在一个称为“导入地址表”的位置。

这使得加载程序在访问这些导入时可以只修改一两页。



Microsoft 在 Visual C++ 的 16 位编译器版本中引入了 __export,

使编译器得以自动生成导出名并将它们放到一个 .lib 文件中。然后,此 .lib 文件就可以像静态 .lib 那样用于与 DLL 链接。

在更新的编译器版本中,可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。

__declspec(dllexport) 会将导出指令添加到对象文件中,因此您不需要使用 .def 文件。

当试图导出 C++ 修饰函数名时,这种便利最明显。由于对名称修饰没有标准规范,因此导出函数的名称在不同的编译器版本中可能有所变化。

如果使用 __declspec(dllexport),仅当解决任何命名约定更改时才必须重新编译 DLL 和依赖 .exe 文件。

许多导出指令(如序号、NONAME 和 PRIVATE)只能在 .def 文件中创建,并且必须使用 .def 文件来指定这些属性。

不过,在 .def 文件的基础上另外使用 __declspec(dllexport) 不会导致生成错误。

若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:

__declspec(dllexport) void __cdecl Function1(void);


若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,如下所示:

class __declspec(dllexport) CExampleExport : public CObject

{ ... class definition ... };


生成 DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将 __declspec(dllexport) 添加到头文件中的声明中。

若要提高代码的可读性,为 __declspec(dllexport) 定义一个宏并对正在导出的每个符号使用该宏:

#define DllExport __declspec( dllexport )


__declspec(dllexport) 将函数名存储在 DLL 的导出表中。

注意:

将 DLL 源代码从 Win16 移植到 Win32 时,请用 __declspec(dllexport) 替换 __export 的每个实例。





你没有link 吧 在Project /Link 标签 把你 的dll
lib 添加到里面
回复
keven1868 2008-12-19
dll的头文件直接拿来用了?dllexport有没有改成dllimport?
回复
ShowhowYoung 2008-12-19
把出错信息报上来。
回复
starl1985 2008-12-19
自己顶一个先
回复
相关推荐
发帖
进程/线程/DLL
创建于2007-09-28

1.5w+

社区成员

VC/MFC 进程/线程/DLL
申请成为版主
帖子事件
创建了帖子
2008-12-19 04:00
社区公告
暂无公告