windows dll 装载过程

2023-11-11

windows dll 装载过程  2010-12-04 19:13:56

分类:

 

         Windows系统平台上,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Microsoft Windows自己就将一些主要的系统功能以DLL模块的形式实现。例如IE中的一些基本功能就是由DLL文件实现的,它可以被其它应用程序调用和集成。一般来说,DLL是一种磁盘文件(通常带有DLL扩展名,是标准win32可执行文件-“PE”格式),它由全局数据、服务函数和资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分,进程中所有线程都可以调用其中的函数。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。

         Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。

         DLL文件中包含一个导出函数表(存在于PE.edata节中)。这些导出函数由它们的符号名和称为标识号的整数与外界联系起来。函数表中还包含了DLL中函数的地址。当应用程序加载DLL模块时时,它并不知道调用函数的实际地址,但它知道函数的符号名和标识号。动态链接过程在加载的DLL模块时动态建立一个函数调用与函数地址的对应表。如果重新编译和重建DLL文件,并不需要修改应用程序,除非你改变了导出函数的符号名和参数序列。

简单的DLL文件只为应用程序提供导出函数,比较复杂的DLL文件除了提供导出函数以外,还调用其它DLL文件中的函数。

         每个DLL都有一个入口函数(DLLMain),系统在特定环境下会调用DLLMain。在下面的事件发生时会调用dll入口函数:1.进程装载DLL2.进程卸载DLL3.DLL在被装载之后创建了新线程。4. DLL在被装载之后一个线程被终止了。

         应用程序导入函数与DLL文件中的导出函数进行链接有两种方式:隐式链接和显式链接。

         隐式链接(load-time dynamic linking)是指在应用程序中不需指明DLL文件的实际存储路径,程序员不需关心DLL文件的实际装载(由编译器自动完成地址分配)。采用隐式链接方式,程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的LIB导入文件。该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。我们使用的大部分系统Dll就是通过这样的方式链接的。若找不到需要的Dll则会给出一个Dll缺少的错误消息。

         显式链接(run-time dynamic linking)与此相反。用户程序在编译的时候并没有指明需要哪些Dll,而是在运行起来之后调用Win32 LoadLibary()函数,去装载Dll。若没有找到Dll则这个函数就会返回一个错误。在用LoadLibary()函数装载Dll之后,应用程序还需要用GetProcAdress()函数去获得Dll输出函数的地址。显式链接方式对于集成化的开发语言比较适合。有了显式链接,程序员就不必再使用导入文件,而是直接调用Win32 LoadLibary()函数,并指定DLL的路径作为参数。还要说明一点的就是Known Dlls就是保证在通过LoadLibary()去装载系统Dll的时候,只从特定的系统目录去装载,防止装载错。装载的时候会去看注册表下是否有一样的注册表键名。如果是装载windows\system32\目录下的对应的Dll

         Dll的搜索顺序,在Windows上有个注册表键值决定了Dll的搜索顺序:HKLM\System\CurrentControlSet\SessionManager\SafeDllSearchMode。在vista,server2003,xp sp2中这个值为1,在xp,2000 sp4中为01值时的搜素顺序为:1.可执行文件所在目录,2.系统目录windows\system32\3. 16位系统目录,4.windows目录,5.当前进程目录。6.环境变量PATH中的目录。0值时的搜素顺序为:1.可执行文件所在目录,2. 当前进程目录。3.系统目录windows\system32\4. 16位系统目录,5.windows目录,6.环境变量PATH中的目录。

DLL的加载与连接

    Windows DLL装入(ntdll.dll)和连接是通过ntdll.dll中一个函数LdrInitializeThunk实现的。先对LdrInitializeThunk()这个函数名作些解释“Ldr显然是“Loader”的缩写。而“Thunk”意为“翻译”、“转换”、或者某种起着“桥梁”作用的东西。这个词在一般的字典中是查不到的,但却是个常见于微软的资料、文档中术语。这个术语起源于编译技术,表示一小片旨在获取某个地址的代码,最初用于函数调用时“形参”和“实参”结合。后来这个术语有了不少新的特殊含义和使用,但是DLL的动态连接与函数调用时“形实结合”确实有着本质的相似。

         由于Windows没有公开这个函数的代码,所以学习起来比较困难,只能通过查阅一些资料来大概猜测这个函数的实现。这个过程中也参看了很多ReactOSReactOS是一个免费而且完全兼容 Microsoft Windows XP 的操作系统。ReactOS 旨在通过使用类似构架和提供完整公共接口实现与 NT 操作系统二进制下的应用程序和驱动设备的完全兼容。)的LdrInitializeThunk()函数实现源代码。

         在进入这个函数之前,目标 EXE映像已经被映射到当前进程的用户空间,系统DLL ntdll.dll的映像也已经被映射,但是并没有在EXE映像与ntdll.dll映像之间建立连接 (实际上 EXE映像未必就直接调用ntdll.dll中的函数)LdrInitializeThunk()ntdll.dll中不经连接就可进入的函数,实质上就是ntdll.dll的入口。除ntdll.dll以外,别的 DLL都还没有被装入(映射)。此外,当前进程(除内核中的“进程控制块”EPROCESS等数据结构外)在用户空间已经有了一个“进程环境块”PEB,以及该进程的第一个“线程环境块”TEB。这就是进入 LdrInitializeThunk()前的“当前形势”。

         PEB中有一个字段Ldr是个PEB_LDR_DATA结构指针,所指向的数据结构用来为本进程维持三个“模块”队列、即InLoadOrderModuleListInMemoryOrderModuleList、和InInitializationOrderModuleList。这里所谓“模块”就是PE格式的可执行映像,包括EXE映像和DLL映像。前两个队列都是模块队列,第三个是初始化队列。两个模块队列的不同之处在于排列的次序,一个是按装入的先后,一个是按装入的位置。每当为本进程装入一个模块、即.exe映像或DLL映像时,就要为其分配,创建一个LDR_DATA_TABLE_ENTRY数据结构,并将其挂入InLoadOrderModuleList。然后,完成对这个模块的动态连接以后,就把它挂入InInitializationOrderModuleList队列,以便依次调用它们的初始化函数。相应地,LDR_DATA_TABLE_ENTRY数据结构中有三个队列头,因而可以同时挂在三个队列中。在我做的小实验当中就是通过查找这三个队列,来将当前进程的Dll加载信息显示出来的。具体的实例请见我的实验说明文档。

         LdrInitializeThunk()中,最开始为做的事情就是将加载的模块信息存放在PEB中的ldr字段,如上面一段文字中所述。之后,LdrInitializeThunk()函数又调用了一个叫LdrPEStartup()的函数。LdrPEStartup()函数首先判断了“期望地址”是否可用,PE映像的NtHeader(peb中有个ImageBaseAddress的地址,代表exe映像在用户空间的位置,在这个地址指向的数据结构中就有NtHeader的结构)中有个指针,指向一个OptionalHeader。在OptionalHeader中有个字段ImageBase,是具体映像建议、或者说希望被装入的地址,我们称之为“愿望地址”。在装入一个映像时,只要相应的区间(取决于它的期望地址和大小)空闲,就总正常装入。但是如果与已经被占用的区间相冲突,就只好利用LdrPerformRelocations()换个地方。

         那么映像的愿望地址有着什么物理的或者逻辑的意义呢?我们知道,软件在编译以后有个连接的过程,即为函数的调用者落实被调用函数的入口地址、为全局变量(按绝对地址)的使用者落实变量地址的过程。连接有静态和动态两种,静态连接是在“制造”软件时进行的,而动态连接则是在使用软件时进行的。尽管EXE模块和DLL模块之间的连接是动态连接,但是EXEDLL模块内部的连接却是静态连接。既是静态连接,就必须为模块的映像提供一个假定的起点地址。如果以此假定地址为基础进行连接以后就不可变更,使用时必须装入到这个地址上,那么这个地址就是固定的“指定地址”了。早期的静态连接往往都是使用指定地址的。但是,如果允许按假定地址连接的映像在实际使用时进行“重定位”,那么这假定地址就是可浮动的“愿望地址”了。可“重定位”的静态连接当然比固定的静态连接灵活。事实上,要是没有可“重定位”的静态连接技术,DLL的使用就无法实现,因为根本就不可能事先为所有可能的DLL划定它们的装入位置和大小。至于可“重定位”静态连接的实现,则一般都是采用间接寻址,通过指针来实现。

         所谓重定位,就是计算出实际装入地址与建议装入地址间的位移a,然后调整每个重定位块中的每一个重定位项、即指针,具体就是在指针上加a。而映像中使用的所有绝对地址(包括函数入口、全局量数据的位置)实际上用的都是间接寻址,每个这样的地址都有个指针存在于某个重定位块中。

         完成了可能需要的EXE映像重定位以后,下一个主要的操作就是LdrFixupImports()了。实际上这才是关键所在,它所处理的就是当前模块所需DLL模块的装入和连接。各DLL的程序入口记录在它们的LDR_DATA_TABLE_ENTRY数据结构中借助InInitializationOrderModuleList队列就可依次调用所有DLL的初始化函数。

         NtHeaderOptionalHeader中有个数组DataDirectory[],其中之一是重定位目录。除此之外,数组中还有“(普通)引入(import)”、“绑定引入(bound import)”以及其它多种目录,但是我们在这里只关心“引入”和“绑定引入”。这两个目录都是用于库函数的引入,但是作用不同,目录项的数据结构也不同。每个引入目录项都代表着一个被引入模块,其模块名、即文件名在dwRVAModuleNameReactOS中的名字,下同)所指的地方。需要从同一个被引入模块引入的函数通常有很多个,dwRVAFunctionNameList指向一个字符串数组,数组中的每一个字符串都是一个函数名;与此相对应,dwRVAFunctionAddressList则指向一个指针数组。这两个数组是平行的,同一个函数在两个数组中具有相同的下标。从一个被引入模块中引入一个函数的过程大体上就是:根据函数名在被引入模块的引出目录中搜索,找到目标函数以后就把它实际装入后的入口地址填写到指针数组中的相应位置上。但是,这个过程可能是个开销相当大、速度比较慢的过程。为此,又发展起一种称为“绑定”的优化。

所谓绑定,就是在软件的编译,连接过程中先对使用时的动态连接来一次预演,预演时假定所有的DLL都被装入到它们的愿望地址上,然后把预演中得到的被引入函数的地址直接记录在引入者模块中相应引入目录下的指针数组中。这样,使用软件时的动态连接就变得很简单快捷,因为实际上已经事先连接好了。其实“绑定引入”和静态连接并无实质的不同。但是,各模块的版本配套就成为一个问题,因为万一使用的某个DLL不是当初绑定时的版本,而且其引出目录又发生了变化,就有可能引起混乱。为此,PE格式增加了一种“绑定引入”目录,相关的机制会进行判断。但是,“绑定引入”毕竟不是很可靠的,万一发现版本不符就不能使用原先的绑定了。所以“绑定引入”不能单独存在,而必须有普通引入作为后备。如果不符就不能按“绑定引入”目录处理引入,而只好退而求其次,改成按普通“引入”目录处理引入。另一方面,所谓“绑定”是指当被引入模块装入在预定位置上时的地址绑定,如果被引入模块的装入位置变了,就得对原先所绑定的地址作相应的调整、即“重定位”。

LdrFixupImports()函数首先从映像头部获取指向“引入”目录和“绑定引入”目录的指针。若存在“绑定引入”目录,则先通过LdrpGetOrLoadModule()找到或装入(映射)被引入模块的映像。首先当然是在模块队列中寻找,找不到就从被引入模块的磁盘文件装入。之后检查绑定版本是否一致,如果不一致就退而求其次,通过LdrpProcessImportDirectory()处理引入。当然,那样一来效率就要降低了。如果一致,则返回(因为在“预演”中已经连接好,效率当然高了)。而LdrpProcessImportDirectory()才是真正意义上的动态连接!!(说了这么多原来才开始……)。

LdrpProcessImportDirectory()首先根据目录项中的两个位移量取得分别指向函数名字符串数组和函数指针数组的指针。这两个数组是平行的(前面有介绍),然后对字符串数组中的元素计数,得到该数组的大小IATSize。显然,函数指针数组的大小也是IATSize。这里IAT是“引入地址表(Imported Address Table)”的缩写,其实就是函数指针数组。这个数组在映像内部,其所在的页面在装入映像时已被加上写保护,而下面要做的事正是要改变这些指针的值,所以先要通过NtProtectVirtualMemory()把这些页面的访问模式改成可读可写。做完这些准备之后,下面就是连接的过程了,那就是根据需要把被引入模块所引出的函数入口(地址)填写到引入者模块的IAT中。与当前模块中的两个数组相对应,在被引入模块的“引出”目录中也有两个数组,说明本模块引出函数的名称和入口地址(在映像中的位移)。当然,这两个数组也是平行的。要获取被引入模块中的函数入口有两种方法,即按序号(Ordinal)引入和按函数名引入。从而分别调用LdrGetExportByOrdinal()LdrGetExportByName()。这两个函数都返回目标函数在本进程用户空间中的入口地址,把它填写入当前模块引入目录函数指针数组中的相应元素,就完成了一个函数的连接。当然,同样的操作要循环实施于当前模块需要从给定模块引入的所有函数,并且(在上一层)循环实施于所有的被引入模块。完成了对一个被引入模块的连接之后,又调用NtProtectVirtualMemory()恢复当前模块中给定目录项内函数指针数组所在页面的保护。

到此,我们大概的清楚Windows Dll的加载与连接过程。

 

总结与感想

         以上的Dll加载与连接过程有点抽象与混乱,在这里进行总结,绝体的函数加载关系如下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

windows dll 装载过程 的相关文章

  • 创建非托管常规 MFC DLL 并从托管 C++ .NET 应用程序调用它时出现问题

    我有几个关于 DLL 的问题 我尝试了很多 但无法获得完整的图片 大多数示例都是用 C 等编写的 使用 VS2005 中的向导 我创建了一个非托管 MFC 常规 DLL 由于剩余代码 必须是 MFC 然后我尝试将其导入 VS2005 管理的
  • 是什么破坏了 .net 二进制 (dll) 接口

    考虑两个 net dll 首先 application dll 包含主要业务逻辑和数据访问代码 第二个 webservice dll 主要由 WebMethod 组成 这些 WebMethod 链接到 application dll 的对象
  • 调用 SWIG 生成的 JNI 时出现 UnsatisfiedLinkError?

    我正在尝试创建一个可从 Java 调用的 C 动态库 我在下面编译了一个DLLCygwin http www cygwin com using SWIG http www swig org Doc1 3 Java html compilin
  • 不使用 Obfuscator 的 DLL 加密

    事实上 我被分配了一项任务来保护我的 DLL 这样 如果我将我的软件部署到任何系统 就没有人可以修改或读取我的 DLL 你能帮我如何在不使用混淆器的情况下对其进行加密吗 您将需要软件来执行此操作 以下是您可能需要考虑的一些软件 Eazfus
  • 如何判断您的 C# 应用程序正在使用哪个 dll?

    我有一个使用 dll 的应用程序 该 dll 充满了位于位置 x 的 gui 应用程序代码 以前有 1 个按钮 现在有 2 个 当我启动我的应用程序时 我期望看到的是一个带有 2 个按钮的 gui 但是我看到一个带有 1 个按钮的 gui
  • 为什么有些程序需要 .lib 和 .dll 才能运行

    当我要设置我的开发环境时 SDLSimple DirectMediaLayer 我下载了网站提供的包 从readme txt文件我发现我需要两者 lib and dll 我的问题是 我不确定我的理解是否正确 在我看来 lib for win
  • 找不到如何使用 HttpContent

    我正在尝试使用HttpContent HttpContent myContent HttpContent Create SOME JSON 但我没有找到定义它的 DLL 首先 我尝试添加对Microsoft Http也System Net
  • 未找到入口点

    当我尝试运行链接到 DLL 的应用程序 我有一段时间没有更改代码 因为它工作正常 时 遇到了一个奇怪的错误 这个 DLL 曾经可以工作 但我一直在更改 DLL 的代码并使其编译正常 不幸的是 当尝试运行该应用程序时 GameTest001
  • 使用两个具有相同命名空间的 .NET 库

    我目前正在为一家公司维护一些旧代码 正如所发生的那样 我正在修改的当前应用程序使用旧版本的内部库 我们将其称为 Lib1 dll 他们还有一个名为 Lib2 dll 的新版本库 它在许多方面对以前的库进行了改进 不幸的是 Lib2 不向后兼
  • ws2_32.lib 与 libws2_32.a,有什么区别以及如何将 libws2_32 链接到 NB 项目?

    我使用 NetBeans Windows 和 Cygwin 以及 g 编译器 我正在研究 Windows Sockets 2 我所做的一切都是用 MS 编写的manual http msdn microsoft com en us libr
  • 在 DLL 中,函数表的结构如何?

    我一直在研究不明确支持我的操作系统的设备库的实现 特别是 我有一个反汇编的 DLL 以及大量的支持源代码 现在 功能表 导出表是如何构造的 我的理解是 第一个结构 data部分是 RVA 表 接下来是通过索引链接到第一个地址表的字符串表 这
  • 如何在 Visual C++ 中创建 ActiveX DLL

    是否有在 Visual Studio 2008 C 中创建 ActiveX DLL 的教程 参考 我有一个使用 DLLRegisterServer UnregisterServer 构建的 DLL 并且已注册 但我在弄清楚使用什么名称来引用
  • 如何使用 DLL 来模拟静态库的“应用程序内全局变量的多个实例”行为?

    我们有一个用 C C 编写的应用程序 它被分解为单个 EXE 和多个 DLL 这些 DLL 中的每一个都使用相同的静态库 utilities lib 实用程序静态库中的任何全局变量实际上在应用程序内运行时都会有多个实例 每个模块 即 DLL
  • 如何在Java程序中调用DLL中的方法

    我正在尝试使用 JNA 调用 DLL 中的方法 到目前为止已经使用加载了DLL Runtime getRuntime load myworkspace test dll 该 dll 包含我需要访问的方法 如何在我的 Java 文件中执行 D
  • 如何在我的项目中使用 FFTW DLL 导入库 .lib 作为静态 .lib?

    我了解 C 和编译小型插件 始终基于类似且布局的工作流程 虽然目前我需要将最新版本的 FFTW 编译成静态库 或者从编译版本中获取它 这应该会容易得多 但这给了我一个非常困难的时间 可能是因为我不知道编译的来龙去脉 请注意 我正在使用 Vi
  • dependency walker 在正确运行的系统上给我错误

    我的系统上缺少 dll 我有以下消息 无法加载文件或程序集 mydll dll 或其依赖项之一 当然 mydll 存在于此 它很可能是它所依赖的另一个 dll 我下载了 dependency walker 来检查它可能是什么 我首先在另一个
  • 如何从 dll 导出 C++ 类? [复制]

    这个问题在这里已经有答案了 我有一个有两个重载函数的类 如何从 dll 导出它以及如何由其他 C 类使用它 我的班级是这样的 define DECLDIREXP declspec dllexport define DECLDIRIMP de
  • java.library.path 中没有 ocijdbc12

    我正在尝试使用 OCI 驱动程序通过 java 程序连接到 oracle 以下是配置 Windows 7 32 位 JDK 1 7 Oracle 客户端 11g R2 ojdbc7 jar在我的独立应用程序的类路径中 但我收到以下异常 Ex
  • 在java程序中使用c++ Dll

    我正在尝试使用System LoadLibrary 使用我用 C 编写的一个简单的 dll UseDllInJava java import com sun jna Library import com sun jna Native imp
  • Labview 2011 中的 DLL 加载错误

    我在 LabVIEW 中加载 DLL 时遇到了问题 我正在使用 LabVIEW 连接到流量计 为此 制造流量计的公司非常好地为我提供了一个子 VI 库 这些子 VI 中的每一个都调用该公司提供的两个 DLL 库之一中的函数 因此 这些子 V

随机推荐

  • 详细介绍如何在linux中配置chisel环境

    一 安装java和scala 1 安装java 安装java sudo apt install openjdk 11 jre headless 安装javac sudo apt install openjdk 11 jdk headless
  • ubuntu 18.04 RTX2080(ti) --- tensorflow-gpu + cuda9.0 + cudnn-9.0 (ubuntu 16.04, TITAN XP)

    0 下载display driver cuda和cudnn RTX2080 Display Driver cuda cudnn 版本对应关系 1 禁止系统默认的显卡驱动 打开系统黑名单 sudo gedit etc modprobe d b
  • C++11新特性,推荐使用emplace_back()替换push_back()的原因

    TOC c 11新加入了emplace back 用来替换push back 在平时我们习惯性的尾插用push back 去完成 但是如果是尾插临时对象的话 push back 需要先构造临时对象 再将这个对象拷贝到容器的末尾 而empla
  • mmsegmentation安装教程,简单易懂,必能成功

    一 安装anaconda 这个自己弄一路默认就行或者你想改个路径也行 无所谓 安装好之后打开anaconda prompt控制面板 换成清华源 pip config set global index url https pypi tuna
  • 数字IC后端设计技术全局观

    数字IC后端设计flow 不含DFT 数字IC后端设计工具 DC 用于逻辑综合 FM 用于形式验证 ICC 用于物理实现 PrimeTime 用于STA 步骤 或文件类型 简述 RTL Register Transfer Level v文件
  • mysql8.0收费价格,MySQl 8.0遇到的坑

    报错 Illuminate Database QueryException SQLSTATE HY000 1045 Access denied for user root localhost using password NO SQL cr
  • Trying to access array offset on value of type int

    问题描述 出现报错信息 先百度翻译 试图访问int类型值的数组偏移量 通过翻译得知 int型的数据被其他不能使用的类型使用了 个人理解 关于这块 php7 4升级之后会有这个bug 网上大多人是说 7 4 版本的向后不兼容更改 非数组的数组
  • valgrind Massif

    valgrind检查内存泄露 valgrind 程序 内存泄漏问题 我们有memcheck工具来检查 很爽 但是有时候memcheck工具查了没泄漏 程序一跑 内存还是狂飙 这又是什么问题 其实memcheck检查的内存泄漏只是狭义的内存泄
  • Docker——安装和启动

    一 环境准备 1 安装Linux虚拟机软件 VMware或VirtualBox 比VMware更小巧轻便且免费 此处安装VirtulaBox 2 安装Linux虚拟系统 在管理中选择导入虚拟电脑 记得选中重新初始化所有网卡的MAC地址 双击
  • Dynamics CRM 365 如何设置经典登录页面

    Don t be surprised If you don t see classic interface post your sign up for dynamics 365 Okay let s face it We are losin
  • 复选框check的选中、不选中设置以及判断是否选中

    复选框的设置 一 JavaScript判断是否选中checkbox框 二 JavaScript设置选中checkbox框 三 JavaScript移除选中checkbox框 四 使用jQuery判断是否选中checkbox框 五 使用jQu
  • 国密(1) - 私钥Key文件( PEM格式)编解码方法

    详细的PEM文件格式解析 PEM文件 是按照私钥的ASN 1的格式 RFC5208 5915 5480 进行DER编码后输出二进制串的基础上 再进行Base64的编码 也就是每6个bit为一组 生成一个ascii码字符 需要4组6个bit
  • 学习笔记59—收藏这7个在线配色神器,再也不愁配色灵感了

    在设计中配色方案是必要的 也是让设计师头疼的一个问题 所以 编辑专为大家整理了一波配色神器网站 不用下载任何应用程序 打开即用 不仅能快速的做出符合设计概念的颜色组合 且有很多样品供你确认的工具 设计新手们千万别错过了 一 Khroma h
  • 【macOS】Win通过VNC远程控制Macbook

    Win通过VNC远程控制Macbook 参考 https zhuanlan zhihu com p 74162964 仅局域网内可用 Macbook配置 进入 电脑设置 勾选两个选项 Windows配置 安装VNC Viewer https
  • openpyxl操作表格的基本用法

    创建文件 以及创建xlsx表格 from openpyxl import Workbook load workbook import os 创建excel文件 默认会有一个sheet命名的表 def create xlsx path nam
  • Beginng_Rust(译):借用和生命周期(第二十二章)

    在本章中 您将学习 借用 和 生命周期 的概念 哪些是关于借用的典型编程错误 即困扰系统软件 Rust严格语法如何使用借用检查器来防止此类典型错误 插入块的方式如何限制借用范围 为什么函数返回引用需要生命周期指示符 如何使用寿命指定符来表示
  • 应用层的原理

    目录 应用层协议原理 网络应用程序体系结构 客户 服务器 P2P 混合模式 UDP TCP 所有能产生网络流量的程序 应用层协议原理 网络应用程序体系结构 客户 服务器 P2P 混合模式 UDP TCP 可供应用程序使用的运输服务 因特网提
  • 解决liquibase.exception.LockException: Could not acquire change log lock. Currently locked by XXXX

    项目启动后报liquibase exception LockException Could not acquire change log lock 解决方案 执行下面语句 use job job为你的数据库 select from DATA
  • HTML5 history新特性pushState、replaceState

    DOM中的window对象通过window history方法提供了对浏览器历史记录的读取 让你可以在用户的访问记录中前进和后退 从HTML5开始 我们可以开始操作这个历史记录堆栈 1 History 使用back forward 和go
  • windows dll 装载过程

    windows dll 装载过程 2010 12 04 19 13 56 分类 Windows系统平台上 你可以将独立的程序模块创建为较小的DLL Dynamic Linkable Library 文件 并可对它们单独编译和测试 在运行时