调用DLL命令后发现堆栈错误怎么办
发布网友
发布时间:2022-04-26 16:15
我来回答
共4个回答
热心网友
时间:2023-10-13 22:24
现在是更深入地进行探讨的时候了。在对托管代码进行 P/INVOKE 调用时,DLLIMPORTATTRIBUTE 类型扮演着重要的角色。DLLIMPORTATTRIBUTE 的主要作用是给 CLR 指示哪个 DLL 导出您想要调用的函数。相关 DLL 的名称被作为一个构造函数参数传递给 DLLIMPORTATTRIBUTE。
如果您无法肯定哪个 DLL 定义了您要使用的 WINDOWS API 函数,PLATFORM SDK 文档将为您提供最好的帮助资源。在 WINDOWS API 函数主题文字临近结尾的位置,SDK 文档指定了 C 应用程序要使用该函数必须链接的 .LIB 文件。在几乎所有的情况下,该 .LIB 文件具有与定义该函数的系统 DLL 文件相同的名称。例如,如果该函数需要 C 应用程序链接到 KERNEL32.LIB,则该函数就定义在 KERNEL32.DLL 中。您可以在 MESSAGEBEEP 中找到有关 MESSAGEBEEP 的 PLATFORM SDK 文档主题。在该主题结尾处,您会注意到它指出库文件是 USER32.LIB;这表明 MESSAGEBEEP 是从 USER32.DLL 中导出的。
可选的 DLLIMPORTATTRIBUTE 属性
除了指出宿主 DLL 外,DLLIMPORTATTRIBUTE 还包含了一些可选属性,其中四个特别有趣:ENTRYPOINT、CHARSET、SETLASTERROR 和 CALLINGCONVENTION。
ENTRYPOINT 在不希望外部托管方法具有与 DLL 导出相同的名称的情况下,可以设置该属性来指示导出的 DLL 函数的入口点名称。当您定义两个调用相同非托管函数的外部方法时,这特别有用。另外,在 WINDOWS 中还可以通过它们的序号值绑定到导出的 DLL 函数。如果您需要这样做,则诸如“#1”或“#129”的 ENTRYPOINT 值指示 DLL 中非托管函数的序号值而不是函数名。
CHARSET 对于字符集,并非所有版本的 WINDOWS 都是同样创建的。WINDOWS 9X 系列产品缺少重要的 UNICODE 支持,而 WINDOWS NT 和 WINDOWS CE 系列则一开始就使用 UNICODE。在这些操作系统上运行的 CLR 将UNICODE 用于 STRING 和 CHAR 数据的内部表示。但也不必担心 — 当调用 WINDOWS 9X API 函数时,CLR 会自动进行必要的转换,将其从 UNICODE转换为 ANSI。
如果 DLL 函数不以任何方式处理文本,则可以忽略 DLLIMPORTATTRIBUTE 的 CHARSET 属性。然而,当 CHAR 或 STRING 数据是等式的一部分时,应该将 CHARSET 属性设置为 CHARSET.AUTO。这样可以使 CLR 根据宿主 OS 使用适当的字符集。如果没有显式地设置 CHARSET 属性,则其默认值为 CHARSET.ANSI。这个默认值是有缺点的,因为对于在 WINDOWS 2000、WINDOWS XP 和 WINDOWS NT�0�3 上进行的 INTEROP 调用,它会消极地影响文本参数封送处理的性能。
应该显式地选择 CHARSET.ANSI 或 CHARSET.UNICODE 的 CHARSET 值而不是使用 CHARSET.AUTO 的唯一情况是:您显式地指定了一个导出函数,而该函数特定于这两种 WIN32 OS 中的某一种。READDIRECTORYCHANGESW API 函数就是这样的一个例子,它只存在于基于 WINDOWS NT 的操作系统中,并且只支持 UNICODE;在这种情况下,您应该显式地使用 CHARSET.UNICODE。
有时,WINDOWS API 是否有字符集关系并不明显。一种决不会有错的确认方法是在 PLATFORM SDK 中检查该函数的 C 语言头文件。(如果您无法肯定要看哪个头文件,则可以查看 PLATFORM SDK 文档中列出的每个 API 函数的头文件。)如果您发现该 API 函数确实定义为一个映射到以 A 或 W 结尾的函数名的宏,则字符集与您尝试调用的函数有关系。WINDOWS API 函数的一个例子是在 WINUSER.H 中声明的 GETMESSAGE API,您也许会惊讶地发现它有 A 和 W 两种版本。
SETLASTERROR 错误处理非常重要,但在编程时经常被遗忘。当您进行 P/INVOKE 调用时,也会面临其他的挑战 — 处理托管代码中 WINDOWS API 错误处理和异常之间的区别。 可以给您一点建议。
如果您正在使用 P/INVOKE 调用 WINDOWS API 函数,而对于该函数,您使用 GETLASTERROR 来查找扩展的错误信息,则应该在外部方法的 DLLIMPORTATTRIBUTE 中将 SETLASTERROR 属性设置为 TRUE。这适用于大多数外部方法。
这会导致 CLR 在每次调用外部方法之后缓存由 API 函数设置的错误。然后,在包装方法中,可以通过调用类库的 SYSTEM.RUNTIME.INTEROPSERVICES.MARSHAL 类型中定义的 MARSHAL.GETLASTWIN32ERROR 方法来获取缓存的错误值。 的建议是检查这些期望来自 API 函数的错误值,并为这些值引发一个可感知的异常。对于其他所有失败情况(包括根本就没意料到的失败情况),则引发在 SYSTEM.COMPONENTMODEL 命名空间中定义的 WIN32EXCEPTION,并将 MARSHAL.GETLASTWIN32ERROR 返回的值传递给它。如果您回头看一下图 1 中的代码,您会看到 在 EXTERN MESSAGEBEEP 方法的公共包装中就采用了这种方法。
CALLINGCONVENTION 将在此介绍的最后也可能是最不重要的一个 DLLIMPORTATTRIBUTE 属性是 CALLINGCONVENTION。通过此属性,可以给 CLR 指示应该将哪种函数调用约定用于堆栈中的参数。CALLINGCONVENTION.WINAPI 的默认值是最好的选择,它在大多数情况下都可行。然而,如果该调用不起作用,则可以检查 PLATFORM SDK 中的声明头文件,看看您调用的 API 函数是否是一个不符合调用约定标准的异常 API。
通常,本机函数(例如 WINDOWS API 函数或 C- 运行时 DLL 函数)的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数 WINDOWS API 函数都是首先将函数的最后一个参数推入堆栈,然后由被调用的函数负责清理该堆栈。相反,许多 C-运行时 DLL 函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈,将堆栈清理工作交给调用者。
幸运的是,要让 P/INVOKE 调用工作只需要让外围设备理解调用约定即可。通常,从默认值 CALLINGCONVENTION.WINAPI 开始是最好的选择。然后,在 C 运行时 DLL 函数和少数函数中,可能需要将约定更改为 CALLINGCONVENTION.CDECL。
热心网友
时间:2023-10-13 22:24
本文说明什么是动态链接库 (DLL) 以及在使用 DLL 时可能发生的各种问题。
然后,本文说明在开发您自己的 DLL 时应该考虑的一些高级问题。在说明什么是 DLL 的过程中,本文将说明动态链接方法、DLL 依赖性、DLL 入口点、导出 DLL 函数以及 DLL 故障排除工具。
本文最后将从较高的层次对 DLL 与 Microsoft .NET Framework 程序集作一比较。
简介
对于“适用于”一节中列出的 Microsoft Windows 操作系统,操作系统的大量功能是由动态链接库 (DLL) 提供的。另外,当您在这些 Windows 操作系统之一上运行某一程序时,该程序的很多功能可能是由 DLL 提供的。例如,某些程序可能包含很多不同的模块,而该程序的每个模块都包含在 DLL 中并从中分发。
使用 DLL 有助于促进代码的模块化、代码重用、内存的有效使用和减少所占用的磁盘空间。因此,操作系统和程序能够更快地加载和运行,并且在计算机中占用较少的磁盘空间。
当程序使用 DLL 时,一个称为依赖性的问题可能导致该程序无法运行。当程序使用 DLL 时,就会创建一个依赖项。如果其他程序改写和损坏了该依赖项,原来的那个程序就可能无法成功运行。
在引入 Microsoft .NET Framework 之后,大多数依赖性问题都已经通过使用程序集消除了。
什么是 DLL?
DLL 是一个包含可由多个程序同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32 DLL 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于促进代码重用和内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。
此外,可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您可能具有一个工资计算程序,而税率每年都会更改。当这些更改被隔离到 DLL 中以后,您无需重新生成或安装整个程序就可以应用更新。
下表说明了 Windows 操作系统中的一些作为 DLL 实现的文件: �6�1ActiveX 控件 (.ocx) 文件
ActiveX 控件的一个示例是日历控件,它使您可以从日历中选择日期。
�6�1控制面板 (.cpl) 文件
.cpl 文件的一个示例是位于控制面板中的项。每个项都是一个专用 DLL。
�6�1设备驱动程序 (.drv) 文件
设备驱动程序的一个示例是控制打印到打印机的打印机驱动程序。
DLL 的优点
下表说明了当程序使用 DLL 时提供的一些优点: �6�1使用较少的资源
当多个程序使用同一个函数库时,DLL 可以减少在磁盘和物理内存中加载的代码的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在 Windows 操作系统上运行的程序。
�6�1推广模块式体系结构
DLL 有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。
�6�1简化部署和安装
当 DLL 中的函数需要更新或修复时,部署和安装 DLL 不要求重新建立程序与该 DLL 的链接。此外,如果多个程序使用同一个 DLL,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方 DLL 时,此问题可能会更频繁地出现。
DLL 依赖项
当某个程序或 DLL 使用其他 DLL 中的 DLL 函数时,就会创建依赖项。因此,该程序就不再是独立的,并且如果该依赖项被损坏,该程序就可能遇到问题。例如,如果发生下列操作之一,则该程序可能无法运行: �6�1依赖 DLL 升级到新版本。
�6�1修复了依赖 DLL。
�6�1依赖 DLL 被其早期版本覆盖。
�6�1从计算机中删除了依赖 DLL。
这些操作通常称为 DLL 冲突。如果没有强制实现向后兼容性,则该程序可能无法成功运行。
下表说明了为了帮助最大限度地减少依赖性问题而在 Microsoft Windows 2000 和较高版本的 Windows 操作系统中引入的更改: �6�1Windows 文件保护
在 Windows 文件保护中,操作系统禁止未经授权的代理更新或删除系统 DLL。因此,当程序安装操作尝试删除或更新被定义为系统 DLL 的 DLL 时,Windows 文件保护将寻找有效的数字签名。
�6�1专用 DLL
通过专用 DLL 可以使程序避免遭受对共享 DLL 进行的更改。专用 DLL 使用版本特定信息或空 .local 文件来强制要求程序所使用的 DLL 的版本。要使用专用 DLL,请在程序根文件夹中查找 DLL。然后,对于新程序,请向该 DLL 中添加版本特定信息。对于旧程序,请使用空 .local 文件。每个方法都告诉操作系统使用位于程序根文件夹中的专用 DLL。
热心网友
时间:2023-10-13 22:24
.DLL命令 GetWindowLongA, 整数型, "user32", "GetWindowLongA", , 从指定窗口的结构中取得信息 由nIndex决定。零表示出错。会设置GetLastError
.参数 窗口句柄, 整数型, , 欲为其获取信息的窗口的句柄
.参数 属性, 整数型, , 欲取回的信息,可以是下述任何一个常数:;GWL_EXSTYLE:扩展窗口样式;GWL_STYLE:窗口样式;GWL_WNDPROC:该窗口的窗口函数的地址;GWL_HINSTANCE:拥有窗口的实例的句柄;GWL_HWNDPARENT:该窗口之父的句柄。不要用SetWindowWord来改变这个值;GWL_ID:对话框中一个子窗口的标识符;GWL_USERDATA:含义由应用程序规定;DWL_DLGPROC:这个窗口的对话框函数地址;DWL_MSGRESULT:在对话框函数中处理的一条消息返回的值;DWL_USER:含义由应用程序规定;
.DLL命令 SetWindowLongA, 整数型, "user32", "SetWindowLongA", , 在窗口结构中为指定的窗口设置信息 指定数据的前一个值
.参数 hwnd, 整数型, , 欲为其取得信息的窗口的句柄
.参数 nIndex, 整数型, , 请参考GetWindowLong函数的nIndex参数的说明
.参数 dwNewLong, 整数型, , 由nIndex指定的窗口信息的新值;
.DLL命令 API_SetLayeredWindowAttributes, 整数型, "user32.dll", "SetLayeredWindowAttributes"
.参数 hwnd, 整数型
.参数 crKey, 整数型
.参数 bAlpha, 字节型
.参数 dwFlags, 整数型
热心网友
时间:2023-10-13 22:25
去网站下载丢失的DLL文件覆盖下即可