如何写 C dll 和调用它们从 Visual Basic

来源:百度文库 编辑:神马文学网 时间:2024/10/05 21:43:39
本文概括介绍了如何使用 Visual basic 的 dll。它包括以下问题: 部分 1.0 什么是 DLL?1.1 为什么使用 DLL?1.2 DLL 的构成...
本文概括介绍了如何使用 Visual basic 的 dll。它包括以下问题: 部分
1.0 什么是 DLL?
1.1 为什么使用 DLL?
1.2 DLL 的构成。
1.3 DLL 内存管理问题。
1.4 构建使用 Visual c + + DLL。
1.5 示例 C DLL。
部分 B
2.0 从 Visual Basic 中调用的 dll。
2.1 DLL 参数。
2.2 故障排除。
2.3 示例 Visual Basic 电话程序。
回到顶端
更多信息 部分 A 1.0 什么是 DLL? dll (动态链接库) 是 Windows 的一个重要方面。DLL 包含可执行程序可以在执行过程中调用的函数。 也就 DLL...
部分 A
1.0 什么是 DLL?
dll (动态链接库) 是 Windows 的一个重要方面。DLL 包含可执行程序可以在执行过程中调用的函数。 也就 DLL 是一种库的程序可以动态地链接使用的函数。
链接可以是静态或动态。静态链接不会更改。您的程序访问库函数时所需的 所有地址信息被都固定的可执行文件在创建和执行过程中保持不变时。
根据需要创建动态链接。当程序需要的不是可执行文件中的函数 时,Windows 加载动态链接库 (DLL) 使其函数的所有可用于您的应用程序。该次 Windows 解析每个函数的地址,并动态地将其链接到您的应用程序。
在 Visual Basic 中使用的所有自定义控件的 dll。唯一的区别是它们需要特殊处理的 Visual Basic 从收到的邮件。 1.1 为什么使用 dll?
下面是为什么您可能希望使用 DLL 的四个原因:
C 运行时函数的访问:
C 运行时库有许多有用的功能将不可用的 Visual Basic 程序员的 dll 不是。例如对于通过 _dos_getdiskfree 函数可以计算总的磁盘空间量和可用的驱动器上可用磁盘空间。
对 Windows API (应用程序编程接口) 的访问功能,需要回调例程:
有些 Windows API 函数要求一个回调函数。回调函数是一个函数,Windows 将调用该函数在执行 API 时调用。这种函数的一个示例是 EnumTaskWindows,将赋予所拥有的特定任务的所有窗口的句柄。
速 度:
C 是一个完全编译的语言,它相当接近为本机代码的级别上工作。这意味着将快速也用 C 编写的程序的执行。
在 使用负载:
只在需要时才会加载代码和 DLL 中的数据。DLL 可以进行组织,以便只有所需的部件相对于整个 DLL 加载。这样可以减少所需的内存和加载的时间量。
1.2 DLL 的构成
每个 DLL 必须包含一个 LibMain 函数,并应包含除了导出的函数可由可执行程序调用的一个 Windows 退出过程 (WEP)。
LibMain:
DLL 必须包含 LibMain 函数。LibMain 函数调用系统以初始化 DLL。LibMain 是只能调用一次--需要该 DLL 的第一个程序加载时。参数传递给 LibMain 如下: 对 DLL 的实例的句柄: 句柄。
-WORD: 库的数据段。
-WORD: 堆大小。
-LPSTR: 命令行参数。
WEP:
卸载磁带库之前,WEP (Windows 退出过程) 的 DLL 执行清理。虽然 WEP 函数所必需的以前版本的 Windows 操作系统中的每个 DLL,3.1 版的它是可选的。一个 WEP 应包含在模块定义文件 (.DEF) 中可视的 C,例如: 导出
WEP
导 出的函数:
这些是您要从您的 DLL 中调用的函数。它们由 _export 表示。_export 用于向后兼容性。(.DEF) 文件的 DLL 文件中,还必须列出想要调用的所有功能。
1.3 DLL 内存管理问题
使用大的内存模型。
C 将存储为静态定义的所有变量或全局 (外部定义的函数) 将在该程序的堆空间和 C 所有其他变量都存储在堆栈上。
在中小型模型中所有的指针将默认情况下位于附近。这意味着数据访问数据段 (DS) 寄存器中,或堆栈段 (SS) 寄存器的 16 位偏移量。遗憾的是,编译器有无法知道是否偏移量从 DS 或该 SS。在大多数程序中这不是问题因为 DS 和 SS 指向同一个网段。一个 DLL 但,是一种特殊情况。
DLL 都有其自己的数据段,但与调用程序共享其堆栈。这意味着 DS 和 $ 在 SS 未指向同一位置。在解决此问题的最简单方法是生成 DLL 在大内存模型中所有变量都引用的值是 32 位的位置。 为什么动态分配内存?
动态分配的内存是 Windows 友好的一项技术。声明的数据的大型数组占用的空间限于到 64k 的程序的堆栈或您程序的数据段浪费了磁盘空间和 Windows 内存中。最好时您需要它,并且在完成后再将其释放内存,询问 Windows。 分配内存
在 Windows 中,您可以动态地分配两种类型的本地和全局的内存。本地内存仅限于 64 的 K,并在一个 DLL 的情况下本地内存与调用 DLL 的程序共享。全局内存是内存的所有 windows 可用后它已加载。
本地内存分配和托管 使用 LocalAlloc、 LocalLock LocalUnlock 和 LocalFree 功能--如下例所示: char* pszBuffer; .... pszBuffer = (char *) LocalAlloc (LPTR, 20); ... LocalFree (pszBuffer);
比分配全局内存分配本地内存将更快。 但是,从本地堆分配仅限于这必须调用 DLL 的所有程序间共享 64 K。最好使用时小,短寿命的内存块所需的本地内存。
全局内存分配和托管使用 GlobalAlloc、 GlobalLock GlobalUnlock 和 GlobalFree 功能--如下例所示: HGLOBAL hglb; char* pszBuffer; hglb = GlobalAlloc (GHND, 2048); // GHND allocates the memory as moveable and // initialized to 0 // 2048 is the amount of memory to be allocated... pszBuffer = GlobalLock (hglb); ... GlobalUnlock (hglb); GlobalFree (hglb);
的 GlobalAlloc 函数分配内存以 4 K 的倍数增长。
如果您想要共享 DLL 与其他程序中分配的内存,您应将其使用 GMEM_SHARED 标志进行分配。如果您想要共享通过 DDE 内存,您必须将其分配使用 GMEM_DDESHARE 标志。 是仔细静态变量中存储数据时
如果试图将数据存储在使用全局或静态变量的 DLL 中不要会惊讶如果接下来调用您的 DLL 时,这些值已更改。用这种方式存储,数据将通用的所有访问此 DLL 的应用程序。无论多少应用程序使用 DLL,没有 DLL 的一个实例。若要避免这最好的方法是从 DLL 返回结构并将它们在在需要时再次传递。 文件句柄
不能共享应用程序或 dll 之间的文件句柄。每个应用程序都有其自己的文件句柄的表。两个应用程序可以使用相同的文件使用 DLL,它们必须同时打开的文件分别。 1.4 构建使用 Visual c + + DLL
以下是生成使用 Visual c + + DLL 所需步骤:
启动 Visual c + +。
通过从项目菜单中选择新建创建一个新的项目。选择以下选项:
将项目类 型设置为"windows 动态链接库 (.DLL)"
清除使用 Microsoft 基础类复选框。
您还可以设 置或以后查看这些选项,通过从选项菜单中选择项目。
将现有.C 和.def 文件添加到项目中,通过使用出现时从项目菜单中选择编辑对话框。或者直接在 Visual c + + 编辑窗口中输入您的代码。(请参阅下面列出了.C 和.def 示例代码。
从项目菜单中选择生成 .DLL 选项。
1.5 示例 C DLL
下面的 DLL 包含 GetDiskInfo 函数可以从 Visual Basic 中调用。它将返回可用磁盘空间、 当前驱动器的名称和卷名。 C Code Example, DISKINFO.C: #include #include int CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) // The following is required only under Windows version 3.1 // Win32 does not require or support UnlockData() { if (wHeapSize > 0) UnlockData (0); //Unlocks the data segment of the library. return 1; } void __export CALLBACK GetDiskInfo (char *cDrive, char *szVolumeName, unsigned long *ulFreeSpace) { unsigned drive; struct _diskfree_t driveinfo; struct _find_t c_file; _dos_getdrive (&drive); _dos_getdiskfree( drive, &driveinfo ); if (!_dos_findfirst( "*.*", _A_VOLID, &c_file )) wsprintf( szVolumeName, "%s", c_file.name); else wsprintf ( szVolumeName, "NO LABEL"); *cDrive = drive + 'A' -1; *ulFreeSpace = (unsigned long) driveinfo.avail_clusters * (unsigned long) driveinfo.sectors_per_cluster * (unsigned long) driveinfo.bytes_per_sector; }
使用 Visual c + + 中的以下 DISKINFO.DEF 文件: LIBRARY diskinfo
说明 GetDiskInfo 可能从调用 vba
EXETYPE 窗口 3.1
代码 PRELOAD 可移动可放弃
数据 PRELOAD 可移动单
HEAPSIZE 4096
导出
GetDiskInfo @ 1
注: 到.def 文件中的 LIBRARY 名称必须与 DLL 的文件名称相同否则 Visual Basic 将为您提供加载 DLL 错误。例如对于创建文件 DISKINFO.DLL 上面到.def 文件中使用 LIBRARY DISKINFO 语句。回到顶端
部分 B
2.0 从 Visual Basic 中调用的 dll
在 vba 所有的函数中包括想要调用的 DLL 函数必须首先声明使用 Declare 语句。您可以声明您在窗体或模块的声明部分中的功能。如果您声明一个 DLL 过程或窗体中的函数,则专用于该窗体。 若要将其公开,您必须在模块中声明它。以下是一个示例 Declare 语句: Declare Sub getdiskinfo Lib "c:\somepath\diskinfo.dll" (ByVal mydrive As String, ByVal myvolume As String, free As Long)
,必须输入整个 Declare 语句作为一个,单个行。此特定的声明语句声明 GETDISKINFO 位于用户创建 DISKINFO.DLL 文件在用户定义的过程。
一旦您声明该函数,您可以调用,并使用该函数,就像您将调用并使用 Visual Basic 函数。 2.1 DLL 参数
因为 dll 通常用 C 编写的 dll 可以使用多种不直接支持 Visual Basic 的参数。如此一来传递参数时, 必须找到传递适当的数据类型程序员。 通过值或通过引用传递参数
默认状态下,Visual Basic 将通过引用传递所有参数。(当通过引用传递,Visual Basic 提供 32 位远地址)。但是,多个 DLL 函数期望通过值传递的参数。这可通过将放在参数声明的前面 ByVal 关键字来实现。
以 下各节介绍如何将参数转换为 Visual Basic。 8 到 16 位数字参数
将 8 到 16 位数字参数 (int、 较短的无符号整数、 无符号的短、 BOOL 和 WORD) 作为整型传递。 32 位数字参数
将 32 位数值参数传递 (长,无符号长整型,和 DWORD) 为 LONG。 对象句柄
所有句柄是与窗口相关联的 16 位的唯一整数值和通过值传递,因此将作为整型传递,这些参数。 字符串
字符串包含 LPSTR 和 LPBYTE 的数据类型 (指向字符的指针) 或为无符号的字符的指针。将作为 (ByVal paramname 为 String) 传递这些参数。若要直接传递 Visual Basic 字符串,请将它们作为 (字符串为参数) 传递。
在 Visual Basic 和 C DLL 之间传递字符串上的其他信息,请参阅 Microsoft 知识库中下面的文章:118643  (http://support.microsoft.com/kb/118643/EN-US/ ) 如何传递一个字符串数组 VB 和 C 的 DLL 之间
注意: Visual Basic 字符串需要不通过字符串直接,除非该 DLL 显式要求这样的特殊处理。 数值的指针
将指针传递给数字值,只需不使用 ByVal 关键字。 结构
如果在 Visual Basic 用户定义的类型与所需的 DLL 结构相匹配,可以通过引用传递结构。
注 意: 不能通过值传递结构。 数组的指针
通过引用传递数组的第一个元素。 指向函数的指针
Visual Basic 不支持回调函数,因此 Visual basic,不能使用具有指向函数的 DLL 函数。 空指针
如果 DLL 需要 Null 指针,将作为 (ByVal paramname As Any) 传递它。您可以使用 0 和 paramname 时调用该 DLL 中的值。 2.2 疑难解答
以下是您可能会遇到一些问题的解决方案。 之后调用该 DLL 系统资源保留: 低
如果您的 DLL 使用的 GDI 对象,您必须记住它们在使用后释放它们。这可能不是很明显,Visual Basic 中,但如果您创建一个 GDI 对象 (例如对于 CreateBrushIndirect),请使用 Windows SDK (软件开发工具包) 时, 您必须将其删除在以后使用 DeleteObject。 错误的 DLL 调用约定错误
此错误通常被引起错误地省略或包括 ByVal 关键字从声明语句。如果传递错误的参数,也可能导致此错误。 在加载 DLL 的错误
调用动态链接库的过程和过程的声明语句中指定该文件不能被加载时,会发生此错误。您可以使用 Microsoft Windows API 函数 LoadLibrary 以了解有关 DLL 加载失败的更具体的信息。 一 般保护 (GP) 错误
您的程序写入不属于它的内存块时,就会发生 GP 错误。对于这两个的最可能原因是:
overstepped 了数组界限。C 不检查正在写入该数组下标无效。因此,您可以轻松地写入不属于您的内存。
您已经将一个内存位置使用的指针。最佳 的选择是将 NULL 给所有的指针后释放其内存。
不正确的变量类型传递给 DLL 函数时,也会发生 GP 故障。 2.3 示例 Visual Basic 电话程序
没有要在 vba 程序中调用 DLL 的两个部分。首先,您将声明功能,然后您它在代码中使用事件。
下 面是一个声明语句的示例。在模块中或在窗体的通用声明部分中,应将放在声明语句。 ' Enter the following Declare as one, single line: Declare Sub getdiskinfo Lib "c:\dllartic\diskinfo.dll" (ByVal mydrive As String, ByVal myvolume As String, free As Long)
指定 ByVal 语句完全如下所示,否则 GP 故障可能会发生。
一旦声明的函数 就可以在事件代码中使用。下面的示例使用从 DLL 函数在 Command1 Click 的事件代码: Sub Command1_Click () Dim drive As String * 1 Dim volume As String * 20 Dim free As Long Call getdiskinfo(drive, volume, free) Text1.Text = drive Text2.Text = volume Text3.Text = Str$(free) End Sub