培训
学习路径
使用 Success by Design for Dynamics 365 应用解决方案 - Training
FastTrack for Dynamics 365 旨在帮助客户和合作伙伴成功部署 Dynamics 365 解决方案。 要交付本服务,解决方案架构师应使用 Success by Design。 本学习路径讨论 Success by Design 概念。
**更新时间:**
重要的 API
创建 DLL 为开发人员带来了许多挑战。 DLL 没有系统强制执行的版本控制。 当系统上存在多个版本的 DLL 时,由于很容易被覆盖,且缺少版本控制架构,会导致产生依赖项和 API 冲突。 开发环境中的复杂性、加载器实现和 DLL 依赖项造成了加载顺序和应用程序行为方面的脆弱性。 最后,许多应用程序依赖于 DLL,并且具有复杂的依赖项集,应用程序必须遵循它们才能正常运行。 本文档为 DLL 开发人员提供了指南,帮助构建更可靠、可移植和可扩展的 DLL。
DllMain 中的不当同步可能会导致应用程序在未初始化的 DLL 中死锁或访问数据或代码。 从 DllMain 中调用某些函数会导致此类问题。
DllMain 在加载器锁被持有时调用。 因此,对可以在 DllMain 中调用的函数施加了重大限制。 因此,DllMain 旨在通过使用 Microsoft® Windows® API 的一小部分来执行最小的初始化任务。 不能调用 DllMain 中直接或间接尝试获取加载器锁的任何函数。 否则,将引入应用程序死锁或崩溃的可能性。 DllMain 实现中的错误可能会危及整个进程及其所有线程。
理想的 DllMain 只是一个空存根。 但是,鉴于许多应用程序的复杂性,这通常过于严格。 DllMain 的一个很好的经验法则是尽可能地推迟初始化。 延迟初始化会增加应用程序的稳定性,因为加载器锁被持有时不会执行此类初始化。 此外,延迟初始化使你能够安全地使用更多 Windows API 功能。
某些初始化任务无法推迟。 例如,如果文件格式不正确或包含垃圾,则依赖于配置文件的 DLL 应无法加载。 对于这种类型的初始化,DLL 应会尝试操作并快速失败,而不是通过完成其他工作来浪费资源。
不应从 DllMain 中执行以下任务:
可以在 DllMain 中安全地执行以下任务:
实现使用多个同步对象(如锁)的代码时,必须遵循锁顺序。 如果需要一次获取多个锁,则必须定义一个称为锁层次结构或锁顺序的显式优先级。 例如,如果在代码中的某个位置在锁 B 之前获取了锁 A,并在代码中的其他位置在锁 C 之前获取了锁 B,则锁顺序为 A、B、C,并且应在整个代码中遵循此顺序。 锁顺序反转发生在未遵循锁顺序时,例如,如果在锁 A 之前获取了锁 B。锁顺序反转可能会导致难以调试的死锁。 为了避免此类问题,所有线程必须按相同的顺序获取锁。
请务必注意,加载器使用已获取的加载器锁调用 DllMain,因此加载器锁在锁层次结构中应具有最高优先级。 另请注意,代码只需要获取正确同步所需的锁,它不必获取层次结构中定义的每个锁。 例如,如果代码的某个部分只需锁 A 和 C 就能进行正确同步,则代码应在获取锁 C 之前获取锁 A,且代码不需要也获取锁 B。此外,DLL 代码无法显式获取加载器锁。 如果代码必须调用可以间接获取加载器锁的 API(例如 GetModuleFileName),并且代码还必须获取专用锁,则代码应在获取锁 P 之前调用 GetModuleFileName,从而确保遵守加载顺序。
图 2 是说明锁顺序反转的示例。 假设有一个主线程包含 DllMain 的 DLL。 库加载器会获取加载器锁 L,然后调用 DllMain。 主线程会创建同步对象 A、B 和 G 以序列化对其数据结构的访问,然后尝试获取锁 G。已成功获取锁 G 的工作线程随后调用尝试获取加载器锁 L 的函数,例如 GetModuleHandle。因此,工作线程在 L 上被阻止,主线程在 G 上被阻止,从而导致死锁。
要防止由锁顺序反转导致的死锁,所有线程都应始终尝试按定义的加载顺序获取同步对象。
假设有一个 DLL 会在初始化中创建工作线程。 DLL 清理后,必须与所有工作线程同步,以确保数据结构处于一致状态,然后终止工作线程。 目前,无法完全直接解决在多线程环境中干净地同步和关闭 DLL 的问题。 本部分介绍在 DLL 关闭期间线程同步的当前最佳做法。
进程退出期间 DllMain 中的线程同步
DLL 卸载期间 DLL_THREAD_DETACH 的 DllMain 中的线程同步
如果 DLL 在其所有线程创建后且它们开始执行前被卸载,则这些线程可能会崩溃。 如果 DLL 在其 DllMain 中作为初始化的一部分创建了线程,则某些线程可能尚未完成初始化,并且其 DLL_THREAD_ATTACH 消息仍在等待被传递到 DLL。 在这种情况下,如果卸载 DLL,它将开始终止线程。 但是,某些线程可能会被挡在加载器锁后面。 它们的 DLL_THREAD_ATTACH 消息会在取消映射 DLL 后处理,从而导致进程崩溃。
建议遵循以下准则:
培训
学习路径
使用 Success by Design for Dynamics 365 应用解决方案 - Training
FastTrack for Dynamics 365 旨在帮助客户和合作伙伴成功部署 Dynamics 365 解决方案。 要交付本服务,解决方案架构师应使用 Success by Design。 本学习路径讨论 Success by Design 概念。