欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Visual Studio编译原理科普

程序员文章站 2022-06-17 14:40:39
...

前言: 刚从vscode、devc、vim或者其他地方来的小伙伴,对vs这种模式往往不是很清楚,特别是什么是release和debug模式,什么时候生成lib文件,什么时候生成dll文件,什么是解决方案,什么时候用sln解决方案,什么时候用suo解决方案,解决方案的生成步骤等等这些,这里我做一个总结,供参考。

1. release模式和debug模式

Debug通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发 人员提供强大的应用程序调试能力。而Release通常称为发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信 息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。为用户的使用提供便利。
debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc, 而release的赋值近似于随机。如果你的程序中的某个变量没被初始化就被引用,就很有可能出现异常:用作控制变量将导致流程导向不一致;用作数组下标将会使程序崩溃;更加可能是造成其他变量的不准确而引起其他的错误。所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到。debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了。
只有DEBUG版的程序才能设置断点、单步执行、使用 TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息,所以体积小、运行速度快。

2. 动态库和静态库、lib文件和dll文件

动态库:动态库有两个文件,一个lib文件和一个dll文件。lib文件中包含了方法名和方法所在的dll等索引信息,并不包含方法的具体实现,方法的具体实现在dll文件中。在编译阶段我们只需要lib文件,只要相应的lib文件引用没有问题,编译就不会报错。而在运行的时候就需要用到dll文件,如果这个时候在lib文件中方法指定的dll位置找不到该dll文件,就会报错找不到XXX.dll文件。动态文件的好处是,当我们生成exe文件的时候只需要在exe文件包含lib文件就可以,有与lib文件不包含方法的具体实现,**所有导出的exe文件一般比较小,但是同时就需要我们运行exe的电脑上面必须有exe文件中包含的lib文件所需要的dll文件。**同样我们有多个程序需要用到同一个库的时候,在电脑上只需要有一份dll文件就行了,在需要的时候只要在lib中指定的位置去加载这个dll就可以了。还有就是我们要更新这个dll文件的时候,不需要exe的重新打包。

**静态库:**静态库就一个lib文件,**这个文件中既包含了包的方法名也包含了他所有的实现,**所以在引用的时候只要一个lib文件就可以了。但是由于这个lib文件中包含了所有的库内容,所以体积较大,导致生成的exe文件也就比较大。但是我们导出的exe文件放在别的电脑上面就可以直接运行,因为这个exe中已经包含了所有引用库的全部内容。但是如果我们要更新这个静态库中某些方法的时候,就需要对这个exe文件整体重新打包以包含我们更新的内容。

3. 解决方案

解决方案是将一个或多个项目组合在一起,以创建一个应用程序。与解决方案有关的项目和状态信息存储在两个不同的解决方案文件中。该解决方案(.sln)文件是可以和源代码控制下被置于与用户之间共享的基于文本的。该解决方案用户选项(名为.suo)文件是二进制的。因此,.suo文件无法置于源代码控制之下,并且包含用户特定的信息。

打开解决方案后的执行过程:

  1. 环境读取解决方案。
  2. 如果环境找到一个CLSID,它将加载相应的VSPackage。
  3. 如果加载了VSPackage,则环境QueryInterface将为VSPackage所需的接口调用IVsPackage接口。
    • .sln文件读取时,环境要求QueryInterfaceIVsPersistSolutionProps
    • 当从文件名为.suo,环境调用阅读QueryInterfaceIVsPersistSolutionOpts

4. 解决方案sln文件

解决方案是用于在Visual Studio中组织项目的结构。该解决方案在两个文件中维护项目的状态信息:

  • .sln文件(基于文本的共享文件)
  • .suo文件(二进制,特定于用户的解决方案选项)

.sln文件包含环境用于查找和加载持久数据及其引用的项目VSPackages的名称-值参数的基于文本的信息。当用户打开的解决方案,环境循环通过preSolutionProjectpostSolution.sln文件信息来加载解决方案中,该解决方案中,和任何持久性信息内的项目附加到该解决方案中。
.sln的文件头

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28701.123
MinimumVisualStudioVersion = 10.0.40219.1

每个参数的含义:

  • Microsoft Visual Studio Solution File, Format Version 12.00
    定义文件格式版本的标准标头。

  • # Visual Studio Version 16
    (最近)保存了此解决方案文件的Visual Studio的主要版本。此信息控制解决方案图标中的版本号。

  • VisualStudioVersion = 16.0.28701.123
    (最近)保存解决方案文件的完整版本的Visual Studio。如果解决方案文件由具有相同主版本的Visual Studio的较新版本保存,则不会更新此值,以减少文件中的用户流失。

  • MinimumVisualStudioVersion = 10.0.40219.1
    可以打开此解决方案文件的最低(最早)Visual Studio版本。

.sln文件主体

.sln文件的主体由几个带有标签的部分组成GlobalSection,比如:

Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Project1", "Project1.vbproj", "{8CDD8387-B905-44A8-B5D5-07BB50E05BEA}"
EndProject
Global
  GlobalSection(SolutionNotes) = postSolution
  EndGlobalSection
  GlobalSection(SolutionConfiguration) = preSolution
       ConfigName.0 = Debug
       ConfigName.1 = Release
  EndGlobalSection
  GlobalSection(ProjectDependencies) = postSolution
  EndGlobalSection
  GlobalSection(ProjectConfiguration) = postSolution
   {8CDD8387-B905-44A8-B5D5-07BB50E05BEA}.Debug.ActiveCfg = Debug|x86
   {8CDD8387-B905-44A8-B5D5-07BB50E05BEA}.Debug.Build.0 = Debug|x86
   {8CDD8387-B905-44A8-B5D5-07BB50E05BEA}.Release.ActiveCfg = Release|x86
   {8CDD8387-B905-44A8-B5D5-07BB50E05BEA}.Release.Build.0 = Release|x86
  EndGlobalSection
  GlobalSection(ExtensibilityGlobals) = postSolution
  EndGlobalSection
  GlobalSection(ExtensibilityAddIns) = postSolution
  EndGlobalSection
EndGlobal

在加载解决方案的时候,依次执行以下任务:

  1. 该环境读取.sln文件的Global部分,并处理所有标记为的部分preSolution。在此示例文件中,有一个这样的语句:

    GlobalSection(SolutionConfiguration) = preSolution
         ConfigName.0 = Debug
         ConfigName.1 = Release
    

    当环境读取GlobalSection('name')标签时,它将使用注册表将名称映射到VSPackage。注册表项应该存在于[HKLM \ <应用程序ID注册表根目录> \ SolutionPersistence \ AggregateGUIDs]下的注册表项中。**的默认值是写入条目的VSPackagePackage GUID(REG_SZ)

  2. 该环境将加载VSPackage,QueryInterfaceIVsPersistSolutionProps接口调用VSPackage ,然后使用该部分中的数据调用ReadSolutionProps方法,以便VSPackage可以存储数据。环境为每个preSolution部分重复此过程。

  3. 环境遍历项目持久性块。在这种情况下,只有一个项目。

    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Project1",
    "Project1.vbproj", "{8CDD8387-B905-44A8-B5D5-07BB50E05BEA}"
    EndProject
    

    该语句包含唯一的项目GUID和项目类型GUID。环境使用此信息来查找项目文件或属于解决方案的文件,以及每个项目所需的VSPackage。将项目GUID传递给[IVsProjectFactory](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.interop.ivsprojectfactory)以加载与项目相关的特定VSPackage,然后由VSPackage加载项目。在这种情况下,为此项目加载的VSPackage是Visual Basic。

    每个项目都可以保留唯一的项目实例ID,以便解决方案中的其他项目可以根据需要对其进行访问。理想情况下,如果解决方案和项目处于源代码控制之下,则项目的路径应相对于解决方案的路径。首次加载解决方案时,项目文件不能位于用户的计算机上。通过将项目文件相对于解决方案文件存储在服务器上,找到项目文件并将其复制到用户机器相对简单。然后,它将复制并加载项目所需的其余文件。

  4. 根据.sln文件的项目部分中包含的信息,环境将加载每个项目文件。然后,项目本身负责填充项目层次结构并加载任何嵌套项目。

  5. 处理完.sln文件的所有部分之后,解决方案将显示在解决方案资源管理器中,并可供用户修改。

如果在解决方案中实现项目的任何VSPackage无法加载,则将调用OnProjectLoadFailure方法,并为解决方案中的所有其他项目提供忽略其在加载过程中所做的更改的机会。如果发生解析错误,解决方案文件将保留尽可能多的信息,并且环境会显示一个对话框,警告用户解决方案已损坏。

保存或关闭解决方案时,将调用QuerySaveSolutionProps方法并将其传递给层次结构,以查看是否已对解决方案进行了更改,需要将其输入.sln文件。QuerySaveSolutionPropsVSQUERYSAVESLNPROPS中传入的空值表示该解决方案的信息正在保留。如果该值不为null,则持久化的信息用于特定项目,由指向IVsHierarchy接口的指针确定。

如果有要保存的信息,则使用指向SaveSolutionProps方法的指针来调用IVsSolutionPersistence接口。该WriteSolutionProps方法,然后由环境调用从获取名称-值对接口将信息写入.sln文件。IPropertyBag

SaveSolutionProps WriteSolutionProps环境会递归地调用和对象,以从IPropertyBag接口检索要保存的信息,直到将所有更改都输入到.sln文件中为止。这样,您可以确保信息将与解决方案一起保留,并在下次打开解决方案时可用。

枚举每个加载的VSPackage,以查看是否有任何要保存到.sln文件的内容。仅在加载时才查询注册表项。该环境知道所有已加载的程序包,因为它们在保存解决方案时已在内存中。

只有.sln文件在preSolutionpostSolution部分中包含条目。.suo文件中没有相似的部分,因为解决方案需要此信息才能正确加载。.suo文件包含用户特定的选项,例如私人注释,这些选项不打算共享或置于源代码控制之下。

5. 解决方案用户选项.suo文件

解决方案用户选项(.suo)文件包含每个用户的解决方案选项,此文件不应签入源代码控制。

解决方案用户选项(.suo)文件是以二进制格式存储的结构化存储或复合文件。您将用户信息保存到流中,流的名称是将用于标识.suo文件中信息的键。解决方案用户选项文件用于存储用户首选项设置,并在Visual Studio保存解决方案时自动创建。

当环境打开.suo文件时,它将枚举当前所有已加载的VSPackages。如果VSPackage实现了IVsPersistSolutionOpts接口,则环境将调用VSPackage上的LoadUserOptions方法,要求其从.suo文件中加载所有数据。

VSPackage的责任是知道它可能已写入.suo文件的流。对于它编写的每个流,VSPackage通过LoadPackageUserOpts回调到环境,以加载由键标识的特定流,该键是流的名称。然后,环境调用VSPackage以读取特定的流,同时传递流的名称和IStream指向LoadPackageUserOpts方法的指针。

此时,将进行另一个调用LoadUserOptions以查看.suo文件是否还有其他部分需要读取。该过程将一直持续到环境已读取并处理.suo文件中的所有数据流为止。

当解决方案被保存或关闭时,环境调用SavePackageSolutionProps方法用的指针SaveUserOptions方法。一个IStream包含二进制信息被保存传递给WriteUserOptions方法,然后将信息写入到文件名为.suo并调用SaveUserOptions再次方法,看看是否有另一个信息流写入到文件名为.suo

对于要保存到.suo文件的每个信息流,都递归调用这两种方法SaveUserOptions WriteUserOptions,并将其指针传递给IVsSolutionPersistence。递归调用它们以允许将多个流写入.suo文件。这样,用户信息将与解决方案一起保留,并确保下次打开解决方案时该用户信息在那里。

参考

  • https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solutions-overview?view=vs-2019
  • https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solution-user-options-dot-suo-file?view=vs-2019