用VC++5.0制作DLL经验一二
北京大唐电信软件中心
周志杰
---- 本文'一'、'二'两部分适用于对DLL的基本制作方法已经了解,或手头有关于DLL制作方法的书籍的读者.
对于初次接触DLL制作的读者,建议您先按'三'中的步骤建立一个自己的DLL并在另一个应用程序中成功的调用
它之后再阅读'一'、'二'.
---- DLL可以分为两个不同的类别:用C/C++(不用对象)编写的基于API的传统DLL和基于MFC对象的DLL.
一.两种类型的比较:
---- 1. 基于MFC的DLL限制在使用MFC的编译器中.
---- 基于API的DLL可以从一种编译器移植到另一种编译器.
---- 2. 基于MFC的DLL可以由制作向导构建框架,制作简单
---- 基于API的DLL的制作向导只为你制作了一个空的DLL工程,工程的维护和代码的编写全部需要手工完成
(这一点在VC 6.0中有了改进)
---- 3. 基于MFC的DLL不适用与制作读取二进制文件的DLL
---- (无法正确读取与DOS应用程序共享的二进制文件)
---- 基于API的DLL可以正确读取在DOS环境中创建的二进制文件.
---- 造成该结果的原因其实很简单:
---- 在读取二进制文件的过程中通常会使用"结构(体)",基于MFC的DLL要加载MFC,它要求"结构成员位对齐"的比
特位是8位,而且你无法方便的通过选中"Project- >Setting- >C/C++"选项卡中的"Code Generation"再修改"
Struct member alignment"来使其变为1位(在VC5.0中即使改变了,在编译时该改变也会被忽略.不过这一不足在
VC6.0中已经得到了改进,读者有兴趣的话可参见笔者《在VC6.0中读取二进制文件的惊喜》一文).
---- 而基于API的DLL则可以通过以上的方法方便的实现.
二.在实际制作与使用中的一点经验:
---- 1.制作DLL的目的之一是共享资源/代码.所谓"共享"当然不应该仅仅是几个VC++制作的应用程序可以使用,但
是在与其他编程语言协作时,有些问题是需要注意的.
---- 在制作DLL时,VC++对函数的省缺声明是"__cedcl",也就是说,如果你在声明你的函数时不作特殊声明的话,你
制作的DLL将只能被C/C++调用,如果你想用其他开发语言(比如VB5.0)调用它就会报错,即使调用方法完全正确.
---- 那么该怎么办呢,你一定已经猜到了---不要用省缺的声明方式---一个很好的选择是使用"WINAPI"来声明你的
函数.它可以把你的DLL中的函数声明成WINDOWS API供其他程序调用(当然也包括C/C++制作的程序).
---- 2.建议你在制作DLL的同时制作包括导出函数原型声明的.H文件虽然这不是必须的但是若你的DLL是被C/C++调
用,.H文件和.LIB文件可以为使用你的DLL的开发人员省去不少精力,当你需要修改/升级你的DLL时更是如此.DLL的
C/C++使用者只要在工程中引入.H和.LIB文件可以象使用自己编写的函数一样方便的使用DLL中的函数,不必再使用
存储DLL句柄、声明函数型指针、LoadLibrary、GetProcAddress那样繁复的调用方式.
---- 3.虽然你不必编写.DEF文件就能制作出基于API的.DLL文件.但是制作.DEF文件并不难,至少你有捷径可以走.
有一个很简单的方法你不妨一试:
---- 例如,你用基于API的方法制作了一个DLL工程文件并为其编写了.CPP和.H文件,你可以保存并关闭该工程,然后
在另一个目录中创建一个与其同名的基于MFC的DLL工程.好了,现在你已经知道怎么做了---将该目录中的.DEF文件移
动过去就可以了.省下的工作就是再次打开基于API的DLL工程,并将.DEF文件加入工程,将你的导出函数的函数名加到
EXPORTS之后,再重新编译工程就OK了.
---- 4.DLL文件的省缺名称是与工程名一致的(也是在.DEF文件中LIBRARY 之后的名字),不要试图在制作完毕之后通
过简单的修改.DLL文件的文件名来改变它,这会导致使用该DLL的应用程序错误.
三.一个例子:
---- DLL中定义有两种函数:
---- 导出函数(exportfunction): 可以被其他模块调用
---- 内部函数(internalfunction): 只能在DLL内部使用
---- 创建一个基于API的DLL.本例只定义了导出函数.
---- 1.在FILE- >NEW- >PROJECTS中选择"WIN32 Dynamic-Link Library"在Project Name中输入 "a"按OK
---- 2.在FILE- >NEW- >FILES中选择C++ SOURCE FILE,在FILE中输入a.cpp,按OK
---- 在FILE- >NEW- >FILES中选择TEXT文件,在FILE中输入a.h,按OK
---- 在FILE- >NEW- >FILES中选择TEXT文件,在FILE中输入a.def,按OK
---- 3.源文件:
//---------------------------
//a.cpp
#include < windows >
WINAPI int add(int a,int b)
{ return (a+b);
}
//---------------------------
//a.h
WINAPI int add(int a,int b);
//---------------------------
//a.def
LIBRARY "aaa" ;指出DLL的名字
DESCRIPTION 'aaa Windows Dynamic Link Library'
;描述DLL的用途(此句可选)
EXPORTS add ;导出函数的名字
四.调用DLL的方法:
---- 1.通常我们在调用DLL时所需的DLL文件必须位于以下三个目录之一:
---- (1)Windows的系统目录:\windows\system;
---- (2)DOS中path所指出的任何目录;
---- (3)程序所在的目录;
---- 同时应注意管理好你的.lib文件和.h和文件
---- 2.建立一个工程,简单起见可建立一个控制台应用程序.
---- 3.在工程中引入a.lib:
---- (1)如果你的a.lib放在VC标准的LIB文件夹中.
单击Project- >Project Settings...
在link选卡的object/library modules中加上a.lib即可
---- (2)如果你的a.lib不是放在VC标准的LIB文件夹中
单击Project- >Add to Project- >files...
找到a.lib文件,按OK
< pre >
4.//------------------------
//call_a.cpp
#include< stdio.h >
#include "c:/a/a.h"
void main(void)
{ int c=0;
c=add(1,2);
printf("1+2=%d",c);
}
//本程序在VC5.0下调试通过
创建"通用的"动态链接库(DLL)
VCKBASE
--------------------------------------------------------------------------------
最近有许多人问如何让动态链接库的输出函数能够被任何支持动态链接库的语言调用。写动态链接库是很EASY
的事情。但是,他们大多觉得输出的名字有乱字符(如:_vbShiftRight@1)。我想在这里就讨论一下如何让输出函
数的名字中不出现乱子符。
首先,除非你绝对需要使用C++编码,否则我还是推荐使用C来写DLL。这里我想让创建的DLL可以在VB中使用。
你需要实现自己DLL的入口点函数和保证代码使用stdcall调用规范,但这要依赖你使用的编译器。例如,你可
以在VC中使用"/entry:"DLLEntry""命令行编译选项来创建自己的入口点。"DLLEntry" 可以参考下列代码:
/////////////////////////////////////////////////////////////////////
// DLL initialization and clean-up.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
// Perform any DLL initialization here
break;
case DLL_PROCESS_DETACH:
// Perform any DLL cleanup here
break;
}
return TRUE;
}
为了保证你使用正确的调用规范,要通知编译器使用stdcall规范和/或使用在windows.h(及相关文件)中定义的常量,
如WINAPI等。通常DLL的代码如下:
/////////////////////////////////////////////////////////////////////
// Shifts bits right for integers.
WORD WINAPI vbShiftRight(WORD nValue, WORD nBits)
{
return (nValue >> nBits);
}
下一步是与你在微软文档中读到的内容相反。你需要创建一个DEF文件。这是你防止输出函数名不出现乱字符的唯一方
式(如_vbShiftRight@1)。DEF文件的形式如下:
EXPORTS
vbShiftRight
下一步是在VB中调用这个函数,使用以下声明:
Declare Function vbShiftRight Lib "MYDLL.DLL" (ByVal nValue As Integer,
ByVal nBits As Integer)
As Integer
Sub Test()
Dim i As Integer
i = vbShiftRight(4, 2)
Debug.Assert i = 1
End Sub
如果你还想要更容易的方法从VB中调用,可以创建一个类型库。为此你需要创建和编译ODL(对象描述语言)文件。
这个文件应该包含如下内容:
module MyModule {
[
helpstring("Shifts the bits of an integer to the right."),
entry("vbShiftRight")
]
short _stdcall vbShiftRight([in] short nValue, [in] short nBits);
};
当VB加载DLL的类型库时,函数名和参数将出现在VB的对象浏览器中。此外,如果用户不输入正确的参数类型,
VB将产生一个错误。
利 用DLL 增 强 软 件 功 能
大 连 建 园 科 技 公 司: 李 军
大 连 轻 工 学 院: 刘 裕 华
---- Microsoft Windows 提 供 了 称 之 为 动 态 连 接 库 的 特 殊 库(DLL), 让 应 用 程 序 共 享 代
码 和 资 源。DLL 是 一 个 包 括 了 若 干 函 数 的 可 执 行 模 块, 为 其 他 应 用 程 序 提 供 服 务。
Windows 利 用DLL 来 提 供 所 有Windows 应 用 程 序 均 可 以 使 用 的 代 码 和 资 源。 此 外, 应 用 程
序 也 可 以 创 建 自 己 的DLL, 在 自 己 的 各 个 应 用 程 序 之 间 共 享 代 码 和 资 源。
---- DLL 主 要 有 如 下 目 标:
应 用 程 序 之 间 共 享 代 码 和 资 源
基 于 系 统 范 围 的 消 息 过 滤
创 建 设 备 驱 动 程 序
提 供 开 发 复 杂 应 用 程 序 的 设 施
---- 我 们 现 在 要 研 究 的 问 题 是, 如 何 通 过 开 发DLL 来 拓 展、 增 强 常 用 软 件 的 功 能,
如:VB4.0、ToolBook3.0 等。
---- 常 用 来 开 发DLL 的 平 台 有BlandC++、MS VisualC++, 二 者 之 间 在 开 发DLL 是 有 一 定 的 差
别, 但 最 方 便 的 是MS VisualC++。 这 里 我 们 利 用MS VisualC++ 开 发 一 个Demo.DLL, 它 定 义 一
个 函 数:F1=(x,y)=x*y。 通 过 这 个 实 例 说 明DLL 的 程 序 结 构, 再 通 过 对Demo.DLL 的 调 用
说 明 增 强 软 件 功 能。
---- 实 例:
---- 在Demo.DLL 中 定 义 一 个 F1(x,y)=x*y ;
---- 总 共 需 要 三 个 文 件:Demo.h ;Demo.c;Demo.DEF
* 在 头 文 件(Demo.h) 中:
#include < windows.h >
extern int_export_far_pascal F1(int,int);
* 在 源 程 序(Demo.c) 中:
#include< windows.h >
#include"Demo.h"
int FAR PASCAL LibMain(HINSTANCE hInstance,
WORD wDataSegment,
WORD wHeapSize,
LPSTR lpszCmdLine)
{
return 1;
}
int _export_far_pascal F1(int X,int Y)
{
int data;
data=X*Y;
return data;
}
int FAR PASCAL WEP(int bSystemExit)
{ return 1;
}
* 在 模 块 文 件(Demo.def) 中:
LIBRARY Demo
DESCRIPTION 'c++xyy DLL for Windows'
EXETYPE WINDOWS
CODE MOVEABLE DISCARDABLE
DATA MOVEABLE SINGLE
HEAPSIZE 0
EXPORTS
F1@1
---- * 在VB 使 用DLL :
---- 在 调 用 之 削 首 先 要 在MODULE 模 块 中 声 明 函 数, 声 明 的 格 式 是:
Declare Function 函 数 名
Lib"DLL 名"(Byval 参 数 As Datatype,,)
Declare Function F1 Lib"Demo"(ByVal X As Integer,
ByVal X As Integer)As Integer
---- 然 后 就 可 以 在 任 何 地 方 使 用 函 数F1()。
---- * 在ToolBook3.0 中 使 用DLLs:
---- 在 调 用 之 前 首 先 要 连 接DLL, 并 声 明 函 数 原 型, 声 明 的 格 式 是:
link DLL "Demo.dll"
函 数 类 型 函 数 名( 参 数 类 型,,)
INT F1(INT,INT)
end linkDLL
---- 利 用 以 上 方 法 可 以 方 便 地 开 发DLL, 用 来 拓 展vb4.0、ToolBook3.0、Delphi1.0 等 大 部 分
软 件 的 功 能, 使 其 功 能 更 加 强 大, 比 如: 实 现 对 非 标 准 硬 件 的 控 制、 满 足 要 求。
这 样 我 们 即 利 用 了 新 型 软 件 的 可 视 化 对 象 编 程, 又 结 合 了C++ 语 言 的 任 意 按 需 编
程 的 优 势, 给 我 们 的 软 件 开 发 带 来 许 多 便 利。
中国计算机世界出版服务公司版权所有