• | 导出函数:这些函数由其他模块调用。 |
• | 内部函数:这些函数仅从定义它们的 DLL 中调用。 |
• | 加载时动态链接:调用方模块执行显式调用以导出 DLL 函数。为 DLL 创建导入库,然后将 DLL 链接到应用程序。在加载应用程序时,导入库提供加载 DLL 和查找导出的 DLL 函数所需的信息。 |
• | 运行时动态链接:在运行时加载 DLL 时,调用方模块使用 LoadLibrary 函数或 LoadLibraryEx 函数。调用方模块调用 GetProcAddress 函数以获取导出的 DLL 函数的地址。 |
1. | 当前进程的可执行程序所在的目录。 |
2. | 当前目录。 |
3. | Windows 系统目录。(GetSystemDirectory 函数获取 Windows 系统目录的路径。) |
4. | Windows 目录。(GetWindowsDirectory 函数获取 Windows 目录的路径。) |
5. | PATH 环境变量中列出的目录。 注意:LIBPATH 环境变量不用于搜索。 |
• | DLL 可节省内存并减少交换。 通过在内存中共享 DLL 的单个副本,多个进程可以同时使用一个 DLL。相比之下,对于使用静态链接库构建的每一个应用程序,Windows 都要在内存中为其加载库代码的一个副本。 |
• | DLL 可节省磁盘空间。 多个应用程序可以共享磁盘上的一个 DLL 副本。相比之下,使用静态链接库构建的每一个应用程序都需要让链接到程序文件映像的库代码作为一个单独的专用副本。 |
• | DLL 可节省时间。 更改 DLL 中的函数时,只要函数的参数和返回值不变,就不必重新编译或重新链接使用这些函数的应用程序。但是,如果您使用静态链接的对象代码,则在更改函数后必须重新链接应用程序。 |
• | DLL 可以共享函数。 在 Win32 中,DLL 可以共享函数。默认情况下,数据对于每个进程来说是独立的。但是,静态库包含针对每一个进程的单独的数据副本和函数。 |
• | 将程序划分为可按需加载的单独模块。 |
• | 存储特定于语言或特定于区域的资源。 |
• | 使您自己的应用程序能够使用核心代码库。 |
• | 生成进程内 COM 对象或 ActiveX 控件 (OCX)。 |
• | 将 OLE 对象用作进程内 DLL。这一用法可改进 OLE 链接的性能。 |
• | 使用控制面板扩展或使用某些类型的驱动程序。 |
• | 如果计划服务的服务帐户密码与域用户管理器帐户的密码不同,则您在计划服务时可能会收到以下初始化错误信息:
Initialization of dynamic link library c:/winnt/system32/KERNEL32.dll failed.Process is terminating abnormally
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
250265 使用 Cmd.exe 运行 AT 计划命令时出现 Kernel32.dll 动态链接库初始化错误
|
• | 如果不断加载和卸载 Crypt32.dll,则在加载 Crypt32.dll 时将分配传输层安全性 (TLS),但在卸载 Crypt32.dll 时却不释放它。此行为可能导致一般性保护错误 (GPF) 错误,并且您可能会收到以下错误信息:
Initialization of the dynamic link library C:/WINNT40/System32/CRYPT32.dll failed.The process is terminating abnormally.
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
212825 SMS:一般性保护错误:Initialization of CRYPT32.DLL Failed(CRYPT32.DLL 初始化失败)
|
• | 如果您将计算机升级到 McAfee VirusScan 6.0,则可能会在启动 Outlook 2000 后的三分钟内收到以下错误信息:
Outlook.exe. DLL initialization failed.Initialization of c:/winnt/system32/compstui.dll. Process is terminating abnormally.
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
310413 OL2000:升级到 McAfee VirusScan 6.0 后 Outlook 2000 发生 DLL 初始化错误
|
• | 如果您将 Windows NT Mail 或 MSMail32.exe 设置为按计划启动,计划服务可能被记录为系统以外的其他内容,而且您可能会收到以下错误信息:
Initialization of the Dynamic Link Library OLECLI32.dll failed Abnormally
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
133476 Windows NT 邮件错误信息:Initialization of the Dynamic Link...(动态链接的初始化...)
|
• | 如果远程访问服务 (RAS) 文件或调制解调器文件丢失或损坏,并且您试图打开控制面板,则可能会收到以下错误信息:
Explorer.exe - DLL Initialization Failed Initialization of the dynamic link library E:/WINNT/System32/RASSCRPT.dll failed.The process is terminating abnormally.
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
184443 错误信息:Explorer.exe - DLL Initialization Failed...(Explorer.exe — DLL 初始化失败...)
|
• | 如果系统内存不足,无法为正在启动的服务创建新的桌面堆栈,您可能会收到以下错误信息:
ServiceName - DLL initialization failure Initialization of the dynamic link library c:/windows/system32/user32.dll failed.The process is terminating abnormally.
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
142676 解决 User32.dll 初始化失败错误
|
• | 如果程序使用导入库来加载 Winmm.dll 文件,则 Winmm.dll 在程序启动时加载。在这种情况下,可能会在启动程序时出现延迟。因此,如果使用批处理文件多次启动此程序,性能会显著下降。 |
• | 如果程序使用 LoadLibrary 加载 Winmm.dll 文件,则 Winmm.dll 仅在程序需要时才加载。例如,如果在您打开菜单时 Explorer.exe 播放声音,则在第一次播放声音时加载 Winmm.dll 文件。在这种情况下,可能会在程序加载 Winmm.dll 文件时出现延迟。 |
• | 将调用应用程序在加载后立刻需要的所有函数放入一个 DLL,然后将该调用应用程序隐式链接到此 DLL。 |
• | 将调用应用程序不立即需要的函数放入另一个 DLL,然后将该应用程序显式链接到此 DLL。 |
• | Dependency Walker Dependency Walker 不仅仅是一个故障排除实用工具。Dependency Walker 提供了许多关于某一特定应用程序的模块布局的宝贵信息。此实用工具还提供关于每个模块的详细信息。有关更多信息,请参阅本文的“DLL 依赖性”部分。 |
• | DLL Universal Problem Solver 工具出现 DLL 文件版本冲突问题(也称为“DLL Hell”问题)时,可以使用 DLL Universal Problem Solver (DUPS) 工具查找这些问题并进行修复。DUPS 程序包是一个实用工具集,使用它可以跟踪和比较多台基于 Windows 的计算机上的 DLL 版本。有关更多信息,请访问下面的 Microsoft Web 站点:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsetup/html/dlldanger1.asp
|
• | RegSvr32 使用 Regsvr32 工具 (Regsvr32.exe) 可注册或注销 OLE 控件,例如可自行注册的 DLL 控制文件或 ActiveX 控件 (OCX) 文件。为排查 Windows、Microsoft Internet Explorer 或其他程序中的故障,您可能必须注册或注销这些文件。有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
249873 Regsvr32 用法和错误消息的说明
|
• | OLE/COM 对象查看器使用 OLE/COM 对象查看器,可通过读取类型库来查看控件(DLL 或 OCX 文件)的接口。有关更多信息,请访问下面的 Microsoft Web 站点:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/vcrefUsingOLECOMObjectViewerToViewControlsInterfaces.asp
|
• | .NET Framework 工具 Microsoft .NET Framework SDK 工具旨在使您创建、部署和管理使用 .NET Framework 的应用程序和组件的过程更轻松。.NET Framework 提供四组工具:配置和部署工具、调试工具、安全工具和常规工具。有关更多信息,请访问下面的 Microsoft Web 站点:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptools/html/cpconnetframeworktools.asp
|
• | 这些 EXE 或 DLL 文件中的代码是由语言编译器生成的。此代码不是 x86 计算机代码或专用于任何特定 CPU 操作系统的计算机代码。该代码是用 Microsoft 中间语言 (MSIL) 生成的。MSIL 可以用汇编语言编写。Microsoft 提供了 MSIL 汇编程序 (ILAsm.exe) 和 MSIL 反汇编程序 (ILDasm.exe)。 |
• | 此 DLL 或 EXE 文件包含编译器产生的元数据。公共语言运行库使用此元数据查找和加载文件中的类类型,确定对象实例在内存中的布局,解析方法调用和字段引用,将 MSIL 转换为本机代码,执行安全设置。 |
• | 您生成的组件不仅仅是 EXE 文件或 DLL 文件。它们是重新使用和部署的基本单位,是 .NET 中的一个程序集。您可以生成单文件程序集或多文件程序集。在单文件程序集中,程序集的文件扩展名是 .exe 或 .dll。 多文件程序集是这样一种思想:通过将一个可重用组件的逻辑概念和物理概念分离开来,将代码划分到多个文件中以满足您的需求。不过,您仍然可以保留集合作为版本控制和部署的一个单位。例如,如果您使用多文件程序集,则可以使用增量和按需下载。 |
• | 通过使用程序集,开发人员和管理员能够表示程序的各组件之间严格的版本依赖性。由于各组件是自描述的,所以您可以创建无影响安装。 在安装新应用程序的组件时,它们可能覆盖早期的应用程序的组件。如果发生此覆盖,早期的应用程序可能会运行不正常或停止运行。.NET Framework 体系结构让应用程序的各组件保持独立,这样应用程序始终能够加载正确的组件。因此,在安装后能按预期运行的应用程序将继续运行。(此功能解决了“DLL Hell”问题。) |
• | 程序集是 .NET 安全系统的一部分。程序集是申请和授予权限的单位。为了正确地在 .NET Framework 中强制实施版本控制、部署和安全功能,程序集起到了解析范围的作用,用于解析对类型的引用。 |
1. | 标识函数名称和导出该函数的 DLL 的名称。例如,通过指定 User32.dll 中的 MessageBox 函数,您可以标识函数 (MessageBox) 和函数的位置(User32.dll、User32 或 user32)。 |
2. | 创建一个类以包装 DLL 函数。在类中为要调用的每个 DLL 函数定义一个静态方法。例如,您可以调用 User32.dll 中的 MessageBox 函数,如下例所示: |
3. | 使用 DllImportAttribute 属性标识 DLL 和函数。使用外部“C”标记包装方法或函数。 |
4. | 通过传递在托管代码中定义的类的成员,调用托管类中的方法。 例如,将 MySystemTime 类的成员(这些成员是按顺序定义的)传递到 User32.dll 文件中的 GetSystemTime 方法,如下例中所示: |
1. | 将 .NET 程序集反汇编为对应的中间语言 (IL) 源代码和元数据。 |
2. | 重新汇编对等的 .NET 程序集中的 IL 代码、元数据和资源。 |
public static void HelloWorld() { Console.WriteLine("Hello World!"); } public static void HelloMyWorld() { Console.WriteLine("Hello My World!"); }
要对该程序集进行反汇编,请使用以下命令行:
.module MyAssembly.dll// MVID: {140B1958-FC28-4802-80E3-74C0F2015CDC}.imagebase 0x11000000.subsystem 0x00000003.file alignment 512.corflags 0x00000001
要进行更改,请执行下列操作:
1. | 要定义一个 v-table 修正并使之包含的插槽与要导出的方法一样多(上例中有两个方法),请按如下所示更改清单: |
2. | .corflags 指令设置运行库头文件标志。默认情况下,.corflags 指令指定值 1。 此值等于 COMIMAGE_FLAGS_ILONLY 标志(在 .NET Framework SDK 中的 CorHdr.h 包含文件中定义)。 设置此标志后,Windows XP 加载程序会忽略该程序集文件的主要部分,而且不进行修正。 在您尝试使用程序集导出时,这会导致致命错误。 所以,如果您希望程序集在 Windows XP 上运行,必须指定 COMIMAGE_FLAGS_32BITREQUIRED 标志(此值为 2): |
.method public hidebysig static void HelloWorld() cil managed { // Code size 11 (0xb) .maxstack 1 .line 18:4 IL_0000: ldstr "Hello World!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) .line 19:3 IL_000a: ret } // end of method Class1::HelloWorld .method public hidebysig static void HelloMyWorld() cil managed { // Code size 11 (0xb) .maxstack 1 .line 23:4 IL_0000: ldstr "Hello My World!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) .line 24:3 IL_000a: ret } // end of method Class1::HelloMyWorld
要让这些方法作为非托管导出,请对它们进行如下更改: .method public hidebysig static void HelloWorld() cil managed { // Code size 11 (0xb) .maxstack 1 .line 18:4 .vtentry 1:1 .export [1] as HelloWorld IL_0000: ldstr "Hello World!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) .line 19:3 IL_000a: ret } // end of method Class1::HelloWorld .method public hidebysig static void HelloMyWorld() cil managed { // Code size 11 (0xb) .maxstack 1 .line 23:4 .vtentry 1:2 .export [2] as HelloMyWorld IL_0000: ldstr "Hello My World!" IL_0005: call void [mscorlib]System.Console::WriteLine(string) .line 24:3 IL_000a: ret } // end of method Class1::HelloMyWorld
每个方法需要一个 .vtentry 指令以将该方法链接到 v-table 修正(粗体显示指定的插槽号),还需要一个 .export 指令以指定导出的名称。 es:MyAssembly.resMicrosoft (R) .NET Framework IL Assembler. Version 1.0.3705.0Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.Assembling 'MyAssembly.il' , no listing file, to DLL --> 'MyAssembly_new.dll'Source file is ANSIAssembled method Class1::MainAssembled method Class1::HelloWorldAssembled method Class1::HelloMyWorldAssembled method Class1::.ctorCreating PE fileEmitting members:GlobalClass 1 Methods: 4;Resolving member refs: 1 -> 1 defs, 0 refsWriting PE fileOperation completed successfully
使用反向 PInvoke 时可能遇到的主要困难是维护问题。如果要通过创造性往返修改编译器生成的程序集,请重新手动更新程序集以导出例程。